2023-04-23 16:24:53 -04:00
import PropTypes from 'prop-types' ;
2023-05-23 11:15:17 -04:00
import { PureComponent } from 'react' ;
2023-11-15 06:13:53 -05:00
import { defineMessages , useIntl , FormattedMessage } from 'react-intl' ;
2023-05-23 11:15:17 -04:00
2023-04-23 16:24:53 -04:00
import classNames from 'classnames' ;
import { Link } from 'react-router-dom' ;
2023-05-23 11:15:17 -04:00
2023-10-24 13:45:08 -04:00
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' ;
2023-04-25 05:12:37 -04:00
import SwipeableViews from 'react-swipeable-views' ;
2023-04-23 16:24:53 -04:00
2023-10-26 07:00:10 -04:00
import { ColumnBackButton } from 'mastodon/components/column_back_button' ;
2023-05-23 11:15:17 -04:00
import { Icon } from 'mastodon/components/icon' ;
import { me , domain } from 'mastodon/initial_state' ;
2023-11-15 06:13:53 -05:00
import { useAppSelector } from 'mastodon/store' ;
2023-05-23 11:15:17 -04:00
2023-04-23 16:24:53 -04:00
const messages = defineMessages ( {
2023-04-25 05:12:37 -04:00
shareableMessage : { id : 'onboarding.share.message' , defaultMessage : 'I\'m {username} on #Mastodon! Come follow me at {url}' } ,
2023-04-23 16:24:53 -04:00
} ) ;
2023-05-23 04:52:27 -04:00
class CopyPasteText extends PureComponent {
2023-04-23 16:24:53 -04: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 } >
2023-10-24 13:45:08 -04:00
< Icon id = 'copy' icon = { ContentCopyIcon } / > { copied ? < FormattedMessage id = 'copypaste.copied' defaultMessage = 'Copied' / > : < FormattedMessage id = 'copypaste.copy_to_clipboard' defaultMessage = 'Copy to clipboard' / > }
2023-04-23 16:24:53 -04:00
< / button >
< / div >
) ;
}
}
2023-05-23 04:52:27 -04:00
class TipCarousel extends PureComponent {
2023-04-25 05:12:37 -04: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-11-15 06:13:53 -05:00
export const Share = ( ) => {
const account = useAppSelector ( state => state . getIn ( [ 'accounts' , me ] ) ) ;
const intl = useIntl ( ) ;
const url = ( new URL ( ` /@ ${ account . get ( 'username' ) } ` , document . baseURI ) ) . href ;
2023-04-23 16:24:53 -04:00
2023-11-15 06:13:53 -05:00
return (
< >
< ColumnBackButton / >
2023-04-23 16:24:53 -04:00
2023-11-15 06:13:53 -05: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 >
2023-04-23 16:24:53 -04:00
2023-11-15 06:13:53 -05:00
< CopyPasteText value = { intl . formatMessage ( messages . shareableMessage , { username : ` @ ${ account . get ( 'username' ) } @ ${ domain } ` , url } ) } / >
2023-04-23 16:24:53 -04:00
2023-11-15 06:13:53 -05:00
< TipCarousel >
< 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 >
< / TipCarousel >
2023-04-23 16:24:53 -04:00
2023-11-15 06:13:53 -05: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' >
< FormattedMessage id = 'onboarding.actions.go_to_home' defaultMessage = 'Take me to my home feed' / >
< Icon icon = { ArrowRightAltIcon } / >
< / Link >
2023-04-23 16:24:53 -04:00
2023-11-15 06:13:53 -05:00
< Link to = '/explore' className = 'onboarding__link' >
< FormattedMessage id = 'onboarding.actions.go_to_explore' defaultMessage = 'Take me to trending' / >
< Icon icon = { ArrowRightAltIcon } / >
< / Link >
< / div >
< div className = 'onboarding__footer' >
< Link className = 'link-button' to = '/start' > < FormattedMessage id = 'onboarding.action.back' defaultMessage = 'Take me back' / > < / Link >
< / div >
< / div >
< / >
) ;
} ;