From d799921c75e7bfb83504bb79dcc1c269c91d168c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 18 Jan 2018 19:17:25 +0100 Subject: Replace tutorial modal with welcome e-mail (#6273) * Remove onboarding modal * Welcome e-mail * Send welcome e-mail after confirmation * Remove obsolete translations --- app/javascript/images/icon_done.svg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/javascript/images/icon_done.svg (limited to 'app/javascript/images') diff --git a/app/javascript/images/icon_done.svg b/app/javascript/images/icon_done.svg new file mode 100644 index 000000000..446af14d9 --- /dev/null +++ b/app/javascript/images/icon_done.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file -- cgit From 540b3f37ae904c135809ef2a65f2700ca361de77 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 19 Jan 2018 15:25:27 +0100 Subject: Replace drawer elephant graphic with a vector image (#6286) * Replace drawer elephant graphic with a vector image * Replace wave graphic with SVG * Remove unused elephant graphic --- app/javascript/images/elephant-friend-1.png | Bin 144967 -> 0 bytes app/javascript/images/elephant_ui_plane.svg | 1 + app/javascript/images/mastodon-ui.png | Bin 32449 -> 0 bytes app/javascript/images/wave-compose-standalone.png | Bin 5921 -> 0 bytes app/javascript/images/wave-drawer.png | Bin 3269 -> 0 bytes app/javascript/images/wave-modal.png | Bin 5200 -> 0 bytes app/javascript/styles/application.scss | 3 ++- app/javascript/styles/mastodon/components.scss | 7 ++----- app/javascript/styles/mastodon/modal.scss | 4 ++-- 9 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 app/javascript/images/elephant-friend-1.png create mode 100644 app/javascript/images/elephant_ui_plane.svg delete mode 100644 app/javascript/images/mastodon-ui.png delete mode 100644 app/javascript/images/wave-compose-standalone.png delete mode 100644 app/javascript/images/wave-drawer.png delete mode 100644 app/javascript/images/wave-modal.png (limited to 'app/javascript/images') diff --git a/app/javascript/images/elephant-friend-1.png b/app/javascript/images/elephant-friend-1.png deleted file mode 100644 index 2b2383330..000000000 Binary files a/app/javascript/images/elephant-friend-1.png and /dev/null differ diff --git a/app/javascript/images/elephant_ui_plane.svg b/app/javascript/images/elephant_ui_plane.svg new file mode 100644 index 000000000..a2624d170 --- /dev/null +++ b/app/javascript/images/elephant_ui_plane.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/images/mastodon-ui.png b/app/javascript/images/mastodon-ui.png deleted file mode 100644 index a1fb642a0..000000000 Binary files a/app/javascript/images/mastodon-ui.png and /dev/null differ diff --git a/app/javascript/images/wave-compose-standalone.png b/app/javascript/images/wave-compose-standalone.png deleted file mode 100644 index 287ee639b..000000000 Binary files a/app/javascript/images/wave-compose-standalone.png and /dev/null differ diff --git a/app/javascript/images/wave-drawer.png b/app/javascript/images/wave-drawer.png deleted file mode 100644 index ca9f9e1d8..000000000 Binary files a/app/javascript/images/wave-drawer.png and /dev/null differ diff --git a/app/javascript/images/wave-modal.png b/app/javascript/images/wave-modal.png deleted file mode 100644 index 88818a6d7..000000000 Binary files a/app/javascript/images/wave-modal.png and /dev/null differ diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss index fd6665f65..300040173 100644 --- a/app/javascript/styles/application.scss +++ b/app/javascript/styles/application.scss @@ -6,7 +6,6 @@ @import 'mastodon/reset'; @import 'mastodon/basics'; -@import 'mastodon/modal'; @import 'mastodon/containers'; @import 'mastodon/lists'; @import 'mastodon/footer'; @@ -15,7 +14,9 @@ @import 'mastodon/forms'; @import 'mastodon/accounts'; @import 'mastodon/stream_entries'; +@import 'mastodon/boost'; @import 'mastodon/components'; +@import 'mastodon/modal'; @import 'mastodon/emoji_picker'; @import 'mastodon/about'; @import 'mastodon/tables'; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 63ee06d8e..c0a32ed05 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1762,7 +1762,7 @@ position: absolute; top: 0; left: 0; - background: lighten($ui-base-color, 13%) url('../images/wave-drawer.png') no-repeat bottom / 100% auto; + background: lighten($ui-base-color, 13%) url('data:image/svg+xml;utf8,') no-repeat bottom / 100% auto; box-sizing: border-box; padding: 0; display: flex; @@ -1777,7 +1777,7 @@ } > .mastodon { - background: url('../images/mastodon-ui.png') no-repeat left bottom / contain; + background: url('../images/elephant_ui_plane.svg') no-repeat left bottom / contain; flex: 1; } } @@ -2154,10 +2154,7 @@ } } -@import 'boost'; - .no-reduce-motion button.icon-button i.fa-retweet { - background-position: 0 0; height: 19px; transition: background-position 0.9s steps(10); diff --git a/app/javascript/styles/mastodon/modal.scss b/app/javascript/styles/mastodon/modal.scss index 310dcb924..f9fad732e 100644 --- a/app/javascript/styles/mastodon/modal.scss +++ b/app/javascript/styles/mastodon/modal.scss @@ -1,5 +1,5 @@ .modal-layout { - background: $ui-base-color url('../images/wave-modal.png') repeat-x bottom fixed; + background: $ui-base-color url('data:image/svg+xml;utf8,') repeat-x bottom fixed; display: flex; flex-direction: column; height: 100vh; @@ -15,6 +15,6 @@ > * { flex: 1; max-height: 235px; - background: url('../images/mastodon-ui.png') no-repeat left bottom / contain; + background: url('../images/elephant_ui_plane.svg') no-repeat left bottom / contain; } } -- cgit From 872a0d5bd801c998d911f7da582a60d2f714a710 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 20 Jan 2018 01:32:21 +0100 Subject: Improve HTML e-mails based on Litmus tests (#6301) * Use PNG images in HTML e-mails * Make webpack use URLs with host so fonts load inside HTML e-mails Convert this back to a relative URL in the premailer CSS loader since local requests are quicker * Improve responsive design * Add missing PNG icon --- app/javascript/images/mailer/icon_cached.png | Bin 0 -> 754 bytes app/javascript/images/mailer/icon_done.png | Bin 0 -> 279 bytes app/javascript/images/mailer/icon_email.png | Bin 0 -> 520 bytes app/javascript/images/mailer/icon_grade.png | Bin 0 -> 541 bytes app/javascript/images/mailer/icon_lock_open.png | Bin 0 -> 550 bytes app/javascript/images/mailer/icon_person_add.png | Bin 0 -> 512 bytes app/javascript/images/mailer/icon_reply.png | Bin 0 -> 391 bytes app/javascript/images/mailer/logo_full.png | Bin 0 -> 3052 bytes app/javascript/images/mailer/logo_transparent.png | Bin 0 -> 627 bytes app/javascript/styles/mailer.scss | 34 ++++++++++++++++++++- app/views/layouts/mailer.html.haml | 9 ++---- app/views/notification_mailer/favourite.html.haml | 2 +- app/views/notification_mailer/follow.html.haml | 2 +- .../notification_mailer/follow_request.html.haml | 2 +- app/views/notification_mailer/mention.html.haml | 2 +- app/views/notification_mailer/reblog.html.haml | 2 +- .../confirmation_instructions.html.haml | 2 +- app/views/user_mailer/email_changed.html.haml | 2 +- app/views/user_mailer/password_change.html.haml | 2 +- .../reconfirmation_instructions.html.haml | 2 +- .../reset_password_instructions.html.haml | 2 +- app/views/user_mailer/welcome.html.haml | 12 ++++---- config/webpack/configuration.js | 2 +- lib/mastodon/premailer_webpack_strategy.rb | 3 ++ 24 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 app/javascript/images/mailer/icon_cached.png create mode 100644 app/javascript/images/mailer/icon_done.png create mode 100644 app/javascript/images/mailer/icon_email.png create mode 100644 app/javascript/images/mailer/icon_grade.png create mode 100644 app/javascript/images/mailer/icon_lock_open.png create mode 100644 app/javascript/images/mailer/icon_person_add.png create mode 100644 app/javascript/images/mailer/icon_reply.png create mode 100644 app/javascript/images/mailer/logo_full.png create mode 100644 app/javascript/images/mailer/logo_transparent.png (limited to 'app/javascript/images') diff --git a/app/javascript/images/mailer/icon_cached.png b/app/javascript/images/mailer/icon_cached.png new file mode 100644 index 000000000..e62c064c8 Binary files /dev/null and b/app/javascript/images/mailer/icon_cached.png differ diff --git a/app/javascript/images/mailer/icon_done.png b/app/javascript/images/mailer/icon_done.png new file mode 100644 index 000000000..0d2ef0cb8 Binary files /dev/null and b/app/javascript/images/mailer/icon_done.png differ diff --git a/app/javascript/images/mailer/icon_email.png b/app/javascript/images/mailer/icon_email.png new file mode 100644 index 000000000..aae9d2bb9 Binary files /dev/null and b/app/javascript/images/mailer/icon_email.png differ diff --git a/app/javascript/images/mailer/icon_grade.png b/app/javascript/images/mailer/icon_grade.png new file mode 100644 index 000000000..895b57d0a Binary files /dev/null and b/app/javascript/images/mailer/icon_grade.png differ diff --git a/app/javascript/images/mailer/icon_lock_open.png b/app/javascript/images/mailer/icon_lock_open.png new file mode 100644 index 000000000..9f62eadc2 Binary files /dev/null and b/app/javascript/images/mailer/icon_lock_open.png differ diff --git a/app/javascript/images/mailer/icon_person_add.png b/app/javascript/images/mailer/icon_person_add.png new file mode 100644 index 000000000..3453060ae Binary files /dev/null and b/app/javascript/images/mailer/icon_person_add.png differ diff --git a/app/javascript/images/mailer/icon_reply.png b/app/javascript/images/mailer/icon_reply.png new file mode 100644 index 000000000..8bce4955c Binary files /dev/null and b/app/javascript/images/mailer/icon_reply.png differ diff --git a/app/javascript/images/mailer/logo_full.png b/app/javascript/images/mailer/logo_full.png new file mode 100644 index 000000000..1c4f33287 Binary files /dev/null and b/app/javascript/images/mailer/logo_full.png differ diff --git a/app/javascript/images/mailer/logo_transparent.png b/app/javascript/images/mailer/logo_transparent.png new file mode 100644 index 000000000..8fda4f67f Binary files /dev/null and b/app/javascript/images/mailer/logo_transparent.png differ diff --git a/app/javascript/styles/mailer.scss b/app/javascript/styles/mailer.scss index b3bcc9209..dbe070f9a 100644 --- a/app/javascript/styles/mailer.scss +++ b/app/javascript/styles/mailer.scss @@ -17,7 +17,7 @@ body { -ms-text-size-adjust: 100%; } -.email_body { +.email-body { td, div, a, @@ -235,6 +235,12 @@ h5 { color: lighten($ui-base-color, 34%); } +.input-cell { + h5 { + margin-top: 4px; + } +} + .input { td { background: darken($ui-base-color, 8%); @@ -512,3 +518,29 @@ ul { min-height: 1024px !important; } } + +@media (max-width: 697px) { + .email-container, + .col-1, + .col-2, + .col-3, + .col-4, + .col-5, + .col-6 { + width: 100% !important; + max-width: none !important; + } + + .email-start { + padding-top: 16px !important; + } + + .email-end { + padding-bottom: 16px !important; + } + + .padded { + padding-left: 0 !important; + padding-right: 0 !important; + } +} diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml index e41b91069..6687546fb 100644 --- a/app/views/layouts/mailer.html.haml +++ b/app/views/layouts/mailer.html.haml @@ -24,7 +24,7 @@ %tr %td.column-cell = link_to root_url do - = image_tag full_pack_url('logo_full.svg'), alt: 'Mastodon', height: 34, class: 'logo' + = image_tag full_pack_url('logo_full.png'), alt: 'Mastodon', height: 34, class: 'logo' = yield @@ -41,15 +41,12 @@ %tr %td.blank-cell.footer .email-row - .col-4 + .col-6 %table.column{ cellspacing: 0, cellpadding: 0 } %tbody %td.column-cell %p= t 'about.hosted_on', domain: site_hostname %p= link_to t('application_mailer.notification_preferences'), settings_notifications_url - .col-2 - %table.column{ cellspacing: 0, cellpadding: 0 } - %tbody %td.column-cell.text-right = link_to root_url do - = image_tag full_pack_url('logo_transparent.svg'), alt: 'Mastodon', height: 24 + = image_tag full_pack_url('logo_transparent.png'), alt: 'Mastodon', height: 24 diff --git a/app/views/notification_mailer/favourite.html.haml b/app/views/notification_mailer/favourite.html.haml index f26b08b18..7d1b494d0 100644 --- a/app/views/notification_mailer/favourite.html.haml +++ b/app/views/notification_mailer/favourite.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_grade.svg'), alt:'' + = image_tag full_pack_url('icon_grade.png'), alt:'' %h1= t 'notification_mailer.favourite.title' %p.lead= t('notification_mailer.favourite.body', name: @account.acct) diff --git a/app/views/notification_mailer/follow.html.haml b/app/views/notification_mailer/follow.html.haml index 1290e2bc4..31a2b7445 100644 --- a/app/views/notification_mailer/follow.html.haml +++ b/app/views/notification_mailer/follow.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_person_add.svg'), alt: '' + = image_tag full_pack_url('icon_person_add.png'), alt: '' %h1= t 'notification_mailer.follow.title' %p.lead= t('notification_mailer.follow.body', name: @account.acct) diff --git a/app/views/notification_mailer/follow_request.html.haml b/app/views/notification_mailer/follow_request.html.haml index 41efeafaf..44f1911c4 100644 --- a/app/views/notification_mailer/follow_request.html.haml +++ b/app/views/notification_mailer/follow_request.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_person_add.svg'), alt: '' + = image_tag full_pack_url('icon_person_add.png'), alt: '' %h1= t 'notification_mailer.follow_request.title' %p.lead= t('notification_mailer.follow_request.body', name: @account.acct) diff --git a/app/views/notification_mailer/mention.html.haml b/app/views/notification_mailer/mention.html.haml index 619c580ce..479fed41c 100644 --- a/app/views/notification_mailer/mention.html.haml +++ b/app/views/notification_mailer/mention.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_reply.svg'), alt: '' + = image_tag full_pack_url('icon_reply.png'), alt: '' %h1= t 'notification_mailer.mention.title' %p.lead= t('notification_mailer.mention.body', name: @status.account.acct) diff --git a/app/views/notification_mailer/reblog.html.haml b/app/views/notification_mailer/reblog.html.haml index 61c6ee6be..85b202cf9 100644 --- a/app/views/notification_mailer/reblog.html.haml +++ b/app/views/notification_mailer/reblog.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_cached.svg'), alt: '' + = image_tag full_pack_url('icon_cached.png'), alt: '' %h1= t 'notification_mailer.reblog.title' %p.lead= t('notification_mailer.reblog.body', name: @account.acct) diff --git a/app/views/user_mailer/confirmation_instructions.html.haml b/app/views/user_mailer/confirmation_instructions.html.haml index 0f999bcbc..1f088a16f 100644 --- a/app/views/user_mailer/confirmation_instructions.html.haml +++ b/app/views/user_mailer/confirmation_instructions.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_email.svg'), alt: '' + = image_tag full_pack_url('icon_email.png'), alt: '' %h1= t 'devise.mailer.confirmation_instructions.title' diff --git a/app/views/user_mailer/email_changed.html.haml b/app/views/user_mailer/email_changed.html.haml index 45dc06650..7e82f23e4 100644 --- a/app/views/user_mailer/email_changed.html.haml +++ b/app/views/user_mailer/email_changed.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_email.svg'), alt: '' + = image_tag full_pack_url('icon_email.png'), alt: '' %h1= t 'devise.mailer.email_changed.title' %p.lead= t 'devise.mailer.email_changed.explanation' diff --git a/app/views/user_mailer/password_change.html.haml b/app/views/user_mailer/password_change.html.haml index 2e9377dff..26314a217 100644 --- a/app/views/user_mailer/password_change.html.haml +++ b/app/views/user_mailer/password_change.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_lock_open.svg'), alt: '' + = image_tag full_pack_url('icon_lock_open.png'), alt: '' %h1= t 'devise.mailer.password_change.title' %p.lead= t 'devise.mailer.password_change.explanation' diff --git a/app/views/user_mailer/reconfirmation_instructions.html.haml b/app/views/user_mailer/reconfirmation_instructions.html.haml index 3ae226093..e3be8e295 100644 --- a/app/views/user_mailer/reconfirmation_instructions.html.haml +++ b/app/views/user_mailer/reconfirmation_instructions.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_email.svg'), alt: '' + = image_tag full_pack_url('icon_email.png'), alt: '' %h1= t 'devise.mailer.reconfirmation_instructions.title' %p.lead= t 'devise.mailer.reconfirmation_instructions.explanation' diff --git a/app/views/user_mailer/reset_password_instructions.html.haml b/app/views/user_mailer/reset_password_instructions.html.haml index c0e6775d4..5d9ce6a75 100644 --- a/app/views/user_mailer/reset_password_instructions.html.haml +++ b/app/views/user_mailer/reset_password_instructions.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_lock_open.svg'), alt: '' + = image_tag full_pack_url('icon_lock_open.png'), alt: '' %h1= t 'devise.mailer.reset_password_instructions.title' %p.lead= t 'devise.mailer.reset_password_instructions.explanation' diff --git a/app/views/user_mailer/welcome.html.haml b/app/views/user_mailer/welcome.html.haml index 0823efa1a..d734da67d 100644 --- a/app/views/user_mailer/welcome.html.haml +++ b/app/views/user_mailer/welcome.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('icon_done.svg'), alt: '' + = image_tag full_pack_url('icon_done.png'), alt: '' %h1= t 'user_mailer.welcome.title', name: @resource.account.username %p.lead= t 'user_mailer.welcome.explanation' @@ -30,7 +30,7 @@ %table.content-section{ cellspacing: 0, cellpadding: 0 } %tbody %tr - %td.content-cell + %td.content-cell.content-start .email-row .col-3 %table.column{ cellspacing: 0, cellpadding: 0 } @@ -46,7 +46,7 @@ %table.column{ cellspacing: 0, cellpadding: 0 } %tbody %tr - %td.column-cell.content-start + %td.column-cell %p= t 'user_mailer.welcome.full_handle_hint', instance: @instance %table.email-table{ cellspacing: 0, cellpadding: 0 } @@ -69,7 +69,7 @@ %table.column{ cellspacing: 0, cellpadding: 0 } %tbody %tr - %td.column-cell + %td.column-cell.padded %table.button.button-small{ align: 'left', cellspacing: 0, cellpadding: 0 } %tbody %tr @@ -89,7 +89,7 @@ %table.column{ cellspacing: 0, cellpadding: 0 } %tbody %tr - %td.column-cell + %td.column-cell.padded %table.button.button-small{ align: 'left', cellspacing: 0, cellpadding: 0 } %tbody %tr @@ -109,7 +109,7 @@ %table.column{ cellspacing: 0, cellpadding: 0 } %tbody %tr - %td.column-cell + %td.column-cell.padded %table.button.button-small{ align: 'left', cellspacing: 0, cellpadding: 0 } %tbody %tr diff --git a/config/webpack/configuration.js b/config/webpack/configuration.js index 822329490..26747e76b 100644 --- a/config/webpack/configuration.js +++ b/config/webpack/configuration.js @@ -27,7 +27,7 @@ function formatPublicPath(host = '', path = '') { const output = { path: resolve('public', settings.public_output_path), - publicPath: formatPublicPath(env.ASSET_HOST, settings.public_output_path), + publicPath: formatPublicPath(env.ASSET_HOST || env.LOCAL_DOMAIN, settings.public_output_path), }; module.exports = { diff --git a/lib/mastodon/premailer_webpack_strategy.rb b/lib/mastodon/premailer_webpack_strategy.rb index 4ea3ce80c..84d83cc66 100644 --- a/lib/mastodon/premailer_webpack_strategy.rb +++ b/lib/mastodon/premailer_webpack_strategy.rb @@ -2,6 +2,9 @@ module PremailerWebpackStrategy def load(url) + public_path_host = ENV['ASSET_HOST'] || ENV['LOCAL_DOMAIN'] + url = url.gsub(/\A\/\/#{public_path_host}/, '') + if Webpacker.dev_server.running? url = File.join("#{Webpacker.dev_server.protocol}://#{Webpacker.dev_server.host_with_port}", url) HTTP.get(url).to_s -- cgit From 79b34a0fa2e993967f22892b857dcf601c07bd88 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 20 Jan 2018 01:32:37 +0100 Subject: Restore onboarding modal (#6303) * Restore onboarding modal Revert 5ba8b3a396895ecec083c5258aaf9084d584a7c4 * Change greeting elephant graphic, fix up some design issues * Fix wrong link color in onboarding modal --- app/javascript/images/elephant_ui_greeting.svg | 1 + app/javascript/mastodon/actions/onboarding.js | 14 + app/javascript/mastodon/containers/mastodon.js | 3 + .../mastodon/features/ui/components/modal_root.js | 2 + .../features/ui/components/onboarding_modal.js | 324 ++++++++++++++++++++ .../mastodon/features/ui/util/async-components.js | 4 + app/javascript/mastodon/locales/en.json | 2 +- app/javascript/styles/mastodon/components.scss | 333 ++++++++++++++++++++- 8 files changed, 679 insertions(+), 4 deletions(-) create mode 100644 app/javascript/images/elephant_ui_greeting.svg create mode 100644 app/javascript/mastodon/actions/onboarding.js create mode 100644 app/javascript/mastodon/features/ui/components/onboarding_modal.js (limited to 'app/javascript/images') diff --git a/app/javascript/images/elephant_ui_greeting.svg b/app/javascript/images/elephant_ui_greeting.svg new file mode 100644 index 000000000..f3eb4b142 --- /dev/null +++ b/app/javascript/images/elephant_ui_greeting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/mastodon/actions/onboarding.js b/app/javascript/mastodon/actions/onboarding.js new file mode 100644 index 000000000..a161c50ef --- /dev/null +++ b/app/javascript/mastodon/actions/onboarding.js @@ -0,0 +1,14 @@ +import { openModal } from './modal'; +import { changeSetting, saveSettings } from './settings'; + +export function showOnboardingOnce() { + return (dispatch, getState) => { + const alreadySeen = getState().getIn(['settings', 'onboarded']); + + if (!alreadySeen) { + dispatch(openModal('ONBOARDING')); + dispatch(changeSetting(['onboarded'], true)); + dispatch(saveSettings()); + } + }; +}; diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index 8ae3b727a..d1710445b 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -2,6 +2,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import PropTypes from 'prop-types'; import configureStore from '../store/configureStore'; +import { showOnboardingOnce } from '../actions/onboarding'; import { BrowserRouter, Route } from 'react-router-dom'; import { ScrollContext } from 'react-router-scroll-4'; import UI from '../features/ui'; @@ -39,6 +40,8 @@ export default class Mastodon extends React.PureComponent { const handlerUrl = window.location.protocol + '//' + window.location.host + '/intent?uri=%s'; window.setTimeout(() => navigator.registerProtocolHandler('web+mastodon', handlerUrl, 'Mastodon'), 5 * 60 * 1000); } + + store.dispatch(showOnboardingOnce()); } componentWillUnmount () { diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js index dbfb46ee7..5839ba40a 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.js +++ b/app/javascript/mastodon/features/ui/components/modal_root.js @@ -9,6 +9,7 @@ import VideoModal from './video_modal'; import BoostModal from './boost_modal'; import ConfirmationModal from './confirmation_modal'; import { + OnboardingModal, MuteModal, ReportModal, EmbedModal, @@ -17,6 +18,7 @@ import { const MODAL_COMPONENTS = { 'MEDIA': () => Promise.resolve({ default: MediaModal }), + 'ONBOARDING': OnboardingModal, 'VIDEO': () => Promise.resolve({ default: VideoModal }), 'BOOST': () => Promise.resolve({ default: BoostModal }), 'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }), diff --git a/app/javascript/mastodon/features/ui/components/onboarding_modal.js b/app/javascript/mastodon/features/ui/components/onboarding_modal.js new file mode 100644 index 000000000..9b713cf9e --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/onboarding_modal.js @@ -0,0 +1,324 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import ReactSwipeableViews from 'react-swipeable-views'; +import classNames from 'classnames'; +import Permalink from '../../../components/permalink'; +import ComposeForm from '../../compose/components/compose_form'; +import Search from '../../compose/components/search'; +import NavigationBar from '../../compose/components/navigation_bar'; +import ColumnHeader from './column_header'; +import { List as ImmutableList } from 'immutable'; +import { me } from '../../../initial_state'; + +const noop = () => { }; + +const messages = defineMessages({ + home_title: { id: 'column.home', defaultMessage: 'Home' }, + notifications_title: { id: 'column.notifications', defaultMessage: 'Notifications' }, + local_title: { id: 'column.community', defaultMessage: 'Local timeline' }, + federated_title: { id: 'column.public', defaultMessage: 'Federated timeline' }, +}); + +const PageOne = ({ acct, domain }) => ( +
+
+

+

+
+ +
+
+
+ +
+ +
+ @{acct}@{domain} +
+
+ +

+
+
+); + +PageOne.propTypes = { + acct: PropTypes.string.isRequired, + domain: PropTypes.string.isRequired, +}; + +const PageTwo = ({ myAccount }) => ( +
+
+
+ + + +
+
+ +

+
+); + +PageTwo.propTypes = { + myAccount: ImmutablePropTypes.map.isRequired, +}; + +const PageThree = ({ myAccount }) => ( +
+
+ + +
+ +
+
+ +

#illustration, introductions: #introductions }} />

+

+
+); + +PageThree.propTypes = { + myAccount: ImmutablePropTypes.map.isRequired, +}; + +const PageFour = ({ domain, intl }) => ( +
+
+
+
+
+

+
+ +
+
+

+
+
+ +
+
+
+
+ +
+
+
+
+ +

+
+
+); + +PageFour.propTypes = { + domain: PropTypes.string.isRequired, + intl: PropTypes.object.isRequired, +}; + +const PageSix = ({ admin, domain }) => { + let adminSection = ''; + + if (admin) { + adminSection = ( +

+ @{admin.get('acct')} }} /> +
+ }} /> +

+ ); + } + + return ( +
+

+ {adminSection} +

GitHub }} />

+

}} />

+

+
+ ); +}; + +PageSix.propTypes = { + admin: ImmutablePropTypes.map, + domain: PropTypes.string.isRequired, +}; + +const mapStateToProps = state => ({ + myAccount: state.getIn(['accounts', me]), + admin: state.getIn(['accounts', state.getIn(['meta', 'admin'])]), + domain: state.getIn(['meta', 'domain']), +}); + +@connect(mapStateToProps) +@injectIntl +export default class OnboardingModal extends React.PureComponent { + + static propTypes = { + onClose: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + myAccount: ImmutablePropTypes.map.isRequired, + domain: PropTypes.string.isRequired, + admin: ImmutablePropTypes.map, + }; + + state = { + currentIndex: 0, + }; + + componentWillMount() { + const { myAccount, admin, domain, intl } = this.props; + this.pages = [ + , + , + , + , + , + ]; + }; + + componentDidMount() { + window.addEventListener('keyup', this.handleKeyUp); + } + + componentWillUnmount() { + window.addEventListener('keyup', this.handleKeyUp); + } + + handleSkip = (e) => { + e.preventDefault(); + this.props.onClose(); + } + + handleDot = (e) => { + const i = Number(e.currentTarget.getAttribute('data-index')); + e.preventDefault(); + this.setState({ currentIndex: i }); + } + + handlePrev = () => { + this.setState(({ currentIndex }) => ({ + currentIndex: Math.max(0, currentIndex - 1), + })); + } + + handleNext = () => { + const { pages } = this; + this.setState(({ currentIndex }) => ({ + currentIndex: Math.min(currentIndex + 1, pages.length - 1), + })); + } + + handleSwipe = (index) => { + this.setState({ currentIndex: index }); + } + + handleKeyUp = ({ key }) => { + switch (key) { + case 'ArrowLeft': + this.handlePrev(); + break; + case 'ArrowRight': + this.handleNext(); + break; + } + } + + handleClose = () => { + this.props.onClose(); + } + + render () { + const { pages } = this; + const { currentIndex } = this.state; + const hasMore = currentIndex < pages.length - 1; + + const nextOrDoneBtn = hasMore ? ( + + ) : ( + + ); + + return ( +
+ + {pages.map((page, i) => { + const className = classNames('onboarding-modal__page__wrapper', `onboarding-modal__page__wrapper-${i}`, { + 'onboarding-modal__page__wrapper--active': i === currentIndex, + }); + + return ( +
{page}
+ ); + })} +
+ +
+
+ +
+ +
+ {pages.map((_, i) => { + const className = classNames('onboarding-modal__dot', { + active: i === currentIndex, + }); + + return ( +
+ ); + })} +
+ +
+ {nextOrDoneBtn} +
+
+
+ ); + } + +} diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index a03c4cefd..d6586680b 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -94,6 +94,10 @@ export function Mutes () { return import(/* webpackChunkName: "features/mutes" */'../../mutes'); } +export function OnboardingModal () { + return import(/* webpackChunkName: "modals/onboarding_modal" */'../components/onboarding_modal'); +} + export function MuteModal () { return import(/* webpackChunkName: "modals/mute_modal" */'../components/mute_modal'); } diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index d214fe85f..8315763bf 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -174,7 +174,7 @@ "onboarding.page_four.home": "The home timeline shows posts from people you follow.", "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.", "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", - "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}", + "onboarding.page_one.handle": "Your full handle is {handle}. This is what you would tell your friends to search for.", "onboarding.page_one.welcome": "Welcome to Mastodon!", "onboarding.page_six.admin": "Your instance's admin is {admin}.", "onboarding.page_six.almost_done": "Almost done...", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index c0a32ed05..4a9a379a8 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1419,6 +1419,10 @@ color: $primary-text-color; } + a { + color: inherit; + } + .permalink { text-decoration: none; } @@ -2760,6 +2764,7 @@ flex: 1 1 auto; align-items: center; justify-content: center; + @supports(display: grid) { // hack to fix Chrome <57 contain: strict; } @@ -2805,11 +2810,48 @@ } } -.pulse-loading { +.no-reduce-motion .pulse-loading { transform-origin: center center; animation: heartbeat 1.5s ease-in-out infinite both; } +@keyframes shake-bottom { + 0%, + 100% { + transform: rotate(0deg); + transform-origin: 50% 100%; + } + + 10% { + transform: rotate(2deg); + } + + 20%, + 40%, + 60% { + transform: rotate(-4deg); + } + + 30%, + 50%, + 70% { + transform: rotate(4deg); + } + + 80% { + transform: rotate(-2deg); + } + + 90% { + transform: rotate(2deg); + } +} + +.no-reduce-motion .shake-bottom { + transform-origin: 50% 100%; + animation: shake-bottom 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) 2s 2 both; +} + .emoji-picker-dropdown__menu { background: $simple-background-color; position: absolute; @@ -3300,6 +3342,7 @@ z-index: 100; } +.onboarding-modal, .error-modal, .embed-modal { background: $ui-secondary-color; @@ -3310,6 +3353,25 @@ flex-direction: column; } +.onboarding-modal__pager { + height: 80vh; + width: 80vw; + max-width: 520px; + max-height: 470px; + + .react-swipeable-view-container > div { + width: 100%; + height: 100%; + box-sizing: border-box; + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + display: flex; + user-select: text; + } +} + .error-modal__body { height: 80vh; width: 80vw; @@ -3343,6 +3405,23 @@ text-align: center; } +@media screen and (max-width: 550px) { + .onboarding-modal { + width: 100%; + height: 100%; + border-radius: 0; + } + + .onboarding-modal__pager { + width: 100%; + height: auto; + max-width: none; + max-height: none; + flex: 1 1 auto; + } +} + +.onboarding-modal__paginator, .error-modal__footer { flex: 0 0 auto; background: darken($ui-secondary-color, 8%); @@ -3353,20 +3432,35 @@ min-width: 33px; } + .onboarding-modal__nav, .error-modal__nav { color: darken($ui-secondary-color, 34%); - background-color: transparent; border: 0; font-size: 14px; font-weight: 500; - padding: 0; + padding: 10px 25px; line-height: inherit; height: auto; + margin: -10px; + border-radius: 4px; + background-color: transparent; &:hover, &:focus, &:active { color: darken($ui-secondary-color, 38%); + background-color: darken($ui-secondary-color, 16%); + } + + &.onboarding-modal__done, + &.onboarding-modal__next { + color: $ui-base-color; + + &:hover, + &:focus, + &:active { + color: darken($ui-base-color, 4%); + } } } } @@ -3375,6 +3469,239 @@ justify-content: center; } +.onboarding-modal__dots { + flex: 1 1 auto; + display: flex; + align-items: center; + justify-content: center; +} + +.onboarding-modal__dot { + width: 14px; + height: 14px; + border-radius: 14px; + background: darken($ui-secondary-color, 16%); + margin: 0 3px; + cursor: pointer; + + &:hover { + background: darken($ui-secondary-color, 18%); + } + + &.active { + cursor: default; + background: darken($ui-secondary-color, 24%); + } +} + +.onboarding-modal__page__wrapper { + pointer-events: none; + padding: 25px; + padding-bottom: 0; + + &.onboarding-modal__page__wrapper--active { + pointer-events: auto; + } +} + +.onboarding-modal__page { + cursor: default; + line-height: 21px; + + h1 { + font-size: 18px; + font-weight: 500; + color: $ui-base-color; + margin-bottom: 20px; + } + + a { + color: $ui-highlight-color; + + &:hover, + &:focus, + &:active { + color: lighten($ui-highlight-color, 4%); + } + } + + .navigation-bar a { + color: inherit; + } + + p { + font-size: 16px; + color: lighten($ui-base-color, 8%); + margin-top: 10px; + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + + strong { + font-weight: 500; + background: $ui-base-color; + color: $ui-secondary-color; + border-radius: 4px; + font-size: 14px; + padding: 3px 6px; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + } +} + +.onboarding-modal__page__wrapper-0 { + background: url('../images/elephant_ui_greeting.svg') no-repeat left bottom / auto 250px; + height: 100%; + padding: 0; +} + +.onboarding-modal__page-one { + &__lead { + padding: 65px; + padding-top: 45px; + padding-bottom: 0; + margin-bottom: 10px; + + h1 { + font-size: 26px; + line-height: 36px; + margin-bottom: 8px; + } + + p { + margin-bottom: 0; + } + } + + &__extra { + padding-right: 65px; + padding-left: 185px; + text-align: center; + } +} + +.display-case { + text-align: center; + font-size: 15px; + margin-bottom: 15px; + + &__label { + font-weight: 500; + color: $ui-base-color; + margin-bottom: 5px; + text-transform: uppercase; + font-size: 12px; + } + + &__case { + background: $ui-base-color; + color: $ui-secondary-color; + font-weight: 500; + padding: 10px; + border-radius: 4px; + } +} + +.onboarding-modal__page-two, +.onboarding-modal__page-three, +.onboarding-modal__page-four, +.onboarding-modal__page-five { + p { + text-align: left; + } + + .figure { + background: darken($ui-base-color, 8%); + color: $ui-secondary-color; + margin-bottom: 20px; + border-radius: 4px; + padding: 10px; + text-align: center; + font-size: 14px; + box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.3); + + .onboarding-modal__image { + border-radius: 4px; + margin-bottom: 10px; + } + + &.non-interactive { + pointer-events: none; + text-align: left; + } + } +} + +.onboarding-modal__page-four__columns { + .row { + display: flex; + margin-bottom: 20px; + + & > div { + flex: 1 1 0; + margin: 0 10px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + + p { + text-align: center; + } + } + + &:last-child { + margin-bottom: 0; + } + } + + .column-header { + color: $primary-text-color; + } +} + +@media screen and (max-width: 320px) and (max-height: 600px) { + .onboarding-modal__page p { + font-size: 14px; + line-height: 20px; + } + + .onboarding-modal__page-two .figure, + .onboarding-modal__page-three .figure, + .onboarding-modal__page-four .figure, + .onboarding-modal__page-five .figure { + font-size: 12px; + margin-bottom: 10px; + } + + .onboarding-modal__page-four__columns .row { + margin-bottom: 10px; + } + + .onboarding-modal__page-four__columns .column-header { + padding: 5px; + font-size: 12px; + } +} + +.onboard-sliders { + display: inline-block; + max-width: 30px; + max-height: auto; + margin-left: 10px; +} + .boost-modal, .confirmation-modal, .report-modal, -- cgit