2023-04-23 16:24:53 -04:00
import PropTypes from 'prop-types' ;
2023-05-23 11:15:17 -04:00
import { FormattedMessage , injectIntl , defineMessages } from 'react-intl' ;
import { Helmet } from 'react-helmet' ;
2023-10-19 13:44:55 -04:00
import { Link , withRouter } from 'react-router-dom' ;
2023-05-23 11:15:17 -04:00
2023-04-23 16:24:53 -04:00
import ImmutablePropTypes from 'react-immutable-proptypes' ;
2023-05-23 11:15:17 -04:00
import ImmutablePureComponent from 'react-immutable-pure-component' ;
2023-04-23 16:24:53 -04:00
import { connect } from 'react-redux' ;
2023-05-23 11:15:17 -04:00
2023-10-24 13:45:08 -04:00
import { ReactComponent as AccountCircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg' ;
import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg' ;
import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg' ;
import { ReactComponent as EditNoteIcon } from '@material-symbols/svg-600/outlined/edit_note.svg' ;
import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg' ;
2023-05-23 11:15:17 -04:00
import { debounce } from 'lodash' ;
import illustration from 'mastodon/../images/elephant_ui_conversation.svg' ;
import { fetchAccount } from 'mastodon/actions/accounts' ;
2023-04-23 16:24:53 -04:00
import { focusCompose } from 'mastodon/actions/compose' ;
2023-05-23 11:15:17 -04:00
import { closeOnboarding } from 'mastodon/actions/onboarding' ;
2023-10-24 13:45:08 -04:00
import { Icon } from 'mastodon/components/icon' ;
2023-04-23 16:24:53 -04:00
import Column from 'mastodon/features/ui/components/column' ;
import { me } from 'mastodon/initial_state' ;
import { makeGetAccount } from 'mastodon/selectors' ;
2023-06-23 10:34:27 -04:00
import { assetHost } from 'mastodon/utils/config' ;
2023-10-19 13:44:55 -04:00
import { WithRouterPropTypes } from 'mastodon/utils/react_router' ;
2023-05-23 11:15:17 -04:00
import Step from './components/step' ;
2023-04-23 16:24:53 -04:00
import Follows from './follows' ;
import Share from './share' ;
2023-04-28 04:05:34 -04:00
const messages = defineMessages ( {
template : { id : 'onboarding.compose.template' , defaultMessage : 'Hello #Mastodon!' } ,
} ) ;
2023-04-23 16:24:53 -04:00
const mapStateToProps = ( ) => {
const getAccount = makeGetAccount ( ) ;
return state => ( {
account : getAccount ( state , me ) ,
} ) ;
} ;
class Onboarding extends ImmutablePureComponent {
static propTypes = {
dispatch : PropTypes . func . isRequired ,
account : ImmutablePropTypes . map ,
2023-05-11 01:55:10 -04:00
multiColumn : PropTypes . bool ,
2023-10-19 13:44:55 -04:00
... WithRouterPropTypes ,
2023-04-23 16:24:53 -04:00
} ;
state = {
step : null ,
profileClicked : false ,
shareClicked : false ,
} ;
handleClose = ( ) => {
2023-10-19 13:44:55 -04:00
const { dispatch , history } = this . props ;
2023-04-23 16:24:53 -04:00
dispatch ( closeOnboarding ( ) ) ;
2023-10-19 13:44:55 -04:00
history . push ( '/home' ) ;
2023-04-23 16:24:53 -04:00
} ;
handleProfileClick = ( ) => {
this . setState ( { profileClicked : true } ) ;
} ;
handleFollowClick = ( ) => {
this . setState ( { step : 'follows' } ) ;
} ;
handleComposeClick = ( ) => {
2023-10-19 13:44:55 -04:00
const { dispatch , intl , history } = this . props ;
2023-04-23 16:24:53 -04:00
2023-10-19 13:44:55 -04:00
dispatch ( focusCompose ( history , intl . formatMessage ( messages . template ) ) ) ;
2023-04-23 16:24:53 -04:00
} ;
handleShareClick = ( ) => {
this . setState ( { step : 'share' , shareClicked : true } ) ;
} ;
handleBackClick = ( ) => {
this . setState ( { step : null } ) ;
} ;
handleWindowFocus = debounce ( ( ) => {
const { dispatch , account } = this . props ;
dispatch ( fetchAccount ( account . get ( 'id' ) ) ) ;
} , 1000 , { trailing : true } ) ;
componentDidMount ( ) {
window . addEventListener ( 'focus' , this . handleWindowFocus , false ) ;
}
componentWillUnmount ( ) {
window . removeEventListener ( 'focus' , this . handleWindowFocus ) ;
}
render ( ) {
2023-05-11 01:55:10 -04:00
const { account , multiColumn } = this . props ;
2023-04-23 16:24:53 -04:00
const { step , shareClicked } = this . state ;
switch ( step ) {
case 'follows' :
2023-05-11 01:55:10 -04:00
return < Follows onBack = { this . handleBackClick } multiColumn = { multiColumn } / > ;
2023-04-23 16:24:53 -04:00
case 'share' :
2023-05-11 01:55:10 -04:00
return < Share onBack = { this . handleBackClick } multiColumn = { multiColumn } / > ;
2023-04-23 16:24:53 -04:00
}
return (
< Column >
< div className = 'scrollable privacy-policy' >
< div className = 'column-title' >
< img src = { illustration } alt = '' className = 'onboarding__illustration' / >
< h3 > < FormattedMessage id = 'onboarding.start.title' defaultMessage = "You've made it!" / > < / h3 >
< p > < FormattedMessage id = 'onboarding.start.lead' defaultMessage = "Your new Mastodon account is ready to go. Here's how you can make the most of it:" / > < / p >
< / div >
< div className = 'onboarding__steps' >
2023-10-24 13:45:08 -04:00
< Step onClick = { this . handleProfileClick } href = '/settings/profile' completed = { ( ! account . get ( 'avatar' ) . endsWith ( 'missing.png' ) ) || ( account . get ( 'display_name' ) . length > 0 && account . get ( 'note' ) . length > 0 ) } icon = 'address-book-o' iconComponent = { AccountCircleIcon } label = { < FormattedMessage id = 'onboarding.steps.setup_profile.title' defaultMessage = 'Customize your profile' / > } description = { < FormattedMessage id = 'onboarding.steps.setup_profile.body' defaultMessage = 'Others are more likely to interact with you with a filled out profile.' / > } / >
< Step onClick = { this . handleFollowClick } completed = { ( account . get ( 'following_count' ) * 1 ) >= 7 } icon = 'user-plus' iconComponent = { PersonAddIcon } label = { < FormattedMessage id = 'onboarding.steps.follow_people.title' defaultMessage = 'Find at least {count, plural, one {one person} other {# people}} to follow' values = { { count : 7 } } / > } description = { < FormattedMessage id = 'onboarding.steps.follow_people.body' defaultMessage = "You curate your own home feed. Let's fill it with interesting people." / > } / >
< Step onClick = { this . handleComposeClick } completed = { ( account . get ( 'statuses_count' ) * 1 ) >= 1 } icon = 'pencil-square-o' iconComponent = { EditNoteIcon } label = { < FormattedMessage id = 'onboarding.steps.publish_status.title' defaultMessage = 'Make your first post' / > } description = { < FormattedMessage id = 'onboarding.steps.publish_status.body' defaultMessage = 'Say hello to the world.' values = { { emoji : < img className = 'emojione' alt = '🐘' src = { ` ${ assetHost } /emoji/1f418.svg ` } / > } } / > } / >
< Step onClick = { this . handleShareClick } completed = { shareClicked } icon = 'copy' iconComponent = { ContentCopyIcon } label = { < FormattedMessage id = 'onboarding.steps.share_profile.title' defaultMessage = 'Share your profile' / > } description = { < FormattedMessage id = 'onboarding.steps.share_profile.body' defaultMessage = 'Let your friends know how to find you on Mastodon!' / > } / >
2023-04-23 16:24:53 -04:00
< / div >
2023-06-23 10:34:27 -04:00
< p className = 'onboarding__lead' > < FormattedMessage id = 'onboarding.start.skip' defaultMessage = "Don't need help getting started?" / > < / p >
2023-04-23 16:24:53 -04:00
< div className = 'onboarding__links' >
< Link to = '/explore' className = 'onboarding__link' >
2023-06-23 10:34:27 -04:00
< FormattedMessage id = 'onboarding.actions.go_to_explore' defaultMessage = 'Take me to trending' / >
2023-10-24 13:45:08 -04:00
< Icon icon = { ArrowRightAltIcon } / >
2023-04-23 16:24:53 -04:00
< / Link >
2023-06-23 10:34:27 -04:00
< Link to = '/home' className = 'onboarding__link' >
< FormattedMessage id = 'onboarding.actions.go_to_home' defaultMessage = 'Take me to my home feed' / >
2023-10-24 13:45:08 -04:00
< Icon icon = { ArrowRightAltIcon } / >
2023-06-23 10:34:27 -04:00
< / Link >
2023-04-23 16:24:53 -04:00
< / div >
< / div >
< Helmet >
< meta name = 'robots' content = 'noindex' / >
< / Helmet >
< / Column >
) ;
}
}
2023-10-19 13:44:55 -04:00
export default withRouter ( connect ( mapStateToProps ) ( injectIntl ( Onboarding ) ) ) ;