From 6d70a8026309c2d41e2402ec84875ced8e181105 Mon Sep 17 00:00:00 2001 From: Eugen <eugen@zeonfederated.com> Date: Sun, 16 Apr 2017 20:32:00 +0200 Subject: [PATCH] Onboarding modal (#1883) * Basic onboarding modal that's shown to users once * Lay out pages 2 through 5, add images, style modals (#1509) * Lay out pages 2 through 5 Added images and laid out pages 2 through 5 in the jsx file. SCSS will come, still working on just seeing if this works at all. * Fix jsx errors, add images to modal pages, style modal pages * Add animations to onboarding pager changes, improve wording and styling * Finishing touches on the onboarding * Add missing propTypes * Update wording --- app/assets/images/elephant-friend.png | Bin 0 -> 24466 bytes .../components/actions/onboarding.jsx | 14 + .../components/containers/mastodon.jsx | 3 + .../features/ui/components/modal_root.jsx | 2 + .../ui/components/onboarding_modal.jsx | 251 ++++++++++++++++++ .../components/reducers/settings.jsx | 2 + app/assets/stylesheets/components.scss | 238 +++++++++++++++++ app/controllers/home_controller.rb | 1 + app/views/home/initial_state.json.rabl | 9 +- 9 files changed, 517 insertions(+), 3 deletions(-) create mode 100644 app/assets/images/elephant-friend.png create mode 100644 app/assets/javascripts/components/actions/onboarding.jsx create mode 100644 app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx diff --git a/app/assets/images/elephant-friend.png b/app/assets/images/elephant-friend.png new file mode 100644 index 0000000000000000000000000000000000000000..3c5145ba987ff14621d0632cec74e02eb3886d8b GIT binary patch literal 24466 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 Y1M{*FjGCj1a{vGU07*qoM6N<$f|KMh4FCWD literal 0 HcmV?d00001 diff --git a/app/assets/javascripts/components/actions/onboarding.jsx b/app/assets/javascripts/components/actions/onboarding.jsx new file mode 100644 index 00000000000..a161c50efed --- /dev/null +++ b/app/assets/javascripts/components/actions/onboarding.jsx @@ -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/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index a771d12696d..08576913e3a 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -8,6 +8,7 @@ import { connectTimeline, disconnectTimeline } from '../actions/timelines'; +import { showOnboardingOnce } from '../actions/onboarding'; import { updateNotifications, refreshNotifications } from '../actions/notifications'; import createBrowserHistory from 'history/lib/createBrowserHistory'; import { @@ -134,6 +135,8 @@ const Mastodon = React.createClass({ if (typeof window.Notification !== 'undefined' && Notification.permission === 'default') { Notification.requestPermission(); } + + store.dispatch(showOnboardingOnce()); }, componentWillUnmount () { diff --git a/app/assets/javascripts/components/features/ui/components/modal_root.jsx b/app/assets/javascripts/components/features/ui/components/modal_root.jsx index a1ed8fd8822..ace3e085f50 100644 --- a/app/assets/javascripts/components/features/ui/components/modal_root.jsx +++ b/app/assets/javascripts/components/features/ui/components/modal_root.jsx @@ -1,11 +1,13 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import MediaModal from './media_modal'; +import OnboardingModal from './onboarding_modal'; import VideoModal from './video_modal'; import BoostModal from './boost_modal'; import { TransitionMotion, spring } from 'react-motion'; const MODAL_COMPONENTS = { 'MEDIA': MediaModal, + 'ONBOARDING': OnboardingModal, 'VIDEO': VideoModal, 'BOOST': BoostModal }; diff --git a/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx b/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx new file mode 100644 index 00000000000..8d5132ea251 --- /dev/null +++ b/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx @@ -0,0 +1,251 @@ +import { connect } from 'react-redux'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import Permalink from '../../../components/permalink'; +import { TransitionMotion, spring } from 'react-motion'; +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 Immutable from 'immutable'; + +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 }) => ( + <div className='onboarding-modal__page onboarding-modal__page-one'> + <div style={{ flex: '0 0 auto' }}> + <div className='onboarding-modal__page-one__elephant-friend' /> + </div> + + <div> + <h1><FormattedMessage id='onboarding.page_one.welcome' defaultMessage='Welcome to Mastodon!' /></h1> + <p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='Mastodon is a social network that belongs to everyone.' /></p> + <p><FormattedMessage id='onboarding.page_one.handle' defaultMessage='You are on {domain}, one of many independent Mastodon instances. Your full handle is {handle}' values={{ domain, handle: <strong>{acct}@{domain}</strong> }}/></p> + </div> + </div> +); + +PageOne.propTypes = { + acct: React.PropTypes.string.isRequired, + domain: React.PropTypes.string.isRequired +}; + +const PageTwo = () => ( + <div className='onboarding-modal__page onboarding-modal__page-two'> + <div className='figure non-interactive'> + <ComposeForm + text='Awoo! #introductions' + suggestions={Immutable.List()} + mentionedDomains={[]} + onChange={() => {}} + onSubmit={() => {}} + onPaste={() => {}} + onPickEmoji={() => {}} + onChangeSpoilerText={() => {}} + onClearSuggestions={() => {}} + onFetchSuggestions={() => {}} + onSuggestionSelected={() => {}} + /> + </div> + + <p><FormattedMessage id='onboarding.page_two.compose' defaultMessage='Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.' /></p> + </div> +); + +const PageThree = ({ me, domain }) => ( + <div className='onboarding-modal__page onboarding-modal__page-three'> + <div className='figure non-interactive'> + <Search + value='' + onChange={() => {}} + onSubmit={() => {}} + onClear={() => {}} + onShow={() => {}} + /> + + <div className='pseudo-drawer'> + <NavigationBar account={me} /> + </div> + </div> + + <p><FormattedMessage id='onboarding.page_three.search' defaultMessage='Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.' values={{ illustration: <Permalink to='/timelines/tag/illustration' href='/tags/illustration'>#illustration</Permalink>, introductions: <Permalink to='/timelines/tag/introductions' href='/tags/introductions'>#introductions</Permalink> }}/></p> + <p><FormattedMessage id='onboarding.page_three.profile' defaultMessage='Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.' /></p> + </div> +); + +PageThree.propTypes = { + me: ImmutablePropTypes.map.isRequired, + domain: React.PropTypes.string.isRequired +}; + +const PageFour = ({ domain, intl }) => ( + <div className='onboarding-modal__page onboarding-modal__page-four'> + <div className='onboarding-modal__page-four__columns'> + <div className='row'> + <div> + <div className='figure non-interactive'><ColumnHeader icon='home' type={intl.formatMessage(messages.home_title)} /></div> + <p><FormattedMessage id='onboarding.page_four.home' defaultMessage='Home timeline shows posts from people you follow'/></p> + </div> + + <div> + <div className='figure non-interactive'><ColumnHeader icon='bell' type={intl.formatMessage(messages.notifications_title)} /></div> + <p><FormattedMessage id='onboarding.page_four.notifications' defaultMessage='Notifications show when someone interacts with you' /></p> + </div> + </div> + + <div className='row'> + <div> + <div className='figure non-interactive' style={{ marginBottom: 0 }}><ColumnHeader icon='globe' type={intl.formatMessage(messages.federated_title)} /></div> + </div> + + <div> + <div className='figure non-interactive' style={{ marginBottom: 0 }}><ColumnHeader icon='users' type={intl.formatMessage(messages.local_title)} /></div> + </div> + </div> + + <p><FormattedMessage id='onboarding.page_five.public_timelines' defaultMessage='Federated timeline lists public posts from everyone who people on {domain} follow. Local timeline is the same, but limited to people on {domain}.' values={{ domain }} /></p> + </div> + </div> +); + +PageFour.propTypes = { + domain: React.PropTypes.string.isRequired, + intl: React.PropTypes.object.isRequired +}; + +const PageSix = ({ admin }) => { + let adminSection = ''; + + if (admin) { + adminSection = ( + <p> + <FormattedMessage id='onboarding.page_six.admin' defaultMessage="Your instance's admin is {admin}." values={{ admin: <Permalink href={admin.get('url')} to={`/accounts/${admin.get('id')}`}>@{admin.get('acct')}</Permalink> }} /> + <br /> + <FormattedMessage id='onboarding.page_six.read_guidelines' defaultMessage='Please, do not forget to read the {guidelines}!' values={{ guidelines: <a href='/about/more' target='_blank'><FormattedMessage id='onboarding.page_six.guidelines' defaultMessage='community guidelines' /></a> }}/> + </p> + ); + } + + return ( + <div className='onboarding-modal__page onboarding-modal__page-six'> + <h1><FormattedMessage id='onboarding.page_six.almost_done' defaultMessage='Almost done...' /></h1> + {adminSection} + <p><FormattedMessage id='onboarding.page_six.github' defaultMessage='Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.' values={{ github: <a href='https://github.com/tootsuite/mastodon' target='_blank' rel='noopener'>GitHub</a> }} /></p> + <p><FormattedMessage id='onboarding.page_six.apps_available' defaultMessage='There are {apps} available for iOS, Android and other platforms. And now... Bon Appetoot!' values={{ apps: <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md' target='_blank' rel='noopener'><FormattedMessage id='onboarding.page_six.various_app' defaultMessage='various mobile apps' /></a> }} /></p> + </div> + ); +}; + +PageSix.propTypes = { + admin: ImmutablePropTypes.map +}; + +const mapStateToProps = state => ({ + me: state.getIn(['accounts', state.getIn(['meta', 'me'])]), + admin: state.getIn(['accounts', state.getIn(['meta', 'admin'])]), + domain: state.getIn(['meta', 'domain']) +}); + +const OnboardingModal = React.createClass({ + + propTypes: { + onClose: React.PropTypes.func.isRequired, + intl: React.PropTypes.object.isRequired, + me: ImmutablePropTypes.map.isRequired, + domain: React.PropTypes.string.isRequired, + admin: ImmutablePropTypes.map + }, + + getInitialState () { + return { + currentIndex: 0 + }; + }, + + mixins: [PureRenderMixin], + + handleSkip (e) { + e.preventDefault(); + this.props.onClose(); + }, + + handleDot (i, e) { + e.preventDefault(); + this.setState({ currentIndex: i }); + }, + + handleNext (maxNum, e) { + e.preventDefault(); + + if (this.state.currentIndex < maxNum - 1) { + this.setState({ currentIndex: this.state.currentIndex + 1 }); + } else { + this.props.onClose(); + } + }, + + render () { + const { me, admin, domain, intl } = this.props; + + const pages = [ + <PageOne acct={me.get('acct')} domain={domain} />, + <PageTwo />, + <PageThree me={me} domain={domain} />, + <PageFour domain={domain} intl={intl} />, + <PageSix admin={admin} /> + ]; + + const { currentIndex } = this.state; + const hasMore = currentIndex < pages.length - 1; + + let nextOrDoneBtn; + + if(hasMore) { + nextOrDoneBtn = <a href='#' onClick={this.handleNext.bind(null, pages.length)} className='onboarding-modal__nav onboarding-modal__next'><FormattedMessage id='onboarding.next' defaultMessage='Next' /></a>; + } else { + nextOrDoneBtn = <a href='#' onClick={this.handleNext.bind(null, pages.length)} className='onboarding-modal__nav onboarding-modal__done'><FormattedMessage id='onboarding.next' defaultMessage='Done' /></a>; + } + + const styles = pages.map((page, i) => ({ + key: i, + style: { opacity: spring(i === currentIndex ? 1 : 0) } + })); + + return ( + <div className='modal-root__modal onboarding-modal'> + <TransitionMotion styles={styles}> + {interpolatedStyles => + <div className='onboarding-modal__pager'> + {pages.map((page, i) => + <div key={i} style={{ opacity: interpolatedStyles[i].style.opacity, pointerEvents: i === currentIndex ? 'auto' : 'none' }}>{page}</div> + )} + </div> + } + </TransitionMotion> + + <div className='onboarding-modal__paginator'> + <div> + <a href='#' className='onboarding-modal__skip' onClick={this.handleSkip}><FormattedMessage id='onboarding.skip' defaultMessage='Skip' /></a> + </div> + + <div className='onboarding-modal__dots'> + {pages.map((_, i) => <div key={i} onClick={this.handleDot.bind(null, i)} className={`onboarding-modal__dot ${i === currentIndex ? 'active' : ''}`} />)} + </div> + + <div> + {nextOrDoneBtn} + </div> + </div> + </div> + ); + } + +}); + +export default connect(mapStateToProps)(injectIntl(OnboardingModal)); diff --git a/app/assets/javascripts/components/reducers/settings.jsx b/app/assets/javascripts/components/reducers/settings.jsx index 8acc3facaf0..820af99edb4 100644 --- a/app/assets/javascripts/components/reducers/settings.jsx +++ b/app/assets/javascripts/components/reducers/settings.jsx @@ -3,6 +3,8 @@ import { STORE_HYDRATE } from '../actions/store'; import Immutable from 'immutable'; const initialState = Immutable.Map({ + onboarded: false, + home: Immutable.Map({ shows: Immutable.Map({ reblog: true, diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index 8bd35819a93..84344e94d4c 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -932,6 +932,12 @@ a.status__content__spoiler-link { } } +.pseudo-drawer { + background: lighten($color1, 13%); + font-size: 13px; + text-align: left; +} + .drawer__header { flex: 0 0 auto; font-size: 16px; @@ -2018,6 +2024,7 @@ button.icon-button.active i.fa-retweet { .modal-root__modal { pointer-events: auto; display: flex; + z-index: 9999; } .media-modal { @@ -2031,6 +2038,237 @@ button.icon-button.active i.fa-retweet { } } +.onboarding-modal { + background: $color2; + color: $color1; + border-radius: 8px; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.onboarding-modal__pager { + height: 80vh; + width: 80vw; + max-width: 500px; + max-height: 350px; + position: relative; + + & > div { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-sizing: border-box; + padding: 25px; + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + display: flex; + opacity: 0; + user-select: text; + } +} + +@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 { + flex: 0 0 auto; + background: darken($color2, 8%); + display: flex; + padding: 25px; + + & > div { + min-width: 33px; + } + + a { + color: darken($color2, 34%); + text-decoration: none; + font-size: 14px; + font-weight: 500; + + &:hover, &:focus, &:active { + color: darken($color2, 38%); + } + + &.onboarding-modal__done, &.onboarding-modal__next { + color: $color4; + } + } +} + +.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($color2, 16%); + margin: 0 3px; + cursor: pointer; + + &:hover { + background: darken($color2, 18%); + } + + &.active { + cursor: default; + background: darken($color2, 24%); + } +} + +.onboarding-modal__page { + cursor: default; + line-height: 21px; + + h1 { + font-size: 18px; + font-weight: 500; + color: $color1; + margin-bottom: 20px; + } + + a { + color: $color4; + + &:hover, &:focus, &:active { + color: lighten($color4, 4%); + } + } + + p { + font-size: 16px; + color: lighten($color1, 8%); + margin-top: 10px; + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + + strong { + font-weight: 500; + background: $color1; + color: $color2; + border-radius: 4px; + font-size: 14px; + padding: 3px 6px; + } + } +} + +.onboarding-modal__page-one { + display: flex; +} + +.onboarding-modal__page-one__elephant-friend { + background: image-url('elephant-friend.png') no-repeat 0 0; + width: 147px; + height: 160px; + margin-right: 10px; +} + +.onboarding-modal__page-two, +.onboarding-modal__page-three, +.onboarding-modal__page-four, +.onboarding-modal__page-five { + p { + text-align: left; + } + + .figure { + background: darken($color1, 8%); + color: $color2; + margin-bottom: 20px; + border-radius: 4px; + padding: 10px; + text-align: center; + font-size: 14px; + box-shadow: 1px 2px 6px rgba($color8, 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: $color5; + } +} + +.onboarding-modal__image { + border-radius: 8px; + width: 70vw; + max-width: 450px; + max-height: auto; + display: block; + margin: auto; + margin-bottom: 20px; +} + +.onboard-sliders { + display: inline-block; + max-width: 30px; + max-height: auto; + margin-left: 10px; +} + .boost-modal { background: lighten($color2, 8%); color: $color1; diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 2d1cf74f03e..0a25b52aa33 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -7,6 +7,7 @@ class HomeController < ApplicationController @body_classes = 'app-body' @token = find_or_create_access_token.token @web_settings = Web::Setting.find_by(user: current_user)&.data || {} + @admin = Account.find_local(Setting.site_contact_username) @streaming_api_base_url = Rails.configuration.x.streaming_api_base_url end diff --git a/app/views/home/initial_state.json.rabl b/app/views/home/initial_state.json.rabl index 9f94e6141df..ce7bfbd44cf 100644 --- a/app/views/home/initial_state.json.rabl +++ b/app/views/home/initial_state.json.rabl @@ -5,7 +5,9 @@ node(:meta) do streaming_api_base_url: @streaming_api_base_url, access_token: @token, locale: I18n.locale, + domain: Rails.configuration.x.local_domain, me: current_account.id, + admin: @admin.try(:id), boost_modal: current_account.user.setting_boost_modal, } end @@ -18,9 +20,10 @@ node(:compose) do end node(:accounts) do - { - current_account.id => partial('api/v1/accounts/show', object: current_account), - } + store = {} + store[current_account.id] = partial('api/v1/accounts/show', object: current_account) + store[@admin.id] = partial('api/v1/accounts/show', object: @admin) unless @admin.nil? + store end node(:settings) { @web_settings }