~cytrogen/masto-fe

ref: 8f66126b1010e0aaaa8b5f2a2f4ea8a28824e2b7 masto-fe/app/javascript/mastodon/features/onboarding/share.jsx -rw-r--r-- 6.7 KiB
8f66126b — Renaud Chaput Use the new JSX transform (#25064) 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
import { PureComponent } from 'react';
import Column from 'mastodon/components/column';
import ColumnBackButton from 'mastodon/components/column_back_button';
import PropTypes from 'prop-types';
import { me, domain } from 'mastodon/initial_state';
import { connect } from 'react-redux';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage, FormattedHTMLMessage } from 'react-intl';
import classNames from 'classnames';
import { Icon }  from 'mastodon/components/icon';
import ArrowSmallRight from './components/arrow_small_right';
import { Link } from 'react-router-dom';
import SwipeableViews from 'react-swipeable-views';

const messages = defineMessages({
  shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on #Mastodon! Come follow me at {url}' },
});

const mapStateToProps = state => ({
  account: state.getIn(['accounts', me]),
});

class CopyPasteText extends PureComponent {

  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>
    );
  }

}

class TipCarousel extends PureComponent {

  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>
    );
  }

}

class Share extends PureComponent {

  static propTypes = {
    onBack: PropTypes.func,
    account: ImmutablePropTypes.map,
    multiColumn: PropTypes.bool,
    intl: PropTypes.object,
  };

  render () {
    const { onBack, account, multiColumn, intl } = this.props;

    const url = (new URL(`/@${account.get('username')}`, document.baseURI)).href;

    return (
      <Column>
        <ColumnBackButton multiColumn={multiColumn} onClick={onBack} />

        <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 })} />

          <TipCarousel>
            <div><p className='onboarding__lead'><FormattedHTMLMessage 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!' /></p></div>
            <div><p className='onboarding__lead'><FormattedHTMLMessage 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 }} /></p></div>
            <div><p className='onboarding__lead'><FormattedHTMLMessage 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!' /></p></div>
          </TipCarousel>

          <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>
    );
  }

}

export default connect(mapStateToProps)(injectIntl(Share));