Merge pull request #2570 from ClearlyClaire/glitch-soc/ports/onboarding-profile-setup
Add profile setup to onboarding in web UI
This commit is contained in:
		
						commit
						545c0041a0
					
				| 
						 | 
					@ -714,6 +714,21 @@ export function fetchPinnedAccountsFail(error) {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const updateAccount = ({ displayName, note, avatar, header, discoverable, indexable }) => (dispatch, getState) => {
 | 
				
			||||||
 | 
					  const data = new FormData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  data.append('display_name', displayName);
 | 
				
			||||||
 | 
					  data.append('note', note);
 | 
				
			||||||
 | 
					  if (avatar) data.append('avatar', avatar);
 | 
				
			||||||
 | 
					  if (header) data.append('header', header);
 | 
				
			||||||
 | 
					  data.append('discoverable', discoverable);
 | 
				
			||||||
 | 
					  data.append('indexable', indexable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return api(getState).patch('/api/v1/accounts/update_credentials', data).then(response => {
 | 
				
			||||||
 | 
					    dispatch(importFetchedAccount(response.data));
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fetchPinnedAccountsSuggestions(q) {
 | 
					export function fetchPinnedAccountsSuggestions(q) {
 | 
				
			||||||
  return (dispatch, getState) => {
 | 
					  return (dispatch, getState) => {
 | 
				
			||||||
    dispatch(fetchPinnedAccountsSuggestionsRequest());
 | 
					    dispatch(fetchPinnedAccountsSuggestionsRequest());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@ export interface ApiAccountJSON {
 | 
				
			||||||
  bot: boolean;
 | 
					  bot: boolean;
 | 
				
			||||||
  created_at: string;
 | 
					  created_at: string;
 | 
				
			||||||
  discoverable: boolean;
 | 
					  discoverable: boolean;
 | 
				
			||||||
 | 
					  indexable: boolean;
 | 
				
			||||||
  display_name: string;
 | 
					  display_name: string;
 | 
				
			||||||
  emojis: ApiCustomEmojiJSON[];
 | 
					  emojis: ApiCustomEmojiJSON[];
 | 
				
			||||||
  fields: ApiAccountFieldJSON[];
 | 
					  fields: ApiAccountFieldJSON[];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ export default class Retention extends PureComponent {
 | 
				
			||||||
    let content;
 | 
					    let content;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (loading) {
 | 
					    if (loading) {
 | 
				
			||||||
      content = <FormattedMessage id='loading_indicator.label' defaultMessage='Loading...' />;
 | 
					      content = <FormattedMessage id='loading_indicator.label' defaultMessage='Loading…' />;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      content = (
 | 
					      content = (
 | 
				
			||||||
        <table className='retention__table'>
 | 
					        <table className='retention__table'>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,23 @@
 | 
				
			||||||
 | 
					import { useIntl, defineMessages } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { CircularProgress } from './circular_progress';
 | 
					import { CircularProgress } from './circular_progress';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const LoadingIndicator: React.FC = () => (
 | 
					const messages = defineMessages({
 | 
				
			||||||
  <div className='loading-indicator'>
 | 
					  loading: { id: 'loading_indicator.label', defaultMessage: 'Loading…' },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const LoadingIndicator: React.FC = () => {
 | 
				
			||||||
 | 
					  const intl = useIntl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      className='loading-indicator'
 | 
				
			||||||
 | 
					      role='progressbar'
 | 
				
			||||||
 | 
					      aria-busy
 | 
				
			||||||
 | 
					      aria-live='polite'
 | 
				
			||||||
 | 
					      aria-label={intl.formatMessage(messages.loading)}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
      <CircularProgress size={50} strokeWidth={6} />
 | 
					      <CircularProgress size={50} strokeWidth={6} />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
);
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,30 +0,0 @@
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					 | 
				
			||||||
import { Fragment } from 'react';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import classNames from 'classnames';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { Icon }  from 'flavours/glitch/components/icon';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ProgressIndicator = ({ steps, completed }) => (
 | 
					 | 
				
			||||||
  <div className='onboarding__progress-indicator'>
 | 
					 | 
				
			||||||
    {(new Array(steps)).fill().map((_, i) => (
 | 
					 | 
				
			||||||
      <Fragment key={i}>
 | 
					 | 
				
			||||||
        {i > 0 && <div className={classNames('onboarding__progress-indicator__line', { active: completed > i })} />}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div className={classNames('onboarding__progress-indicator__step', { active: completed > i })}>
 | 
					 | 
				
			||||||
          {completed > i && <Icon icon={CheckIcon} />}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </Fragment>
 | 
					 | 
				
			||||||
    ))}
 | 
					 | 
				
			||||||
  </div>
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ProgressIndicator.propTypes = {
 | 
					 | 
				
			||||||
  steps: PropTypes.number.isRequired,
 | 
					 | 
				
			||||||
  completed: PropTypes.number,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default ProgressIndicator;
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,13 @@
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Link } from 'react-router-dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg';
 | 
					import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg';
 | 
				
			||||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg';
 | 
					import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Icon } from 'flavours/glitch/components/icon';
 | 
					import { Icon } from 'flavours/glitch/components/icon';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Step = ({ label, description, icon, iconComponent, completed, onClick, href }) => {
 | 
					export const Step = ({ label, description, icon, iconComponent, completed, onClick, href, to }) => {
 | 
				
			||||||
  const content = (
 | 
					  const content = (
 | 
				
			||||||
    <>
 | 
					    <>
 | 
				
			||||||
      <div className='onboarding__steps__item__icon'>
 | 
					      <div className='onboarding__steps__item__icon'>
 | 
				
			||||||
| 
						 | 
					@ -29,6 +31,12 @@ const Step = ({ label, description, icon, iconComponent, completed, onClick, hre
 | 
				
			||||||
        {content}
 | 
					        {content}
 | 
				
			||||||
      </a>
 | 
					      </a>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					  } else if (to) {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Link to={to} className='onboarding__steps__item'>
 | 
				
			||||||
 | 
					        {content}
 | 
				
			||||||
 | 
					      </Link>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
| 
						 | 
					@ -45,7 +53,6 @@ Step.propTypes = {
 | 
				
			||||||
  iconComponent: PropTypes.func,
 | 
					  iconComponent: PropTypes.func,
 | 
				
			||||||
  completed: PropTypes.bool,
 | 
					  completed: PropTypes.bool,
 | 
				
			||||||
  href: PropTypes.string,
 | 
					  href: PropTypes.string,
 | 
				
			||||||
 | 
					  to: PropTypes.string,
 | 
				
			||||||
  onClick: PropTypes.func,
 | 
					  onClick: PropTypes.func,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Step;
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,44 +1,31 @@
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import { useEffect } from 'react';
 | 
				
			||||||
import { PureComponent } from 'react';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormattedMessage } from 'react-intl';
 | 
					import { FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
					import { Link } from 'react-router-dom';
 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					
 | 
				
			||||||
 | 
					import { useDispatch } from 'react-redux';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { fetchSuggestions } from 'flavours/glitch/actions/suggestions';
 | 
					import { fetchSuggestions } from 'flavours/glitch/actions/suggestions';
 | 
				
			||||||
import { markAsPartial } from 'flavours/glitch/actions/timelines';
 | 
					import { markAsPartial } from 'flavours/glitch/actions/timelines';
 | 
				
			||||||
import Column from 'flavours/glitch/components/column';
 | 
					 | 
				
			||||||
import { ColumnBackButton } from 'flavours/glitch/components/column_back_button';
 | 
					import { ColumnBackButton } from 'flavours/glitch/components/column_back_button';
 | 
				
			||||||
import { EmptyAccount } from 'flavours/glitch/components/empty_account';
 | 
					import { EmptyAccount } from 'flavours/glitch/components/empty_account';
 | 
				
			||||||
import Account from 'flavours/glitch/containers/account_container';
 | 
					import Account from 'flavours/glitch/containers/account_container';
 | 
				
			||||||
 | 
					import { useAppSelector } from 'flavours/glitch/store';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = state => ({
 | 
					export const Follows = () => {
 | 
				
			||||||
  suggestions: state.getIn(['suggestions', 'items']),
 | 
					  const dispatch = useDispatch();
 | 
				
			||||||
  isLoading: state.getIn(['suggestions', 'isLoading']),
 | 
					  const isLoading = useAppSelector(state => state.getIn(['suggestions', 'isLoading']));
 | 
				
			||||||
});
 | 
					  const suggestions = useAppSelector(state => state.getIn(['suggestions', 'items']));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Follows extends PureComponent {
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  static propTypes = {
 | 
					 | 
				
			||||||
    onBack: PropTypes.func,
 | 
					 | 
				
			||||||
    dispatch: PropTypes.func.isRequired,
 | 
					 | 
				
			||||||
    suggestions: ImmutablePropTypes.list,
 | 
					 | 
				
			||||||
    isLoading: PropTypes.bool,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  componentDidMount () {
 | 
					 | 
				
			||||||
    const { dispatch } = this.props;
 | 
					 | 
				
			||||||
    dispatch(fetchSuggestions(true));
 | 
					    dispatch(fetchSuggestions(true));
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentWillUnmount () {
 | 
					    return () => {
 | 
				
			||||||
    const { dispatch } = this.props;
 | 
					 | 
				
			||||||
      dispatch(markAsPartial('home'));
 | 
					      dispatch(markAsPartial('home'));
 | 
				
			||||||
  }
 | 
					    };
 | 
				
			||||||
 | 
					  }, [dispatch]);
 | 
				
			||||||
  render () {
 | 
					 | 
				
			||||||
    const { onBack, isLoading, suggestions } = this.props;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let loadedContent;
 | 
					  let loadedContent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,8 +38,8 @@ class Follows extends PureComponent {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
      <Column>
 | 
					    <>
 | 
				
			||||||
        <ColumnBackButton onClick={onBack} />
 | 
					      <ColumnBackButton />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div className='scrollable privacy-policy'>
 | 
					      <div className='scrollable privacy-policy'>
 | 
				
			||||||
        <div className='column-title'>
 | 
					        <div className='column-title'>
 | 
				
			||||||
| 
						 | 
					@ -67,13 +54,9 @@ class Follows extends PureComponent {
 | 
				
			||||||
        <p className='onboarding__lead'><FormattedMessage id='onboarding.tips.accounts_from_other_servers' defaultMessage='<strong>Did you know?</strong> Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!' values={{ strong: chunks => <strong>{chunks}</strong> }} /></p>
 | 
					        <p className='onboarding__lead'><FormattedMessage id='onboarding.tips.accounts_from_other_servers' defaultMessage='<strong>Did you know?</strong> Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!' values={{ strong: chunks => <strong>{chunks}</strong> }} /></p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className='onboarding__footer'>
 | 
					        <div className='onboarding__footer'>
 | 
				
			||||||
            <button className='link-button' onClick={onBack}><FormattedMessage id='onboarding.actions.back' defaultMessage='Take me back' /></button>
 | 
					          <Link className='link-button' to='/start'><FormattedMessage id='onboarding.actions.back' defaultMessage='Take me back' /></Link>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      </Column>
 | 
					    </>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  }
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default connect(mapStateToProps)(Follows);
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,117 +1,50 @@
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import { useCallback } from 'react';
 | 
				
			||||||
import React from 'react';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
 | 
					import { FormattedMessage, useIntl, defineMessages } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Helmet } from 'react-helmet';
 | 
					import { Helmet } from 'react-helmet';
 | 
				
			||||||
import { Link, withRouter } from 'react-router-dom';
 | 
					import { Link, Switch, Route, useHistory } from 'react-router-dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useDispatch } from 'react-redux';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
					 | 
				
			||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
					 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ReactComponent as AccountCircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg';
 | 
					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 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 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 EditNoteIcon } from '@material-symbols/svg-600/outlined/edit_note.svg';
 | 
				
			||||||
import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg';
 | 
					import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg';
 | 
				
			||||||
import { debounce } from 'lodash';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { fetchAccount } from 'flavours/glitch/actions/accounts';
 | 
					 | 
				
			||||||
import { focusCompose } from 'flavours/glitch/actions/compose';
 | 
					import { focusCompose } from 'flavours/glitch/actions/compose';
 | 
				
			||||||
import { closeOnboarding } from 'flavours/glitch/actions/onboarding';
 | 
					 | 
				
			||||||
import { Icon }  from 'flavours/glitch/components/icon';
 | 
					import { Icon }  from 'flavours/glitch/components/icon';
 | 
				
			||||||
import Column from 'flavours/glitch/features/ui/components/column';
 | 
					import Column from 'flavours/glitch/features/ui/components/column';
 | 
				
			||||||
import { me } from 'flavours/glitch/initial_state';
 | 
					import { me } from 'flavours/glitch/initial_state';
 | 
				
			||||||
import { makeGetAccount } from 'flavours/glitch/selectors';
 | 
					import { useAppSelector } from 'flavours/glitch/store';
 | 
				
			||||||
import { assetHost } from 'flavours/glitch/utils/config';
 | 
					import { assetHost } from 'flavours/glitch/utils/config';
 | 
				
			||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
 | 
					 | 
				
			||||||
import illustration from 'mastodon/../images/elephant_ui_conversation.svg';
 | 
					import illustration from 'mastodon/../images/elephant_ui_conversation.svg';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Step from './components/step';
 | 
					import { Step } from './components/step';
 | 
				
			||||||
import Follows from './follows';
 | 
					import { Follows } from './follows';
 | 
				
			||||||
import Share from './share';
 | 
					import { Profile } from './profile';
 | 
				
			||||||
 | 
					import { Share } from './share';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const messages = defineMessages({
 | 
					const messages = defineMessages({
 | 
				
			||||||
  template: { id: 'onboarding.compose.template', defaultMessage: 'Hello #Mastodon!' },
 | 
					  template: { id: 'onboarding.compose.template', defaultMessage: 'Hello #Mastodon!' },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = () => {
 | 
					const Onboarding = () => {
 | 
				
			||||||
  const getAccount = makeGetAccount();
 | 
					  const account = useAppSelector(state => state.getIn(['accounts', me]));
 | 
				
			||||||
 | 
					  const dispatch = useDispatch();
 | 
				
			||||||
  return state => ({
 | 
					  const intl = useIntl();
 | 
				
			||||||
    account: getAccount(state, me),
 | 
					  const history = useHistory();
 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Onboarding extends ImmutablePureComponent {
 | 
					 | 
				
			||||||
  static propTypes = {
 | 
					 | 
				
			||||||
    dispatch: PropTypes.func.isRequired,
 | 
					 | 
				
			||||||
    account: ImmutablePropTypes.record,
 | 
					 | 
				
			||||||
    ...WithRouterPropTypes,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  state = {
 | 
					 | 
				
			||||||
    step: null,
 | 
					 | 
				
			||||||
    profileClicked: false,
 | 
					 | 
				
			||||||
    shareClicked: false,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleClose = () => {
 | 
					 | 
				
			||||||
    const { dispatch, history } = this.props;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dispatch(closeOnboarding());
 | 
					 | 
				
			||||||
    history.push('/home');
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleProfileClick = () => {
 | 
					 | 
				
			||||||
    this.setState({ profileClicked: true });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleFollowClick = () => {
 | 
					 | 
				
			||||||
    this.setState({ step: 'follows' });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleComposeClick = () => {
 | 
					 | 
				
			||||||
    const { dispatch, intl, history } = this.props;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleComposeClick = useCallback(() => {
 | 
				
			||||||
    dispatch(focusCompose(history, intl.formatMessage(messages.template)));
 | 
					    dispatch(focusCompose(history, intl.formatMessage(messages.template)));
 | 
				
			||||||
  };
 | 
					  }, [dispatch, intl, history]);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  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 () {
 | 
					 | 
				
			||||||
    const { account } = this.props;
 | 
					 | 
				
			||||||
    const { step, shareClicked } = this.state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    switch(step) {
 | 
					 | 
				
			||||||
    case 'follows':
 | 
					 | 
				
			||||||
      return <Follows onBack={this.handleBackClick} />;
 | 
					 | 
				
			||||||
    case 'share':
 | 
					 | 
				
			||||||
      return <Share onBack={this.handleBackClick} />;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Column>
 | 
					    <Column>
 | 
				
			||||||
 | 
					      <Switch>
 | 
				
			||||||
 | 
					        <Route path='/start' exact>
 | 
				
			||||||
          <div className='scrollable privacy-policy'>
 | 
					          <div className='scrollable privacy-policy'>
 | 
				
			||||||
            <div className='column-title'>
 | 
					            <div className='column-title'>
 | 
				
			||||||
              <img src={illustration} alt='' className='onboarding__illustration' />
 | 
					              <img src={illustration} alt='' className='onboarding__illustration' />
 | 
				
			||||||
| 
						 | 
					@ -120,10 +53,10 @@ class Onboarding extends ImmutablePureComponent {
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div className='onboarding__steps'>
 | 
					            <div className='onboarding__steps'>
 | 
				
			||||||
            <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 to='/start/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 to='/start/follows' completed={(account.get('following_count') * 1) >= 1} 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={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!' />} />
 | 
					              <Step to='/start/share' 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!' />} />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <p className='onboarding__lead'><FormattedMessage id='onboarding.start.skip' defaultMessage="Don't need help getting started?" /></p>
 | 
					            <p className='onboarding__lead'><FormattedMessage id='onboarding.start.skip' defaultMessage="Don't need help getting started?" /></p>
 | 
				
			||||||
| 
						 | 
					@ -140,14 +73,18 @@ class Onboarding extends ImmutablePureComponent {
 | 
				
			||||||
              </Link>
 | 
					              </Link>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					        </Route>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Route path='/start/profile' component={Profile} />
 | 
				
			||||||
 | 
					        <Route path='/start/follows' component={Follows} />
 | 
				
			||||||
 | 
					        <Route path='/start/share' component={Share} />
 | 
				
			||||||
 | 
					      </Switch>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Helmet>
 | 
					      <Helmet>
 | 
				
			||||||
        <meta name='robots' content='noindex' />
 | 
					        <meta name='robots' content='noindex' />
 | 
				
			||||||
      </Helmet>
 | 
					      </Helmet>
 | 
				
			||||||
    </Column>
 | 
					    </Column>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  }
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					export default Onboarding;
 | 
				
			||||||
 | 
					 | 
				
			||||||
export default withRouter(connect(mapStateToProps)(injectIntl(Onboarding)));
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,162 @@
 | 
				
			||||||
 | 
					import { useState, useMemo, useCallback, createRef } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import classNames from 'classnames';
 | 
				
			||||||
 | 
					import { useHistory } from 'react-router-dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useDispatch } from 'react-redux';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { ReactComponent as AddPhotoAlternateIcon } from '@material-symbols/svg-600/outlined/add_photo_alternate.svg';
 | 
				
			||||||
 | 
					import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
 | 
				
			||||||
 | 
					import Toggle from 'react-toggle';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { updateAccount } from 'flavours/glitch/actions/accounts';
 | 
				
			||||||
 | 
					import { Button } from 'flavours/glitch/components/button';
 | 
				
			||||||
 | 
					import { ColumnBackButton } from 'flavours/glitch/components/column_back_button';
 | 
				
			||||||
 | 
					import { Icon } from 'flavours/glitch/components/icon';
 | 
				
			||||||
 | 
					import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
 | 
				
			||||||
 | 
					import { me } from 'flavours/glitch/initial_state';
 | 
				
			||||||
 | 
					import { useAppSelector } from 'flavours/glitch/store';
 | 
				
			||||||
 | 
					import { unescapeHTML } from 'flavours/glitch/utils/html';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const messages = defineMessages({
 | 
				
			||||||
 | 
					  uploadHeader: { id: 'onboarding.profile.upload_header', defaultMessage: 'Upload profile header' },
 | 
				
			||||||
 | 
					  uploadAvatar: { id: 'onboarding.profile.upload_avatar', defaultMessage: 'Upload profile picture' },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const nullIfMissing = path => path.endsWith('missing.png') ? null : path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Profile = () => {
 | 
				
			||||||
 | 
					  const account = useAppSelector(state => state.getIn(['accounts', me]));
 | 
				
			||||||
 | 
					  const [displayName, setDisplayName] = useState(account.get('display_name'));
 | 
				
			||||||
 | 
					  const [note, setNote] = useState(unescapeHTML(account.get('note')));
 | 
				
			||||||
 | 
					  const [avatar, setAvatar] = useState(null);
 | 
				
			||||||
 | 
					  const [header, setHeader] = useState(null);
 | 
				
			||||||
 | 
					  const [discoverable, setDiscoverable] = useState(account.get('discoverable'));
 | 
				
			||||||
 | 
					  const [isSaving, setIsSaving] = useState(false);
 | 
				
			||||||
 | 
					  const [errors, setErrors] = useState();
 | 
				
			||||||
 | 
					  const avatarFileRef = createRef();
 | 
				
			||||||
 | 
					  const headerFileRef = createRef();
 | 
				
			||||||
 | 
					  const dispatch = useDispatch();
 | 
				
			||||||
 | 
					  const intl = useIntl();
 | 
				
			||||||
 | 
					  const history = useHistory();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleDisplayNameChange = useCallback(e => {
 | 
				
			||||||
 | 
					    setDisplayName(e.target.value);
 | 
				
			||||||
 | 
					  }, [setDisplayName]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleNoteChange = useCallback(e => {
 | 
				
			||||||
 | 
					    setNote(e.target.value);
 | 
				
			||||||
 | 
					  }, [setNote]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleDiscoverableChange = useCallback(e => {
 | 
				
			||||||
 | 
					    setDiscoverable(e.target.checked);
 | 
				
			||||||
 | 
					  }, [setDiscoverable]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleAvatarChange = useCallback(e => {
 | 
				
			||||||
 | 
					    setAvatar(e.target?.files?.[0]);
 | 
				
			||||||
 | 
					  }, [setAvatar]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleHeaderChange = useCallback(e => {
 | 
				
			||||||
 | 
					    setHeader(e.target?.files?.[0]);
 | 
				
			||||||
 | 
					  }, [setHeader]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const avatarPreview = useMemo(() => avatar ? URL.createObjectURL(avatar) : nullIfMissing(account.get('avatar')), [avatar, account]);
 | 
				
			||||||
 | 
					  const headerPreview = useMemo(() => header ? URL.createObjectURL(header) : nullIfMissing(account.get('header')), [header, account]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleSubmit = useCallback(() => {
 | 
				
			||||||
 | 
					    setIsSaving(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dispatch(updateAccount({
 | 
				
			||||||
 | 
					      displayName,
 | 
				
			||||||
 | 
					      note,
 | 
				
			||||||
 | 
					      avatar,
 | 
				
			||||||
 | 
					      header,
 | 
				
			||||||
 | 
					      discoverable,
 | 
				
			||||||
 | 
					      indexable: discoverable,
 | 
				
			||||||
 | 
					    })).then(() => history.push('/start/follows')).catch(err => {
 | 
				
			||||||
 | 
					      setIsSaving(false);
 | 
				
			||||||
 | 
					      setErrors(err.response.data.details);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }, [dispatch, displayName, note, avatar, header, discoverable, history]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <ColumnBackButton />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <div className='scrollable privacy-policy'>
 | 
				
			||||||
 | 
					        <div className='column-title'>
 | 
				
			||||||
 | 
					          <h3><FormattedMessage id='onboarding.profile.title' defaultMessage='Profile setup' /></h3>
 | 
				
			||||||
 | 
					          <p><FormattedMessage id='onboarding.profile.lead' defaultMessage='You can always complete this later in the settings, where even more customization options are available.' /></p>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='simple_form'>
 | 
				
			||||||
 | 
					          <div className='onboarding__profile'>
 | 
				
			||||||
 | 
					            <label className={classNames('app-form__header-input', { selected: !!headerPreview, invalid: !!errors?.header })} title={intl.formatMessage(messages.uploadHeader)}>
 | 
				
			||||||
 | 
					              <input
 | 
				
			||||||
 | 
					                type='file'
 | 
				
			||||||
 | 
					                hidden
 | 
				
			||||||
 | 
					                ref={headerFileRef}
 | 
				
			||||||
 | 
					                accept='image/*'
 | 
				
			||||||
 | 
					                onChange={handleHeaderChange}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              {headerPreview && <img src={headerPreview} alt='' />}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              <Icon icon={headerPreview ? EditIcon : AddPhotoAlternateIcon} />
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <label className={classNames('app-form__avatar-input', { selected: !!avatarPreview, invalid: !!errors?.avatar })} title={intl.formatMessage(messages.uploadAvatar)}>
 | 
				
			||||||
 | 
					              <input
 | 
				
			||||||
 | 
					                type='file'
 | 
				
			||||||
 | 
					                hidden
 | 
				
			||||||
 | 
					                ref={avatarFileRef}
 | 
				
			||||||
 | 
					                accept='image/*'
 | 
				
			||||||
 | 
					                onChange={handleAvatarChange}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              {avatarPreview && <img src={avatarPreview} alt='' />}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              <Icon icon={avatarPreview ? EditIcon : AddPhotoAlternateIcon} />
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div className={classNames('input with_block_label', { field_with_errors: !!errors?.display_name })}>
 | 
				
			||||||
 | 
					            <label htmlFor='display_name'><FormattedMessage id='onboarding.profile.display_name' defaultMessage='Display name' /></label>
 | 
				
			||||||
 | 
					            <span className='hint'><FormattedMessage id='onboarding.profile.display_name_hint' defaultMessage='Your full name or your fun name…' /></span>
 | 
				
			||||||
 | 
					            <div className='label_input'>
 | 
				
			||||||
 | 
					              <input id='display_name' type='text' value={displayName} onChange={handleDisplayNameChange} maxLength={30} />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div className={classNames('input with_block_label', { field_with_errors: !!errors?.note })}>
 | 
				
			||||||
 | 
					            <label htmlFor='note'><FormattedMessage id='onboarding.profile.note' defaultMessage='Bio' /></label>
 | 
				
			||||||
 | 
					            <span className='hint'><FormattedMessage id='onboarding.profile.note_hint' defaultMessage='You can @mention other people or #hashtags…' /></span>
 | 
				
			||||||
 | 
					            <div className='label_input'>
 | 
				
			||||||
 | 
					              <textarea id='note' value={note} onChange={handleNoteChange} maxLength={500} />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <label className='app-form__toggle'>
 | 
				
			||||||
 | 
					            <div className='app-form__toggle__label'>
 | 
				
			||||||
 | 
					              <strong><FormattedMessage id='onboarding.profile.discoverable' defaultMessage='Make my profile discoverable' /></strong> <span className='recommended'><FormattedMessage id='recommended' defaultMessage='Recommended' /></span>
 | 
				
			||||||
 | 
					              <span className='hint'><FormattedMessage id='onboarding.profile.discoverable_hint' defaultMessage='When you opt in to discoverability on Mastodon, your posts may appear in search results and trending, and your profile may be suggested to people with similar interests to you.' /></span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div className='app-form__toggle__toggle'>
 | 
				
			||||||
 | 
					              <div>
 | 
				
			||||||
 | 
					                <Toggle checked={discoverable} onChange={handleDiscoverableChange} />
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </label>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='onboarding__footer'>
 | 
				
			||||||
 | 
					          <Button block onClick={handleSubmit} disabled={isSaving}>{isSaving ? <LoadingIndicator /> : <FormattedMessage id='onboarding.profile.save_and_continue' defaultMessage='Save and continue' />}</Button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -1,31 +1,25 @@
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
import { PureComponent } from 'react';
 | 
					import { PureComponent } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 | 
					import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import classNames from 'classnames';
 | 
					import classNames from 'classnames';
 | 
				
			||||||
import { Link } from 'react-router-dom';
 | 
					import { Link } from 'react-router-dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
					 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.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 ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg';
 | 
				
			||||||
import SwipeableViews from 'react-swipeable-views';
 | 
					import SwipeableViews from 'react-swipeable-views';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Column from 'flavours/glitch/components/column';
 | 
					 | 
				
			||||||
import { ColumnBackButton } from 'flavours/glitch/components/column_back_button';
 | 
					import { ColumnBackButton } from 'flavours/glitch/components/column_back_button';
 | 
				
			||||||
import { Icon }  from 'flavours/glitch/components/icon';
 | 
					import { Icon }  from 'flavours/glitch/components/icon';
 | 
				
			||||||
import { me, domain } from 'flavours/glitch/initial_state';
 | 
					import { me, domain } from 'flavours/glitch/initial_state';
 | 
				
			||||||
 | 
					import { useAppSelector } from 'flavours/glitch/store';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const messages = defineMessages({
 | 
					const messages = defineMessages({
 | 
				
			||||||
  shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on #Mastodon! Come follow me at {url}' },
 | 
					  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 {
 | 
					class CopyPasteText extends PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static propTypes = {
 | 
					  static propTypes = {
 | 
				
			||||||
| 
						 | 
					@ -141,22 +135,14 @@ class TipCarousel extends PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Share extends PureComponent {
 | 
					export const Share = () => {
 | 
				
			||||||
 | 
					  const account = useAppSelector(state => state.getIn(['accounts', me]));
 | 
				
			||||||
  static propTypes = {
 | 
					  const intl = useIntl();
 | 
				
			||||||
    onBack: PropTypes.func,
 | 
					 | 
				
			||||||
    account: ImmutablePropTypes.record,
 | 
					 | 
				
			||||||
    intl: PropTypes.object,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  render () {
 | 
					 | 
				
			||||||
    const { onBack, account, intl } = this.props;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const url = (new URL(`/@${account.get('username')}`, document.baseURI)).href;
 | 
					  const url = (new URL(`/@${account.get('username')}`, document.baseURI)).href;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
      <Column>
 | 
					    <>
 | 
				
			||||||
        <ColumnBackButton onClick={onBack} />
 | 
					      <ColumnBackButton />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div className='scrollable privacy-policy'>
 | 
					      <div className='scrollable privacy-policy'>
 | 
				
			||||||
        <div className='column-title'>
 | 
					        <div className='column-title'>
 | 
				
			||||||
| 
						 | 
					@ -187,13 +173,9 @@ class Share extends PureComponent {
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className='onboarding__footer'>
 | 
					        <div className='onboarding__footer'>
 | 
				
			||||||
            <button className='link-button' onClick={onBack}><FormattedMessage id='onboarding.action.back' defaultMessage='Take me back' /></button>
 | 
					          <Link className='link-button' to='/start'><FormattedMessage id='onboarding.action.back' defaultMessage='Take me back' /></Link>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      </Column>
 | 
					    </>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  }
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default connect(mapStateToProps)(injectIntl(Share));
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -219,7 +219,7 @@ class SwitchingColumnsArea extends PureComponent {
 | 
				
			||||||
            <WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
 | 
					            <WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
 | 
				
			||||||
            <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
 | 
					            <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <WrappedRoute path='/start' exact component={Onboarding} content={children} />
 | 
					            <WrappedRoute path='/start' component={Onboarding} content={children} />
 | 
				
			||||||
            <WrappedRoute path='/directory' component={Directory} content={children} />
 | 
					            <WrappedRoute path='/directory' component={Directory} content={children} />
 | 
				
			||||||
            <WrappedRoute path={['/explore', '/search']} component={Explore} content={children} />
 | 
					            <WrappedRoute path={['/explore', '/search']} component={Explore} content={children} />
 | 
				
			||||||
            <WrappedRoute path={['/publish', '/statuses/new']} component={Compose} content={children} />
 | 
					            <WrappedRoute path={['/publish', '/statuses/new']} component={Compose} content={children} />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
 | 
					  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
 | 
				
			||||||
  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
 | 
					  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
 | 
				
			||||||
  "account.follows": "Follows",
 | 
					  "account.follows": "Follows",
 | 
				
			||||||
 | 
					  "account.follows_you": "Follows you",
 | 
				
			||||||
  "account.joined": "Joined {date}",
 | 
					  "account.joined": "Joined {date}",
 | 
				
			||||||
  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
 | 
					  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
 | 
				
			||||||
  "account.view_full_profile": "View full profile",
 | 
					  "account.view_full_profile": "View full profile",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,6 +67,7 @@ export const accountDefaultValues: AccountShape = {
 | 
				
			||||||
  bot: false,
 | 
					  bot: false,
 | 
				
			||||||
  created_at: '',
 | 
					  created_at: '',
 | 
				
			||||||
  discoverable: false,
 | 
					  discoverable: false,
 | 
				
			||||||
 | 
					  indexable: false,
 | 
				
			||||||
  display_name: '',
 | 
					  display_name: '',
 | 
				
			||||||
  display_name_html: '',
 | 
					  display_name_html: '',
 | 
				
			||||||
  emojis: List<CustomEmoji>(),
 | 
					  emojis: List<CustomEmoji>(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,13 +161,20 @@ body {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					a {
 | 
				
			||||||
 | 
					  &:focus {
 | 
				
			||||||
 | 
					    border-radius: 4px;
 | 
				
			||||||
 | 
					    outline: $ui-button-focus-outline;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &:focus:not(:focus-visible) {
 | 
				
			||||||
 | 
					    outline: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
button {
 | 
					button {
 | 
				
			||||||
  font-family: inherit;
 | 
					  font-family: inherit;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  &:focus {
 | 
					 | 
				
			||||||
    outline: none;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.app-holder {
 | 
					.app-holder {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -245,6 +245,8 @@ $ui-header-height: 55px;
 | 
				
			||||||
  text-decoration: none;
 | 
					  text-decoration: none;
 | 
				
			||||||
  overflow: hidden;
 | 
					  overflow: hidden;
 | 
				
			||||||
  white-space: nowrap;
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					  border: 0;
 | 
				
			||||||
 | 
					  border-left: 4px solid transparent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:hover,
 | 
					  &:hover,
 | 
				
			||||||
  &:focus,
 | 
					  &:focus,
 | 
				
			||||||
| 
						 | 
					@ -256,6 +258,11 @@ $ui-header-height: 55px;
 | 
				
			||||||
    outline: 0;
 | 
					    outline: 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &:focus-visible {
 | 
				
			||||||
 | 
					    border-color: $ui-button-focus-outline-color;
 | 
				
			||||||
 | 
					    border-radius: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &--transparent {
 | 
					  &--transparent {
 | 
				
			||||||
    background: transparent;
 | 
					    background: transparent;
 | 
				
			||||||
    color: $ui-secondary-color;
 | 
					    color: $ui-secondary-color;
 | 
				
			||||||
| 
						 | 
					@ -339,7 +346,6 @@ $ui-header-height: 55px;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  z-index: 2;
 | 
					  z-index: 2;
 | 
				
			||||||
  outline: 0;
 | 
					  outline: 0;
 | 
				
			||||||
  overflow: hidden;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & > button {
 | 
					  & > button {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
| 
						 | 
					@ -356,6 +362,10 @@ $ui-header-height: 55px;
 | 
				
			||||||
    overflow: hidden;
 | 
					    overflow: hidden;
 | 
				
			||||||
    white-space: nowrap;
 | 
					    white-space: nowrap;
 | 
				
			||||||
    flex: 1;
 | 
					    flex: 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:focus-visible {
 | 
				
			||||||
 | 
					      outline: $ui-button-icon-focus-outline;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & > .column-header__back-button {
 | 
					  & > .column-header__back-button {
 | 
				
			||||||
| 
						 | 
					@ -420,10 +430,18 @@ $ui-header-height: 55px;
 | 
				
			||||||
  font-size: 16px;
 | 
					  font-size: 16px;
 | 
				
			||||||
  padding: 0 15px;
 | 
					  padding: 0 15px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &:last-child {
 | 
				
			||||||
 | 
					    border-start-end-radius: 4px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    color: lighten($darker-text-color, 7%);
 | 
					    color: lighten($darker-text-color, 7%);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &:focus-visible {
 | 
				
			||||||
 | 
					    outline: $ui-button-focus-outline;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.active {
 | 
					  &.active {
 | 
				
			||||||
    color: $primary-text-color;
 | 
					    color: $primary-text-color;
 | 
				
			||||||
    background: lighten($ui-base-color, 8%);
 | 
					    background: lighten($ui-base-color, 8%);
 | 
				
			||||||
| 
						 | 
					@ -434,11 +452,6 @@ $ui-header-height: 55px;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // glitch - added focus ring for keyboard navigation
 | 
					 | 
				
			||||||
  &:focus {
 | 
					 | 
				
			||||||
    text-shadow: 0 0 4px darken($ui-highlight-color, 5%);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  &:disabled {
 | 
					  &:disabled {
 | 
				
			||||||
    color: $dark-text-color;
 | 
					    color: $dark-text-color;
 | 
				
			||||||
    cursor: default;
 | 
					    cursor: default;
 | 
				
			||||||
| 
						 | 
					@ -898,7 +911,7 @@ $ui-header-height: 55px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.column-title {
 | 
					.column-title {
 | 
				
			||||||
  text-align: center;
 | 
					  text-align: center;
 | 
				
			||||||
  padding-bottom: 40px;
 | 
					  padding-bottom: 32px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  h3 {
 | 
					  h3 {
 | 
				
			||||||
    font-size: 24px;
 | 
					    font-size: 24px;
 | 
				
			||||||
| 
						 | 
					@ -1083,58 +1096,6 @@ $ui-header-height: 55px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.onboarding__progress-indicator {
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
  margin-bottom: 30px;
 | 
					 | 
				
			||||||
  position: sticky;
 | 
					 | 
				
			||||||
  background: $ui-base-color;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @media screen and (width >= 600) {
 | 
					 | 
				
			||||||
    padding: 0 40px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  &__line {
 | 
					 | 
				
			||||||
    height: 4px;
 | 
					 | 
				
			||||||
    flex: 1 1 auto;
 | 
					 | 
				
			||||||
    background: lighten($ui-base-color, 4%);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  &__step {
 | 
					 | 
				
			||||||
    flex: 0 0 auto;
 | 
					 | 
				
			||||||
    width: 30px;
 | 
					 | 
				
			||||||
    height: 30px;
 | 
					 | 
				
			||||||
    background: lighten($ui-base-color, 4%);
 | 
					 | 
				
			||||||
    border-radius: 50%;
 | 
					 | 
				
			||||||
    color: $primary-text-color;
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    align-items: center;
 | 
					 | 
				
			||||||
    justify-content: center;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    svg {
 | 
					 | 
				
			||||||
      width: 15px;
 | 
					 | 
				
			||||||
      height: auto;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    &.active {
 | 
					 | 
				
			||||||
      background: $valid-value-color;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  &__step.active,
 | 
					 | 
				
			||||||
  &__line.active {
 | 
					 | 
				
			||||||
    background: $valid-value-color;
 | 
					 | 
				
			||||||
    background-image: linear-gradient(
 | 
					 | 
				
			||||||
      90deg,
 | 
					 | 
				
			||||||
      $valid-value-color,
 | 
					 | 
				
			||||||
      lighten($valid-value-color, 8%),
 | 
					 | 
				
			||||||
      $valid-value-color
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    background-size: 200px 100%;
 | 
					 | 
				
			||||||
    animation: skeleton 1.2s ease-in-out infinite;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.follow-recommendations {
 | 
					.follow-recommendations {
 | 
				
			||||||
  background: darken($ui-base-color, 4%);
 | 
					  background: darken($ui-base-color, 4%);
 | 
				
			||||||
  border-radius: 8px;
 | 
					  border-radius: 8px;
 | 
				
			||||||
| 
						 | 
					@ -1211,6 +1172,28 @@ $ui-header-height: 55px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.onboarding__profile {
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  margin-bottom: 40px + 20px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .app-form__avatar-input {
 | 
				
			||||||
 | 
					    border: 2px solid $ui-base-color;
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    inset-inline-start: -2px;
 | 
				
			||||||
 | 
					    bottom: -40px;
 | 
				
			||||||
 | 
					    z-index: 2;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .app-form__header-input {
 | 
				
			||||||
 | 
					    margin: 0 -20px;
 | 
				
			||||||
 | 
					    border-radius: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    img {
 | 
				
			||||||
 | 
					      border-radius: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.compose-form__highlightable {
 | 
					.compose-form__highlightable {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  flex-direction: column;
 | 
					  flex-direction: column;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -526,7 +526,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.privacy-dropdown__dropdown {
 | 
					.privacy-dropdown__dropdown {
 | 
				
			||||||
  border-radius: 4px;
 | 
					  border-radius: 4px;
 | 
				
			||||||
  box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
 | 
					  box-shadow: var(--dropdown-shadow);
 | 
				
			||||||
  background: $simple-background-color;
 | 
					  background: $simple-background-color;
 | 
				
			||||||
  overflow: hidden;
 | 
					  overflow: hidden;
 | 
				
			||||||
  transform-origin: 50% 0;
 | 
					  transform-origin: 50% 0;
 | 
				
			||||||
| 
						 | 
					@ -581,7 +581,6 @@
 | 
				
			||||||
  column-gap: 5px;
 | 
					  column-gap: 5px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .compose-form__publish-button-wrapper {
 | 
					  .compose-form__publish-button-wrapper {
 | 
				
			||||||
    overflow: hidden;
 | 
					 | 
				
			||||||
    padding-top: 10px;
 | 
					    padding-top: 10px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    button {
 | 
					    button {
 | 
				
			||||||
| 
						 | 
					@ -604,7 +603,7 @@
 | 
				
			||||||
.language-dropdown {
 | 
					.language-dropdown {
 | 
				
			||||||
  &__dropdown {
 | 
					  &__dropdown {
 | 
				
			||||||
    background: $simple-background-color;
 | 
					    background: $simple-background-color;
 | 
				
			||||||
    box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
 | 
					    box-shadow: var(--dropdown-shadow);
 | 
				
			||||||
    border-radius: 4px;
 | 
					    border-radius: 4px;
 | 
				
			||||||
    overflow: hidden;
 | 
					    overflow: hidden;
 | 
				
			||||||
    z-index: 2;
 | 
					    z-index: 2;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,9 +129,10 @@
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.navigation-bar__profile {
 | 
					.navigation-bar__profile {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
  flex: 1 1 auto;
 | 
					  flex: 1 1 auto;
 | 
				
			||||||
  margin-inline-start: 8px;
 | 
					  margin-inline-start: 8px;
 | 
				
			||||||
  overflow: hidden;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.drawer--results {
 | 
					.drawer--results {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
.emoji-picker-dropdown__menu {
 | 
					.emoji-picker-dropdown__menu {
 | 
				
			||||||
  background: $simple-background-color;
 | 
					  background: $simple-background-color;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
 | 
					  box-shadow: var(--dropdown-shadow);
 | 
				
			||||||
  border-radius: 4px;
 | 
					  border-radius: 4px;
 | 
				
			||||||
  margin-top: 5px;
 | 
					  margin-top: 5px;
 | 
				
			||||||
  z-index: 2;
 | 
					  z-index: 2;
 | 
				
			||||||
| 
						 | 
					@ -79,11 +79,6 @@
 | 
				
			||||||
  outline: 0;
 | 
					  outline: 0;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:active,
 | 
					 | 
				
			||||||
  &:focus {
 | 
					 | 
				
			||||||
    outline: 0 !important;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  img {
 | 
					  img {
 | 
				
			||||||
    filter: grayscale(100%);
 | 
					    filter: grayscale(100%);
 | 
				
			||||||
    opacity: 0.8;
 | 
					    opacity: 0.8;
 | 
				
			||||||
| 
						 | 
					@ -99,6 +94,13 @@
 | 
				
			||||||
    img {
 | 
					    img {
 | 
				
			||||||
      opacity: 1;
 | 
					      opacity: 1;
 | 
				
			||||||
      filter: none;
 | 
					      filter: none;
 | 
				
			||||||
 | 
					      border-radius: 100%;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &:focus-visible {
 | 
				
			||||||
 | 
					    img {
 | 
				
			||||||
 | 
					      outline: $ui-button-icon-focus-outline;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,6 +77,10 @@
 | 
				
			||||||
    background-color: $ui-button-focus-background-color;
 | 
					    background-color: $ui-button-focus-background-color;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &:focus {
 | 
				
			||||||
 | 
					    outline: $ui-button-icon-focus-outline;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &--destructive {
 | 
					  &--destructive {
 | 
				
			||||||
    &:active,
 | 
					    &:active,
 | 
				
			||||||
    &:focus,
 | 
					    &:focus,
 | 
				
			||||||
| 
						 | 
					@ -200,12 +204,10 @@
 | 
				
			||||||
  &:focus {
 | 
					  &:focus {
 | 
				
			||||||
    color: lighten($action-button-color, 7%);
 | 
					    color: lighten($action-button-color, 7%);
 | 
				
			||||||
    background-color: rgba($action-button-color, 0.15);
 | 
					    background-color: rgba($action-button-color, 0.15);
 | 
				
			||||||
    transition: all 200ms ease-out;
 | 
					 | 
				
			||||||
    transition-property: background-color, color;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:focus {
 | 
					  &:focus-visible {
 | 
				
			||||||
    background-color: rgba($action-button-color, 0.3);
 | 
					    outline: $ui-button-icon-focus-outline;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.disabled {
 | 
					  &.disabled {
 | 
				
			||||||
| 
						 | 
					@ -227,16 +229,6 @@
 | 
				
			||||||
    transition: none;
 | 
					    transition: none;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &::-moz-focus-inner {
 | 
					 | 
				
			||||||
    border: 0;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  &::-moz-focus-inner,
 | 
					 | 
				
			||||||
  &:focus,
 | 
					 | 
				
			||||||
  &:active {
 | 
					 | 
				
			||||||
    outline: 0 !important;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  &.inverted {
 | 
					  &.inverted {
 | 
				
			||||||
    color: $lighter-text-color;
 | 
					    color: $lighter-text-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -247,23 +239,30 @@
 | 
				
			||||||
      background-color: rgba($lighter-text-color, 0.15);
 | 
					      background-color: rgba($lighter-text-color, 0.15);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &:focus {
 | 
					    &:focus-visible {
 | 
				
			||||||
      background-color: rgba($lighter-text-color, 0.3);
 | 
					      outline: $ui-button-icon-focus-outline;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &.disabled {
 | 
					    &.disabled {
 | 
				
			||||||
      color: lighten($lighter-text-color, 7%);
 | 
					      color: lighten($lighter-text-color, 7%);
 | 
				
			||||||
      background-color: transparent;
 | 
					      background-color: transparent;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.active {
 | 
					  &.active {
 | 
				
			||||||
    color: $highlight-text-color;
 | 
					    color: $highlight-text-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:hover,
 | 
				
			||||||
 | 
					    &:active,
 | 
				
			||||||
 | 
					    &:focus {
 | 
				
			||||||
 | 
					      color: $highlight-text-color;
 | 
				
			||||||
 | 
					      background-color: transparent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &.disabled {
 | 
					    &.disabled {
 | 
				
			||||||
      color: lighten($highlight-text-color, 13%);
 | 
					      color: lighten($highlight-text-color, 13%);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.overlayed {
 | 
					  &.overlayed {
 | 
				
			||||||
    box-sizing: content-box;
 | 
					    box-sizing: content-box;
 | 
				
			||||||
| 
						 | 
					@ -301,21 +300,16 @@
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  padding: 0 3px;
 | 
					  padding: 0 3px;
 | 
				
			||||||
  white-space: nowrap;
 | 
					  white-space: nowrap;
 | 
				
			||||||
  outline: 0;
 | 
					 | 
				
			||||||
  transition: all 100ms ease-in;
 | 
					 | 
				
			||||||
  transition-property: background-color, color;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:hover,
 | 
					  &:hover,
 | 
				
			||||||
  &:active,
 | 
					  &:active,
 | 
				
			||||||
  &:focus {
 | 
					  &:focus {
 | 
				
			||||||
    color: darken($lighter-text-color, 7%);
 | 
					    color: darken($lighter-text-color, 7%);
 | 
				
			||||||
    background-color: rgba($lighter-text-color, 0.15);
 | 
					    background-color: rgba($lighter-text-color, 0.15);
 | 
				
			||||||
    transition: all 200ms ease-out;
 | 
					 | 
				
			||||||
    transition-property: background-color, color;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:focus {
 | 
					  &:focus {
 | 
				
			||||||
    background-color: rgba($lighter-text-color, 0.3);
 | 
					    outline: $ui-button-icon-focus-outline;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.disabled {
 | 
					  &.disabled {
 | 
				
			||||||
| 
						 | 
					@ -326,16 +320,13 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.active {
 | 
					  &.active {
 | 
				
			||||||
    color: $highlight-text-color;
 | 
					    color: $highlight-text-color;
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &::-moz-focus-inner {
 | 
					    &:hover,
 | 
				
			||||||
    border: 0;
 | 
					    &:active,
 | 
				
			||||||
 | 
					    &:focus {
 | 
				
			||||||
 | 
					      color: $highlight-text-color;
 | 
				
			||||||
 | 
					      background-color: transparent;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  &::-moz-focus-inner,
 | 
					 | 
				
			||||||
  &:focus,
 | 
					 | 
				
			||||||
  &:active {
 | 
					 | 
				
			||||||
    outline: 0 !important;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -580,7 +571,7 @@ body > [data-popper-placement] {
 | 
				
			||||||
    font-size: inherit;
 | 
					    font-size: inherit;
 | 
				
			||||||
    line-height: inherit;
 | 
					    line-height: inherit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &:focus {
 | 
					    &:focus-visible {
 | 
				
			||||||
      outline: 1px dotted;
 | 
					      outline: 1px dotted;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -855,6 +846,7 @@ body > [data-popper-placement] {
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  background-color: transparent;
 | 
					  background-color: transparent;
 | 
				
			||||||
  border: 0;
 | 
					  border: 0;
 | 
				
			||||||
 | 
					  border-radius: 10px;
 | 
				
			||||||
  padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
  user-select: none;
 | 
					  user-select: none;
 | 
				
			||||||
  -webkit-tap-highlight-color: rgba($base-overlay-background, 0);
 | 
					  -webkit-tap-highlight-color: rgba($base-overlay-background, 0);
 | 
				
			||||||
| 
						 | 
					@ -879,81 +871,41 @@ body > [data-popper-placement] {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.react-toggle-track {
 | 
					.react-toggle-track {
 | 
				
			||||||
  width: 50px;
 | 
					  width: 32px;
 | 
				
			||||||
  height: 24px;
 | 
					  height: 20px;
 | 
				
			||||||
  padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
  border-radius: 30px;
 | 
					  border-radius: 10px;
 | 
				
			||||||
  background-color: $ui-base-color;
 | 
					  background-color: #626982;
 | 
				
			||||||
  transition: background-color 0.2s ease;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.react-toggle:is(:hover, :focus-within):not(.react-toggle--disabled)
 | 
					.react-toggle--focus {
 | 
				
			||||||
  .react-toggle-track {
 | 
					  outline: $ui-button-focus-outline;
 | 
				
			||||||
  background-color: darken($ui-base-color, 10%);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.react-toggle--checked .react-toggle-track {
 | 
					.react-toggle--checked .react-toggle-track {
 | 
				
			||||||
  background-color: darken($ui-highlight-color, 2%);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.react-toggle--checked:is(:hover, :focus-within):not(.react-toggle--disabled)
 | 
					 | 
				
			||||||
  .react-toggle-track {
 | 
					 | 
				
			||||||
  background-color: $ui-highlight-color;
 | 
					  background-color: $ui-highlight-color;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.react-toggle-track-check {
 | 
					.react-toggle-track-check,
 | 
				
			||||||
  position: absolute;
 | 
					 | 
				
			||||||
  width: 14px;
 | 
					 | 
				
			||||||
  height: 10px;
 | 
					 | 
				
			||||||
  top: 0;
 | 
					 | 
				
			||||||
  bottom: 0;
 | 
					 | 
				
			||||||
  margin-top: auto;
 | 
					 | 
				
			||||||
  margin-bottom: auto;
 | 
					 | 
				
			||||||
  line-height: 0;
 | 
					 | 
				
			||||||
  inset-inline-start: 8px;
 | 
					 | 
				
			||||||
  opacity: 0;
 | 
					 | 
				
			||||||
  transition: opacity 0.25s ease;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.react-toggle--checked .react-toggle-track-check {
 | 
					 | 
				
			||||||
  opacity: 1;
 | 
					 | 
				
			||||||
  transition: opacity 0.25s ease;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.react-toggle-track-x {
 | 
					.react-toggle-track-x {
 | 
				
			||||||
  position: absolute;
 | 
					  display: none;
 | 
				
			||||||
  width: 10px;
 | 
					 | 
				
			||||||
  height: 10px;
 | 
					 | 
				
			||||||
  top: 0;
 | 
					 | 
				
			||||||
  bottom: 0;
 | 
					 | 
				
			||||||
  margin-top: auto;
 | 
					 | 
				
			||||||
  margin-bottom: auto;
 | 
					 | 
				
			||||||
  line-height: 0;
 | 
					 | 
				
			||||||
  inset-inline-end: 10px;
 | 
					 | 
				
			||||||
  opacity: 1;
 | 
					 | 
				
			||||||
  transition: opacity 0.25s ease;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.react-toggle--checked .react-toggle-track-x {
 | 
					 | 
				
			||||||
  opacity: 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.react-toggle-thumb {
 | 
					.react-toggle-thumb {
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  top: 1px;
 | 
					  top: 2px;
 | 
				
			||||||
  inset-inline-start: 1px;
 | 
					  inset-inline-start: 2px;
 | 
				
			||||||
  width: 22px;
 | 
					  width: 16px;
 | 
				
			||||||
  height: 22px;
 | 
					  height: 16px;
 | 
				
			||||||
  border: 1px solid $ui-base-color;
 | 
					 | 
				
			||||||
  border-radius: 50%;
 | 
					  border-radius: 50%;
 | 
				
			||||||
  background-color: darken($simple-background-color, 2%);
 | 
					  background-color: $primary-text-color;
 | 
				
			||||||
  box-sizing: border-box;
 | 
					  box-sizing: border-box;
 | 
				
			||||||
  transition: all 0.25s ease;
 | 
					  transition: all 0.25s ease;
 | 
				
			||||||
  transition-property: border-color, left;
 | 
					  transition-property: border-color, left;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.react-toggle--checked .react-toggle-thumb {
 | 
					.react-toggle--checked .react-toggle-thumb {
 | 
				
			||||||
  inset-inline-start: 27px;
 | 
					  inset-inline-start: 32px - 16px - 2px;
 | 
				
			||||||
  border-color: $ui-highlight-color;
 | 
					  border-color: $ui-highlight-color;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1239,6 +1191,17 @@ body > [data-popper-placement] {
 | 
				
			||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.button .loading-indicator {
 | 
				
			||||||
 | 
					  position: static;
 | 
				
			||||||
 | 
					  transform: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .circular-progress {
 | 
				
			||||||
 | 
					    color: $primary-text-color;
 | 
				
			||||||
 | 
					    width: 22px;
 | 
				
			||||||
 | 
					    height: 22px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.circular-progress {
 | 
					.circular-progress {
 | 
				
			||||||
  color: lighten($ui-base-color, 26%);
 | 
					  color: lighten($ui-base-color, 26%);
 | 
				
			||||||
  animation: 1.4s linear 0s infinite normal none running simple-rotate;
 | 
					  animation: 1.4s linear 0s infinite normal none running simple-rotate;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -697,12 +697,14 @@
 | 
				
			||||||
  &__toggle {
 | 
					  &__toggle {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
    margin-bottom: 10px;
 | 
					    margin-bottom: 16px;
 | 
				
			||||||
 | 
					    gap: 8px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    & > span {
 | 
					    & > span {
 | 
				
			||||||
      font-size: 17px;
 | 
					      display: block;
 | 
				
			||||||
 | 
					      font-size: 14px;
 | 
				
			||||||
      font-weight: 500;
 | 
					      font-weight: 500;
 | 
				
			||||||
      margin-inline-start: 10px;
 | 
					      line-height: 20px;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ code {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .input {
 | 
					  .input {
 | 
				
			||||||
    margin-bottom: 15px;
 | 
					    margin-bottom: 16px;
 | 
				
			||||||
    overflow: hidden;
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &.hidden {
 | 
					    &.hidden {
 | 
				
			||||||
| 
						 | 
					@ -267,12 +267,13 @@ code {
 | 
				
			||||||
      font-size: 14px;
 | 
					      font-size: 14px;
 | 
				
			||||||
      color: $primary-text-color;
 | 
					      color: $primary-text-color;
 | 
				
			||||||
      display: block;
 | 
					      display: block;
 | 
				
			||||||
      font-weight: 500;
 | 
					      font-weight: 600;
 | 
				
			||||||
      padding-top: 5px;
 | 
					      line-height: 20px;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .hint {
 | 
					    .hint {
 | 
				
			||||||
      margin-bottom: 15px;
 | 
					      line-height: 16px;
 | 
				
			||||||
 | 
					      margin-bottom: 12px;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ul {
 | 
					    ul {
 | 
				
			||||||
| 
						 | 
					@ -428,7 +429,8 @@ code {
 | 
				
			||||||
  input[type='datetime-local'],
 | 
					  input[type='datetime-local'],
 | 
				
			||||||
  textarea {
 | 
					  textarea {
 | 
				
			||||||
    box-sizing: border-box;
 | 
					    box-sizing: border-box;
 | 
				
			||||||
    font-size: 16px;
 | 
					    font-size: 14px;
 | 
				
			||||||
 | 
					    line-height: 20px;
 | 
				
			||||||
    color: $primary-text-color;
 | 
					    color: $primary-text-color;
 | 
				
			||||||
    display: block;
 | 
					    display: block;
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
| 
						 | 
					@ -436,9 +438,9 @@ code {
 | 
				
			||||||
    font-family: inherit;
 | 
					    font-family: inherit;
 | 
				
			||||||
    resize: vertical;
 | 
					    resize: vertical;
 | 
				
			||||||
    background: darken($ui-base-color, 10%);
 | 
					    background: darken($ui-base-color, 10%);
 | 
				
			||||||
    border: 1px solid darken($ui-base-color, 14%);
 | 
					    border: 1px solid darken($ui-base-color, 10%);
 | 
				
			||||||
    border-radius: 4px;
 | 
					    border-radius: 8px;
 | 
				
			||||||
    padding: 10px;
 | 
					    padding: 10px 16px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &::placeholder {
 | 
					    &::placeholder {
 | 
				
			||||||
      color: lighten($darker-text-color, 4%);
 | 
					      color: lighten($darker-text-color, 4%);
 | 
				
			||||||
| 
						 | 
					@ -452,14 +454,13 @@ code {
 | 
				
			||||||
      border-color: $valid-value-color;
 | 
					      border-color: $valid-value-color;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &:hover {
 | 
					 | 
				
			||||||
      border-color: darken($ui-base-color, 20%);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    &:active,
 | 
					    &:active,
 | 
				
			||||||
    &:focus {
 | 
					    &:focus {
 | 
				
			||||||
      border-color: $highlight-text-color;
 | 
					      border-color: $highlight-text-color;
 | 
				
			||||||
      background: darken($ui-base-color, 8%);
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @media screen and (width <= 600px) {
 | 
				
			||||||
 | 
					      font-size: 16px;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -525,12 +526,11 @@ code {
 | 
				
			||||||
    border-radius: 4px;
 | 
					    border-radius: 4px;
 | 
				
			||||||
    background: $ui-button-background-color;
 | 
					    background: $ui-button-background-color;
 | 
				
			||||||
    color: $ui-button-color;
 | 
					    color: $ui-button-color;
 | 
				
			||||||
    font-size: 18px;
 | 
					    font-size: 15px;
 | 
				
			||||||
    line-height: inherit;
 | 
					    line-height: 22px;
 | 
				
			||||||
    height: auto;
 | 
					    height: auto;
 | 
				
			||||||
    padding: 10px;
 | 
					    padding: 7px 18px;
 | 
				
			||||||
    text-decoration: none;
 | 
					    text-decoration: none;
 | 
				
			||||||
    text-transform: uppercase;
 | 
					 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
    box-sizing: border-box;
 | 
					    box-sizing: border-box;
 | 
				
			||||||
    cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
| 
						 | 
					@ -1222,3 +1222,115 @@ code {
 | 
				
			||||||
    background: $highlight-text-color;
 | 
					    background: $highlight-text-color;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.app-form {
 | 
				
			||||||
 | 
					  &__avatar-input,
 | 
				
			||||||
 | 
					  &__header-input {
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					    border-radius: 8px;
 | 
				
			||||||
 | 
					    background: var(--dropdown-background-color);
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    img {
 | 
				
			||||||
 | 
					      position: absolute;
 | 
				
			||||||
 | 
					      width: 100%;
 | 
				
			||||||
 | 
					      height: 100%;
 | 
				
			||||||
 | 
					      object-fit: cover;
 | 
				
			||||||
 | 
					      border-radius: 8px;
 | 
				
			||||||
 | 
					      z-index: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .icon {
 | 
				
			||||||
 | 
					      position: absolute;
 | 
				
			||||||
 | 
					      inset-inline-start: 50%;
 | 
				
			||||||
 | 
					      top: 50%;
 | 
				
			||||||
 | 
					      transform: translate(-50%, -50%);
 | 
				
			||||||
 | 
					      color: $darker-text-color;
 | 
				
			||||||
 | 
					      z-index: 3;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.selected .icon {
 | 
				
			||||||
 | 
					      color: $primary-text-color;
 | 
				
			||||||
 | 
					      transform: none;
 | 
				
			||||||
 | 
					      inset-inline-start: auto;
 | 
				
			||||||
 | 
					      inset-inline-end: 8px;
 | 
				
			||||||
 | 
					      top: auto;
 | 
				
			||||||
 | 
					      bottom: 8px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.invalid img {
 | 
				
			||||||
 | 
					      outline: 1px solid $error-value-color;
 | 
				
			||||||
 | 
					      outline-offset: -1px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.invalid::before {
 | 
				
			||||||
 | 
					      display: block;
 | 
				
			||||||
 | 
					      content: '';
 | 
				
			||||||
 | 
					      width: 100%;
 | 
				
			||||||
 | 
					      height: 100%;
 | 
				
			||||||
 | 
					      position: absolute;
 | 
				
			||||||
 | 
					      background: rgba($error-value-color, 0.25);
 | 
				
			||||||
 | 
					      z-index: 2;
 | 
				
			||||||
 | 
					      border-radius: 8px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:hover {
 | 
				
			||||||
 | 
					      background-color: var(--dropdown-border-color);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__avatar-input {
 | 
				
			||||||
 | 
					    width: 80px;
 | 
				
			||||||
 | 
					    height: 80px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__header-input {
 | 
				
			||||||
 | 
					    aspect-ratio: 580/193;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__toggle {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    gap: 16px;
 | 
				
			||||||
 | 
					    color: $darker-text-color;
 | 
				
			||||||
 | 
					    font-size: 14px;
 | 
				
			||||||
 | 
					    line-height: 20px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .icon {
 | 
				
			||||||
 | 
					      flex: 0 0 auto;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .icon {
 | 
				
			||||||
 | 
					      width: 24px;
 | 
				
			||||||
 | 
					      height: 24px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &__label {
 | 
				
			||||||
 | 
					      flex: 1 1 auto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      strong {
 | 
				
			||||||
 | 
					        color: $primary-text-color;
 | 
				
			||||||
 | 
					        font-weight: 600;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .recommended {
 | 
				
			||||||
 | 
					        position: absolute;
 | 
				
			||||||
 | 
					        margin: 0 4px;
 | 
				
			||||||
 | 
					        margin-top: -2px;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &__toggle {
 | 
				
			||||||
 | 
					      flex: 0 0 auto;
 | 
				
			||||||
 | 
					      display: flex;
 | 
				
			||||||
 | 
					      align-items: center;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &__toggle > div {
 | 
				
			||||||
 | 
					      display: flex;
 | 
				
			||||||
 | 
					      border-inline-start: 1px solid lighten($ui-base-color, 8%);
 | 
				
			||||||
 | 
					      padding-inline-start: 16px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ $red-600: #b7253d !default; // Deep Carmine
 | 
				
			||||||
$red-500: #df405a !default; // Cerise
 | 
					$red-500: #df405a !default; // Cerise
 | 
				
			||||||
$blurple-600: #563acc; // Iris
 | 
					$blurple-600: #563acc; // Iris
 | 
				
			||||||
$blurple-500: #6364ff; // Brand purple
 | 
					$blurple-500: #6364ff; // Brand purple
 | 
				
			||||||
 | 
					$blurple-400: #7477fd; // Medium slate blue
 | 
				
			||||||
$blurple-300: #858afa; // Faded Blue
 | 
					$blurple-300: #858afa; // Faded Blue
 | 
				
			||||||
$grey-600: #4e4c5a; // Trout
 | 
					$grey-600: #4e4c5a; // Trout
 | 
				
			||||||
$grey-100: #dadaf3; // Topaz
 | 
					$grey-100: #dadaf3; // Topaz
 | 
				
			||||||
| 
						 | 
					@ -42,6 +43,8 @@ $ui-highlight-color: $classic-highlight-color !default;
 | 
				
			||||||
$ui-button-color: $white !default;
 | 
					$ui-button-color: $white !default;
 | 
				
			||||||
$ui-button-background-color: $blurple-500 !default;
 | 
					$ui-button-background-color: $blurple-500 !default;
 | 
				
			||||||
$ui-button-focus-background-color: $blurple-600 !default;
 | 
					$ui-button-focus-background-color: $blurple-600 !default;
 | 
				
			||||||
 | 
					$ui-button-focus-outline-color: $blurple-400 !default;
 | 
				
			||||||
 | 
					$ui-button-focus-outline: solid 2px $ui-button-focus-outline-color !default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$ui-button-secondary-color: $grey-100 !default;
 | 
					$ui-button-secondary-color: $grey-100 !default;
 | 
				
			||||||
$ui-button-secondary-border-color: $grey-100 !default;
 | 
					$ui-button-secondary-border-color: $grey-100 !default;
 | 
				
			||||||
| 
						 | 
					@ -56,6 +59,9 @@ $ui-button-tertiary-focus-color: $white !default;
 | 
				
			||||||
$ui-button-destructive-background-color: $red-500 !default;
 | 
					$ui-button-destructive-background-color: $red-500 !default;
 | 
				
			||||||
$ui-button-destructive-focus-background-color: $red-600 !default;
 | 
					$ui-button-destructive-focus-background-color: $red-600 !default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$ui-button-icon-focus-outline: $ui-button-focus-outline !default;
 | 
				
			||||||
 | 
					$ui-button-icon-hover-background-color: rgba(140, 141, 255, 40%) !default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Variables for texts
 | 
					// Variables for texts
 | 
				
			||||||
$primary-text-color: $white !default;
 | 
					$primary-text-color: $white !default;
 | 
				
			||||||
$darker-text-color: $ui-primary-color !default;
 | 
					$darker-text-color: $ui-primary-color !default;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue