2017-05-02 17:04:16 -07:00
import React from 'react' ;
2017-04-16 11:32:00 -07:00
import { connect } from 'react-redux' ;
2017-04-21 11:05:35 -07:00
import PropTypes from 'prop-types' ;
2017-04-16 11:32:00 -07:00
import ImmutablePropTypes from 'react-immutable-proptypes' ;
import { defineMessages , injectIntl , FormattedMessage } from 'react-intl' ;
2017-07-09 06:02:26 -07:00
import ReactSwipeableViews from 'react-swipeable-views' ;
2017-06-05 01:09:14 -07:00
import classNames from 'classnames' ;
2017-12-03 23:26:40 -08:00
import Permalink from 'flavours/glitch/components/permalink' ;
2019-05-03 09:54:06 -07:00
import ComposeForm from 'flavours/glitch/features/compose/components/compose_form' ;
2019-04-20 11:32:16 -07:00
import DrawerAccount from 'flavours/glitch/features/compose/components/navigation_bar' ;
2019-04-20 08:50:12 -07:00
import Search from 'flavours/glitch/features/compose/components/search' ;
2017-04-16 11:32:00 -07:00
import ColumnHeader from './column_header' ;
2022-10-11 01:17:04 -07:00
import { me , source _url } from 'flavours/glitch/initial_state' ;
2017-04-16 11:32:00 -07:00
2019-04-20 08:50:12 -07:00
const noop = ( ) => { } ;
2017-04-16 11:32:00 -07:00
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' } ,
2017-05-20 08:31:47 -07:00
federated _title : { id : 'column.public' , defaultMessage : 'Federated timeline' } ,
2017-04-16 11:32:00 -07:00
} ) ;
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 >
2017-06-21 10:02:02 -07:00
< h1 > < FormattedMessage id = 'onboarding.page_one.welcome' defaultMessage = 'Welcome to {domain}!' values = { { domain } } / > < / h1 >
< p > < FormattedMessage id = 'onboarding.page_one.federation' defaultMessage = '{domain} is an "instance" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.' values = { { domain } } / > < / p >
2017-08-08 13:13:04 -07:00
< p > < FormattedMessage id = 'onboarding.page_one.handle' defaultMessage = 'You are on {domain}, so your full handle is {handle}' values = { { domain , handle : < strong > @ { acct } @ { domain } < / strong > } } / > < / p >
2017-04-16 11:32:00 -07:00
< / div >
< / div >
) ;
PageOne . propTypes = {
2017-04-21 11:05:35 -07:00
acct : PropTypes . string . isRequired ,
2017-05-20 08:31:47 -07:00
domain : PropTypes . string . isRequired ,
2017-04-16 11:32:00 -07:00
} ;
2023-05-07 09:22:25 -07:00
const PageTwo = ( { myAccount } ) => (
2017-04-16 11:32:00 -07:00
< div className = 'onboarding-modal__page onboarding-modal__page-two' >
< div className = 'figure non-interactive' >
2017-04-26 06:15:47 -07:00
< div className = 'pseudo-drawer' >
2017-12-29 14:55:06 -08:00
< DrawerAccount account = { myAccount } / >
2019-04-20 12:28:03 -07:00
< ComposeForm
2018-01-14 14:13:24 -08:00
privacy = 'public'
text = 'Awoo! #introductions'
2019-05-03 09:54:06 -07:00
spoilerText = ''
2023-02-03 11:52:07 -08:00
suggestions = { [ ] }
2017-12-27 14:28:41 -08:00
/ >
2017-04-26 06:15:47 -07:00
< / div >
2017-04-16 11:32:00 -07:00
< / 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 >
) ;
2017-04-26 06:15:47 -07:00
PageTwo . propTypes = {
2017-12-27 14:28:41 -08:00
intl : PropTypes . object . isRequired ,
2017-10-30 19:27:48 -07:00
myAccount : ImmutablePropTypes . map . isRequired ,
2017-04-26 06:15:47 -07:00
} ;
2023-05-07 09:22:25 -07:00
const PageThree = ( { myAccount } ) => (
2017-04-16 11:32:00 -07:00
< div className = 'onboarding-modal__page onboarding-modal__page-three' >
< div className = 'figure non-interactive' >
2019-04-20 08:50:12 -07:00
< Search
value = ''
onChange = { noop }
onSubmit = { noop }
onClear = { noop }
onShow = { noop }
/ >
2017-04-16 11:32:00 -07:00
< div className = 'pseudo-drawer' >
2017-12-29 14:55:06 -08:00
< DrawerAccount account = { myAccount } / >
2017-04-16 11:32:00 -07:00
< / div >
< / div >
2021-09-25 20:46:13 -07:00
< 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 = '/tag/illustration' href = '/tags/illustration' > # illustration < / Permalink > , introductions : < Permalink to = '/tag/introductions' href = '/tags/introductions' > # introductions < / Permalink > } } / > < / p >
2017-04-16 11:32:00 -07:00
< 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 = {
2017-12-27 14:28:41 -08:00
intl : PropTypes . object . isRequired ,
2017-10-30 19:27:48 -07:00
myAccount : ImmutablePropTypes . map . isRequired ,
2017-04-16 11:32:00 -07:00
} ;
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 >
2017-06-06 04:20:07 -07:00
< p > < FormattedMessage id = 'onboarding.page_four.home' defaultMessage = 'The home timeline shows posts from people you follow.' / > < / p >
2017-04-16 11:32:00 -07:00
< / div >
< div >
< div className = 'figure non-interactive' > < ColumnHeader icon = 'bell' type = { intl . formatMessage ( messages . notifications _title ) } / > < / div >
2017-04-20 09:20:40 -07:00
< p > < FormattedMessage id = 'onboarding.page_four.notifications' defaultMessage = 'The notifications column shows when someone interacts with you.' / > < / p >
2017-04-16 11:32:00 -07:00
< / div >
< / div >
< div className = 'row' >
< div >
2017-04-20 09:20:40 -07:00
< div className = 'figure non-interactive' style = { { marginBottom : 0 } } > < ColumnHeader icon = 'users' type = { intl . formatMessage ( messages . local _title ) } / > < / div >
2017-04-16 11:32:00 -07:00
< / div >
< div >
2017-04-20 09:20:40 -07:00
< div className = 'figure non-interactive' style = { { marginBottom : 0 } } > < ColumnHeader icon = 'globe' type = { intl . formatMessage ( messages . federated _title ) } / > < / div >
2017-04-16 11:32:00 -07:00
< / div >
< / div >
2017-04-20 09:20:40 -07:00
< p > < FormattedMessage id = 'onboarding.page_five.public_timelines' defaultMessage = 'The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.' values = { { domain } } / > < / p >
2017-04-16 11:32:00 -07:00
< / div >
< / div >
) ;
PageFour . propTypes = {
2017-04-21 11:05:35 -07:00
domain : PropTypes . string . isRequired ,
2017-05-20 08:31:47 -07:00
intl : PropTypes . object . isRequired ,
2017-04-16 11:32:00 -07:00
} ;
2017-04-26 05:54:12 -07:00
const PageSix = ( { admin , domain } ) => {
2017-04-16 11:32:00 -07:00
let adminSection = '' ;
if ( admin ) {
adminSection = (
< p >
2021-09-25 20:46:13 -07:00
< FormattedMessage id = 'onboarding.page_six.admin' defaultMessage = "Your instance's admin is {admin}." values = { { admin : < Permalink href = { admin . get ( 'url' ) } to = { ` /@ ${ admin . get ( 'acct' ) } ` } > @ { admin . get ( 'acct' ) } < / Permalink > } } / >
2017-04-16 11:32:00 -07:00
< br / >
2017-06-06 04:20:07 -07:00
< FormattedMessage id = 'onboarding.page_six.read_guidelines' defaultMessage = "Please read {domain}'s {guidelines}!" values = { { domain , guidelines : < a href = '/about/more' target = '_blank' > < FormattedMessage id = 'onboarding.page_six.guidelines' defaultMessage = 'community guidelines' / > < / a > } } / >
2017-04-16 11:32:00 -07:00
< / p >
) ;
}
return (
< div className = 'onboarding-modal__page onboarding-modal__page-six' >
< h1 > < FormattedMessage id = 'onboarding.page_six.almost_done' defaultMessage = 'Almost done...' / > < / h1 >
{ adminSection }
2021-09-08 04:47:48 -07:00
< p >
< FormattedMessage
id = 'onboarding.page_six.github'
defaultMessage = '{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.'
values = { {
domain ,
fork : < a href = 'https://en.wikipedia.org/wiki/Fork_(software_development)' target = '_blank' rel = 'noopener' > fork < / a > ,
2022-11-01 01:38:05 -07:00
Mastodon : < a href = 'https://github.com/mastodon/mastodon' target = '_blank' rel = 'noopener' > Mastodon < / a > ,
2021-09-08 04:47:48 -07:00
github : < a href = { source _url } target = '_blank' rel = 'noopener' > GitHub < / a > ,
} }
/ >
< / p >
2018-10-07 11:51:50 -07:00
< p > < FormattedMessage id = 'onboarding.page_six.apps_available' defaultMessage = 'There are {apps} available for iOS, Android and other platforms.' values = { { domain , apps : < a href = 'https://joinmastodon.org/apps' target = '_blank' rel = 'noopener' > < FormattedMessage id = 'onboarding.page_six.various_app' defaultMessage = 'mobile apps' / > < / a > } } / > < / p >
2017-04-20 09:20:40 -07:00
< p > < em > < FormattedMessage id = 'onboarding.page_six.appetoot' defaultMessage = 'Bon Appetoot!' / > < / em > < / p >
2017-04-16 11:32:00 -07:00
< / div >
) ;
} ;
PageSix . propTypes = {
2017-04-26 05:54:12 -07:00
admin : ImmutablePropTypes . map ,
2017-05-20 08:31:47 -07:00
domain : PropTypes . string . isRequired ,
2017-04-16 11:32:00 -07:00
} ;
const mapStateToProps = state => ( {
2017-10-30 19:27:48 -07:00
myAccount : state . getIn ( [ 'accounts' , me ] ) ,
2017-04-16 11:32:00 -07:00
admin : state . getIn ( [ 'accounts' , state . getIn ( [ 'meta' , 'admin' ] ) ] ) ,
2017-05-20 08:31:47 -07:00
domain : state . getIn ( [ 'meta' , 'domain' ] ) ,
2017-04-16 11:32:00 -07:00
} ) ;
2019-09-09 06:16:08 -07:00
class OnboardingModal extends React . PureComponent {
2017-04-16 11:32:00 -07:00
2017-05-12 05:44:10 -07:00
static propTypes = {
onClose : PropTypes . func . isRequired ,
intl : PropTypes . object . isRequired ,
2017-10-30 19:27:48 -07:00
myAccount : ImmutablePropTypes . map . isRequired ,
2017-05-12 05:44:10 -07:00
domain : PropTypes . string . isRequired ,
2017-05-20 08:31:47 -07:00
admin : ImmutablePropTypes . map ,
2017-05-12 05:44:10 -07:00
} ;
state = {
2017-05-20 08:31:47 -07:00
currentIndex : 0 ,
2017-05-12 05:44:10 -07:00
} ;
2017-06-05 01:09:14 -07:00
componentWillMount ( ) {
2017-10-30 19:27:48 -07:00
const { myAccount , admin , domain , intl } = this . props ;
2017-06-05 01:09:14 -07:00
this . pages = [
2023-05-07 09:22:25 -07:00
< PageOne key = '1' acct = { myAccount . get ( 'acct' ) } domain = { domain } / > ,
< PageTwo key = '2' myAccount = { myAccount } intl = { intl } / > ,
< PageThree key = '3' myAccount = { myAccount } intl = { intl } / > ,
< PageFour key = '4' domain = { domain } intl = { intl } / > ,
< PageSix key = '6' admin = { admin } domain = { domain } / > ,
2017-06-05 01:09:14 -07:00
] ;
2023-02-03 11:52:07 -08:00
}
2017-06-05 01:09:14 -07:00
componentDidMount ( ) {
window . addEventListener ( 'keyup' , this . handleKeyUp ) ;
}
componentWillUnmount ( ) {
window . addEventListener ( 'keyup' , this . handleKeyUp ) ;
}
2017-05-12 05:44:10 -07:00
handleSkip = ( e ) => {
2017-04-16 11:32:00 -07:00
e . preventDefault ( ) ;
this . props . onClose ( ) ;
2023-02-03 11:52:07 -08:00
} ;
2017-04-16 11:32:00 -07:00
2017-05-19 11:58:12 -07:00
handleDot = ( e ) => {
const i = Number ( e . currentTarget . getAttribute ( 'data-index' ) ) ;
2017-04-16 11:32:00 -07:00
e . preventDefault ( ) ;
this . setState ( { currentIndex : i } ) ;
2023-02-03 11:52:07 -08:00
} ;
2017-04-16 11:32:00 -07:00
2017-06-05 01:09:14 -07:00
handlePrev = ( ) => {
this . setState ( ( { currentIndex } ) => ( {
currentIndex : Math . max ( 0 , currentIndex - 1 ) ,
} ) ) ;
2023-02-03 11:52:07 -08:00
} ;
2017-06-05 01:09:14 -07:00
2017-05-26 05:10:37 -07:00
handleNext = ( ) => {
2017-06-05 01:09:14 -07:00
const { pages } = this ;
2017-05-26 05:10:37 -07:00
this . setState ( ( { currentIndex } ) => ( {
2017-06-05 01:09:14 -07:00
currentIndex : Math . min ( currentIndex + 1 , pages . length - 1 ) ,
2017-05-26 05:10:37 -07:00
} ) ) ;
2023-02-03 11:52:07 -08:00
} ;
2017-04-16 11:32:00 -07:00
2017-07-09 06:02:26 -07:00
handleSwipe = ( index ) => {
this . setState ( { currentIndex : index } ) ;
2023-02-03 11:52:07 -08:00
} ;
2017-07-09 06:02:26 -07:00
2017-06-05 01:09:14 -07:00
handleKeyUp = ( { key } ) => {
switch ( key ) {
case 'ArrowLeft' :
this . handlePrev ( ) ;
break ;
case 'ArrowRight' :
this . handleNext ( ) ;
break ;
}
2023-02-03 11:52:07 -08:00
} ;
2017-06-05 01:09:14 -07:00
2017-05-26 05:10:37 -07:00
handleClose = ( ) => {
this . props . onClose ( ) ;
2023-02-03 11:52:07 -08:00
} ;
2017-04-16 11:32:00 -07:00
render ( ) {
2017-06-05 01:09:14 -07:00
const { pages } = this ;
2017-04-16 11:32:00 -07:00
const { currentIndex } = this . state ;
const hasMore = currentIndex < pages . length - 1 ;
2017-05-26 05:10:37 -07:00
const nextOrDoneBtn = hasMore ? (
< button
onClick = { this . handleNext }
className = 'onboarding-modal__nav onboarding-modal__next'
>
< FormattedMessage id = 'onboarding.next' defaultMessage = 'Next' / >
< / button >
) : (
< button
onClick = { this . handleClose }
className = 'onboarding-modal__nav onboarding-modal__done'
>
< FormattedMessage id = 'onboarding.done' defaultMessage = 'Done' / >
< / button >
) ;
2017-04-16 11:32:00 -07:00
return (
< div className = 'modal-root__modal onboarding-modal' >
2017-07-09 06:02:26 -07:00
< ReactSwipeableViews index = { currentIndex } onChangeIndex = { this . handleSwipe } className = 'onboarding-modal__pager' >
{ pages . map ( ( page , i ) => {
const className = classNames ( 'onboarding-modal__page__wrapper' , {
'onboarding-modal__page__wrapper--active' : i === currentIndex ,
} ) ;
return (
< div key = { i } className = { className } > { page } < / div >
) ;
} ) }
< / ReactSwipeableViews >
2017-04-16 11:32:00 -07:00
< div className = 'onboarding-modal__paginator' >
< div >
2017-05-26 05:10:37 -07:00
< button
onClick = { this . handleSkip }
className = 'onboarding-modal__nav onboarding-modal__skip'
>
< FormattedMessage id = 'onboarding.skip' defaultMessage = 'Skip' / >
< / button >
2017-04-16 11:32:00 -07:00
< / div >
< div className = 'onboarding-modal__dots' >
2017-06-05 01:09:14 -07:00
{ pages . map ( ( _ , i ) => {
const className = classNames ( 'onboarding-modal__dot' , {
active : i === currentIndex ,
} ) ;
return (
< div
key = { ` dot- ${ i } ` }
role = 'button'
2023-04-04 07:33:44 -07:00
tabIndex = { 0 }
2017-06-05 01:09:14 -07:00
data - index = { i }
onClick = { this . handleDot }
className = { className }
/ >
) ;
} ) }
2017-04-16 11:32:00 -07:00
< / div >
< div >
{ nextOrDoneBtn }
< / div >
< / div >
< / div >
) ;
}
2017-04-21 11:05:35 -07:00
}
2023-03-24 15:15:25 -07:00
export default connect ( mapStateToProps ) ( injectIntl ( OnboardingModal ) ) ;