2023-04-23 20:24:53 +00:00
import PropTypes from 'prop-types' ;
2023-12-02 18:21:43 +00:00
import { PureComponent } from 'react' ;
2023-05-31 21:43:39 +00:00
import { defineMessages , injectIntl , FormattedMessage } from 'react-intl' ;
2023-12-02 18:21:43 +00:00
2023-04-23 20:24:53 +00:00
import classNames from 'classnames' ;
import { Link } from 'react-router-dom' ;
2023-12-02 18:21:43 +00:00
import ImmutablePropTypes from 'react-immutable-proptypes' ;
import { connect } from 'react-redux' ;
2023-04-25 09:12:37 +00:00
import SwipeableViews from 'react-swipeable-views' ;
2023-04-23 20:24:53 +00:00
2023-12-02 18:21:43 +00:00
import Column from 'flavours/glitch/components/column' ;
import ColumnBackButton from 'flavours/glitch/components/column_back_button' ;
import { Icon } from 'flavours/glitch/components/icon' ;
import { me , domain } from 'flavours/glitch/initial_state' ;
import ArrowSmallRight from './components/arrow_small_right' ;
2023-04-23 20:24:53 +00:00
const messages = defineMessages ( {
2023-04-25 09:12:37 +00:00
shareableMessage : { id : 'onboarding.share.message' , defaultMessage : 'I\'m {username} on #Mastodon! Come follow me at {url}' } ,
2023-04-23 20:24:53 +00:00
} ) ;
const mapStateToProps = state => ( {
account : state . getIn ( [ 'accounts' , me ] ) ,
} ) ;
2023-05-23 08:52:27 +00:00
class CopyPasteText extends PureComponent {
2023-04-23 20:24:53 +00:00
static propTypes = {
value : PropTypes . string ,
} ;
state = {
copied : false ,
focused : false ,
} ;
setRef = c => {
this . input = c ;
} ;
handleInputClick = ( ) => {
this . setState ( { copied : false } ) ;
this . input . focus ( ) ;
this . input . select ( ) ;
this . input . setSelectionRange ( 0 , this . props . value . length ) ;
} ;
handleButtonClick = e => {
e . stopPropagation ( ) ;
const { value } = this . props ;
navigator . clipboard . writeText ( value ) ;
this . input . blur ( ) ;
this . setState ( { copied : true } ) ;
this . timeout = setTimeout ( ( ) => this . setState ( { copied : false } ) , 700 ) ;
} ;
handleFocus = ( ) => {
this . setState ( { focused : true } ) ;
} ;
handleBlur = ( ) => {
this . setState ( { focused : false } ) ;
} ;
componentWillUnmount ( ) {
if ( this . timeout ) clearTimeout ( this . timeout ) ;
}
render ( ) {
const { value } = this . props ;
const { copied , focused } = this . state ;
return (
< div className = { classNames ( 'copy-paste-text' , { copied , focused } ) } tabIndex = '0' role = 'button' onClick = { this . handleInputClick } >
< textarea readOnly value = { value } ref = { this . setRef } onClick = { this . handleInputClick } onFocus = { this . handleFocus } onBlur = { this . handleBlur } / >
< button className = 'button' onClick = { this . handleButtonClick } >
< Icon id = 'copy' / > { copied ? < FormattedMessage id = 'copypaste.copied' defaultMessage = 'Copied' / > : < FormattedMessage id = 'copypaste.copy_to_clipboard' defaultMessage = 'Copy to clipboard' / > }
< / button >
< / div >
) ;
}
}
2023-05-23 08:52:27 +00:00
class TipCarousel extends PureComponent {
2023-04-25 09:12:37 +00:00
static propTypes = {
children : PropTypes . node ,
} ;
state = {
index : 0 ,
} ;
handleSwipe = index => {
this . setState ( { index } ) ;
} ;
handleChangeIndex = e => {
this . setState ( { index : Number ( e . currentTarget . getAttribute ( 'data-index' ) ) } ) ;
} ;
handleKeyDown = e => {
switch ( e . key ) {
case 'ArrowLeft' :
e . preventDefault ( ) ;
this . setState ( ( { index } , { children } ) => ( { index : Math . abs ( index - 1 ) % children . length } ) ) ;
break ;
case 'ArrowRight' :
e . preventDefault ( ) ;
this . setState ( ( { index } , { children } ) => ( { index : ( index + 1 ) % children . length } ) ) ;
break ;
}
} ;
render ( ) {
const { children } = this . props ;
const { index } = this . state ;
return (
< div className = 'tip-carousel' tabIndex = '0' onKeyDown = { this . handleKeyDown } >
< SwipeableViews onChangeIndex = { this . handleSwipe } index = { index } enableMouseEvents tabIndex = '-1' >
{ children }
< / SwipeableViews >
< div className = 'media-modal__pagination' >
{ children . map ( ( _ , i ) => (
< button key = { i } className = { classNames ( 'media-modal__page-dot' , { active : i === index } ) } data - index = { i } onClick = { this . handleChangeIndex } >
{ i + 1 }
< / button >
) ) }
< / div >
< / div >
) ;
}
}
2023-05-23 08:52:27 +00:00
class Share extends PureComponent {
2023-04-23 20:24:53 +00:00
static propTypes = {
onBack : PropTypes . func ,
account : ImmutablePropTypes . map ,
2023-05-11 05:55:10 +00:00
multiColumn : PropTypes . bool ,
2023-04-23 20:24:53 +00:00
intl : PropTypes . object ,
} ;
render ( ) {
2023-05-11 05:55:10 +00:00
const { onBack , account , multiColumn , intl } = this . props ;
2023-04-23 20:24:53 +00:00
const url = ( new URL ( ` /@ ${ account . get ( 'username' ) } ` , document . baseURI ) ) . href ;
return (
< Column >
2023-05-11 05:55:10 +00:00
< ColumnBackButton multiColumn = { multiColumn } onClick = { onBack } / >
2023-04-23 20:24:53 +00:00
< div className = 'scrollable privacy-policy' >
< div className = 'column-title' >
< h3 > < FormattedMessage id = 'onboarding.share.title' defaultMessage = 'Share your profile' / > < / h3 >
< p > < FormattedMessage id = 'onboarding.share.lead' defaultMessage = 'Let people know how they can find you on Mastodon!' / > < / p >
< / div >
< CopyPasteText value = { intl . formatMessage ( messages . shareableMessage , { username : ` @ ${ account . get ( 'username' ) } @ ${ domain } ` , url } ) } / >
2023-04-25 09:12:37 +00:00
< TipCarousel >
2023-05-31 21:43:39 +00:00
< div > < p className = 'onboarding__lead' > < FormattedMessage id = 'onboarding.tips.verification' defaultMessage = '<strong>Did you know?</strong> You can verify your account by putting a link to your Mastodon profile on your own website and adding the website to your profile. No fees or documents necessary!' values = { { strong : chunks => < strong > { chunks } < / strong > } } / > < / p > < / div >
< div > < p className = 'onboarding__lead' > < FormattedMessage id = 'onboarding.tips.migration' defaultMessage = '<strong>Did you know?</strong> If you feel like {domain} is not a great server choice for you in the future, you can move to another Mastodon server without losing your followers. You can even host your own server!' values = { { domain , strong : chunks => < strong > { chunks } < / strong > } } / > < / p > < / div >
< div > < p className = 'onboarding__lead' > < FormattedMessage id = 'onboarding.tips.2fa' defaultMessage = '<strong>Did you know?</strong> You can secure your account by setting up two-factor authentication in your account settings. It works with any TOTP app of your choice, no phone number necessary!' values = { { strong : chunks => < strong > { chunks } < / strong > } } / > < / p > < / div >
2023-04-25 09:12:37 +00:00
< / TipCarousel >
2023-04-23 20:24:53 +00:00
< p className = 'onboarding__lead' > < FormattedMessage id = 'onboarding.share.next_steps' defaultMessage = 'Possible next steps:' / > < / p >
< div className = 'onboarding__links' >
< Link to = '/home' className = 'onboarding__link' >
< ArrowSmallRight / >
< FormattedMessage id = 'onboarding.actions.go_to_home' defaultMessage = 'Go to your home feed' / >
< / Link >
< Link to = '/explore' className = 'onboarding__link' >
< ArrowSmallRight / >
< FormattedMessage id = 'onboarding.actions.go_to_explore' defaultMessage = "See what's trending" / >
< / Link >
< / div >
< div className = 'onboarding__footer' >
< button className = 'link-button' onClick = { onBack } > < FormattedMessage id = 'onboarding.action.back' defaultMessage = 'Take me back' / > < / button >
< / div >
< / div >
< / Column >
) ;
}
}
2023-05-09 01:11:56 +00:00
export default connect ( mapStateToProps ) ( injectIntl ( Share ) ) ;