[Glitch] Change design of boost modal in web UI
Port 29b9642b31 to glitch-soc
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
			
			
This commit is contained in:
		
							parent
							
								
									d3629d191f
								
							
						
					
					
						commit
						e15fad27bc
					
				| 
						 | 
					@ -1,28 +1,17 @@
 | 
				
			||||||
import type { MouseEventHandler } from 'react';
 | 
					 | 
				
			||||||
import { useCallback, useState } from 'react';
 | 
					import { useCallback, useState } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
 | 
					import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import classNames from 'classnames';
 | 
					import classNames from 'classnames';
 | 
				
			||||||
import { useHistory } from 'react-router';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import type Immutable from 'immutable';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
 | 
					import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
 | 
				
			||||||
import AttachmentList from 'flavours/glitch/components/attachment_list';
 | 
					import { Button } from 'flavours/glitch/components/button';
 | 
				
			||||||
import { Icon } from 'flavours/glitch/components/icon';
 | 
					import { Icon } from 'flavours/glitch/components/icon';
 | 
				
			||||||
import { VisibilityIcon } from 'flavours/glitch/components/visibility_icon';
 | 
					 | 
				
			||||||
import PrivacyDropdown from 'flavours/glitch/features/compose/components/privacy_dropdown';
 | 
					import PrivacyDropdown from 'flavours/glitch/features/compose/components/privacy_dropdown';
 | 
				
			||||||
import type { Account } from 'flavours/glitch/models/account';
 | 
					import { EmbeddedStatus } from 'flavours/glitch/features/notifications_v2/components/embedded_status';
 | 
				
			||||||
import type { Status, StatusVisibility } from 'flavours/glitch/models/status';
 | 
					import type { Status, StatusVisibility } from 'flavours/glitch/models/status';
 | 
				
			||||||
import { useAppSelector } from 'flavours/glitch/store';
 | 
					import { useAppSelector } from 'flavours/glitch/store';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Avatar } from '../../../components/avatar';
 | 
					 | 
				
			||||||
import { Button } from '../../../components/button';
 | 
					 | 
				
			||||||
import { DisplayName } from '../../../components/display_name';
 | 
					 | 
				
			||||||
import { RelativeTimestamp } from '../../../components/relative_timestamp';
 | 
					 | 
				
			||||||
import StatusContent from '../../../components/status_content';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const messages = defineMessages({
 | 
					const messages = defineMessages({
 | 
				
			||||||
  cancel_reblog: {
 | 
					  cancel_reblog: {
 | 
				
			||||||
    id: 'status.cancel_reblog_private',
 | 
					    id: 'status.cancel_reblog_private',
 | 
				
			||||||
| 
						 | 
					@ -35,21 +24,19 @@ export const BoostModal: React.FC<{
 | 
				
			||||||
  status: Status;
 | 
					  status: Status;
 | 
				
			||||||
  onClose: () => void;
 | 
					  onClose: () => void;
 | 
				
			||||||
  onReblog: (status: Status, privacy: StatusVisibility) => void;
 | 
					  onReblog: (status: Status, privacy: StatusVisibility) => void;
 | 
				
			||||||
  missingMediaDescription?: boolean;
 | 
					}> = ({ status, onReblog, onClose }) => {
 | 
				
			||||||
}> = ({ status, onReblog, onClose, missingMediaDescription }) => {
 | 
					 | 
				
			||||||
  const intl = useIntl();
 | 
					  const intl = useIntl();
 | 
				
			||||||
  const history = useHistory();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const default_privacy = useAppSelector(
 | 
					  const defaultPrivacy = useAppSelector(
 | 
				
			||||||
    // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
 | 
					    // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
 | 
				
			||||||
    (state) => state.compose.get('default_privacy') as StatusVisibility,
 | 
					    (state) => state.compose.get('default_privacy') as StatusVisibility,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const account = status.get('account') as Account;
 | 
					  const statusId = status.get('id') as string;
 | 
				
			||||||
  const statusVisibility = status.get('visibility') as StatusVisibility;
 | 
					  const statusVisibility = status.get('visibility') as StatusVisibility;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [privacy, setPrivacy] = useState<StatusVisibility>(
 | 
					  const [privacy, setPrivacy] = useState<StatusVisibility>(
 | 
				
			||||||
    statusVisibility === 'private' ? 'private' : default_privacy,
 | 
					    statusVisibility === 'private' ? 'private' : defaultPrivacy,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onPrivacyChange = useCallback((value: StatusVisibility) => {
 | 
					  const onPrivacyChange = useCallback((value: StatusVisibility) => {
 | 
				
			||||||
| 
						 | 
					@ -61,20 +48,9 @@ export const BoostModal: React.FC<{
 | 
				
			||||||
    onClose();
 | 
					    onClose();
 | 
				
			||||||
  }, [onClose, onReblog, status, privacy]);
 | 
					  }, [onClose, onReblog, status, privacy]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleAccountClick = useCallback<MouseEventHandler>(
 | 
					  const handleCancel = useCallback(() => {
 | 
				
			||||||
    (e) => {
 | 
					    onClose();
 | 
				
			||||||
      if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
 | 
					  }, [onClose]);
 | 
				
			||||||
        e.preventDefault();
 | 
					 | 
				
			||||||
        onClose();
 | 
					 | 
				
			||||||
        history.push(`/@${account.acct}`);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    [history, onClose, account],
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const buttonText = status.get('reblogged')
 | 
					 | 
				
			||||||
    ? messages.cancel_reblog
 | 
					 | 
				
			||||||
    : messages.reblog;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const findContainer = useCallback(
 | 
					  const findContainer = useCallback(
 | 
				
			||||||
    () => document.getElementsByClassName('modal-root__container')[0],
 | 
					    () => document.getElementsByClassName('modal-root__container')[0],
 | 
				
			||||||
| 
						 | 
					@ -82,88 +58,78 @@ export const BoostModal: React.FC<{
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className='modal-root__modal boost-modal'>
 | 
					    <div className='modal-root__modal safety-action-modal'>
 | 
				
			||||||
      <div className='boost-modal__container'>
 | 
					      <div className='safety-action-modal__top'>
 | 
				
			||||||
        <div
 | 
					        <div className='safety-action-modal__header'>
 | 
				
			||||||
          className={classNames(
 | 
					          <div className='safety-action-modal__header__icon'>
 | 
				
			||||||
            'status',
 | 
					            <Icon icon={RepeatIcon} id='retweet' />
 | 
				
			||||||
            `status-${statusVisibility}`,
 | 
					 | 
				
			||||||
            'light',
 | 
					 | 
				
			||||||
          )}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <div className='status__info'>
 | 
					 | 
				
			||||||
            <a
 | 
					 | 
				
			||||||
              href={status.get('url') as string}
 | 
					 | 
				
			||||||
              className='status__relative-time'
 | 
					 | 
				
			||||||
              target='_blank'
 | 
					 | 
				
			||||||
              rel='noopener noreferrer'
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              <span className='status__visibility-icon'>
 | 
					 | 
				
			||||||
                <VisibilityIcon visibility={statusVisibility} />
 | 
					 | 
				
			||||||
              </span>
 | 
					 | 
				
			||||||
              <RelativeTimestamp
 | 
					 | 
				
			||||||
                timestamp={status.get('created_at') as string}
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
            </a>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <a
 | 
					 | 
				
			||||||
              onClick={handleAccountClick}
 | 
					 | 
				
			||||||
              href={account.url}
 | 
					 | 
				
			||||||
              className='status__display-name'
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              <div className='status__avatar'>
 | 
					 | 
				
			||||||
                <Avatar account={account} size={48} />
 | 
					 | 
				
			||||||
              </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              <DisplayName account={account} />
 | 
					 | 
				
			||||||
            </a>
 | 
					 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          {/* @ts-expect-error Expected until StatusContent is typed */}
 | 
					          <div>
 | 
				
			||||||
          <StatusContent status={status} />
 | 
					            <h1>
 | 
				
			||||||
 | 
					              {status.get('reblogged') ? (
 | 
				
			||||||
 | 
					                <FormattedMessage
 | 
				
			||||||
 | 
					                  id='boost_modal.undo_reblog'
 | 
				
			||||||
 | 
					                  defaultMessage='Unboost post?'
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              ) : (
 | 
				
			||||||
 | 
					                <FormattedMessage
 | 
				
			||||||
 | 
					                  id='boost_modal.reblog'
 | 
				
			||||||
 | 
					                  defaultMessage='Boost post?'
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            </h1>
 | 
				
			||||||
 | 
					            <div>
 | 
				
			||||||
 | 
					              <FormattedMessage
 | 
				
			||||||
 | 
					                id='boost_modal.combo'
 | 
				
			||||||
 | 
					                defaultMessage='You can press {combo} to skip this next time'
 | 
				
			||||||
 | 
					                values={{
 | 
				
			||||||
 | 
					                  combo: (
 | 
				
			||||||
 | 
					                    <span className='hotkey-combination'>
 | 
				
			||||||
 | 
					                      <kbd>Shift</kbd>+<Icon id='retweet' icon={RepeatIcon} />
 | 
				
			||||||
 | 
					                    </span>
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          {(status.get('media_attachments') as Immutable.List<unknown>).size >
 | 
					        <div className='safety-action-modal__status'>
 | 
				
			||||||
            0 && (
 | 
					          <EmbeddedStatus statusId={statusId} />
 | 
				
			||||||
            <AttachmentList compact media={status.get('media_attachments')} />
 | 
					 | 
				
			||||||
          )}
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div className='boost-modal__action-bar'>
 | 
					      <div className={classNames('safety-action-modal__bottom')}>
 | 
				
			||||||
        <div>
 | 
					        <div className='safety-action-modal__actions'>
 | 
				
			||||||
          {missingMediaDescription ? (
 | 
					          {!status.get('reblogged') && (
 | 
				
			||||||
            <FormattedMessage
 | 
					            <PrivacyDropdown
 | 
				
			||||||
              id='boost_modal.missing_description'
 | 
					              noDirect
 | 
				
			||||||
              defaultMessage='This toot contains some media without description'
 | 
					              value={privacy}
 | 
				
			||||||
            />
 | 
					              container={findContainer}
 | 
				
			||||||
          ) : (
 | 
					              onChange={onPrivacyChange}
 | 
				
			||||||
            <FormattedMessage
 | 
					              disabled={statusVisibility === 'private'}
 | 
				
			||||||
              id='boost_modal.combo'
 | 
					 | 
				
			||||||
              defaultMessage='You can press {combo} to skip this next time'
 | 
					 | 
				
			||||||
              values={{
 | 
					 | 
				
			||||||
                combo: (
 | 
					 | 
				
			||||||
                  <span>
 | 
					 | 
				
			||||||
                    Shift + <Icon id='retweet' icon={RepeatIcon} />
 | 
					 | 
				
			||||||
                  </span>
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
              }}
 | 
					 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
        </div>
 | 
					
 | 
				
			||||||
        {statusVisibility !== 'private' && !status.get('reblogged') && (
 | 
					          <div className='spacer' />
 | 
				
			||||||
          <PrivacyDropdown
 | 
					
 | 
				
			||||||
            noDirect
 | 
					          <button onClick={handleCancel} className='link-button'>
 | 
				
			||||||
            value={privacy}
 | 
					            <FormattedMessage
 | 
				
			||||||
            container={findContainer}
 | 
					              id='confirmation_modal.cancel'
 | 
				
			||||||
            onChange={onPrivacyChange}
 | 
					              defaultMessage='Cancel'
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <Button
 | 
				
			||||||
 | 
					            onClick={handleReblog}
 | 
				
			||||||
 | 
					            text={intl.formatMessage(
 | 
				
			||||||
 | 
					              status.get('reblogged')
 | 
				
			||||||
 | 
					                ? messages.cancel_reblog
 | 
				
			||||||
 | 
					                : messages.reblog,
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        )}
 | 
					        </div>
 | 
				
			||||||
        <Button
 | 
					 | 
				
			||||||
          text={intl.formatMessage(buttonText)}
 | 
					 | 
				
			||||||
          onClick={handleReblog}
 | 
					 | 
				
			||||||
          // eslint-disable-next-line jsx-a11y/no-autofocus
 | 
					 | 
				
			||||||
          autoFocus
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6590,6 +6590,48 @@ a.status-card {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__status {
 | 
				
			||||||
 | 
					    border: 1px solid var(--modal-border-color);
 | 
				
			||||||
 | 
					    border-radius: 8px;
 | 
				
			||||||
 | 
					    padding: 8px;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &__account {
 | 
				
			||||||
 | 
					      display: flex;
 | 
				
			||||||
 | 
					      align-items: center;
 | 
				
			||||||
 | 
					      gap: 4px;
 | 
				
			||||||
 | 
					      margin-bottom: 8px;
 | 
				
			||||||
 | 
					      color: $dark-text-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      bdi {
 | 
				
			||||||
 | 
					        color: inherit;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &__content {
 | 
				
			||||||
 | 
					      display: -webkit-box;
 | 
				
			||||||
 | 
					      font-size: 15px;
 | 
				
			||||||
 | 
					      line-height: 22px;
 | 
				
			||||||
 | 
					      color: $dark-text-color;
 | 
				
			||||||
 | 
					      -webkit-line-clamp: 4;
 | 
				
			||||||
 | 
					      -webkit-box-orient: vertical;
 | 
				
			||||||
 | 
					      max-height: 4 * 22px;
 | 
				
			||||||
 | 
					      overflow: hidden;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      p,
 | 
				
			||||||
 | 
					      a {
 | 
				
			||||||
 | 
					        color: inherit;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .reply-indicator__attachments {
 | 
				
			||||||
 | 
					      margin-top: 0;
 | 
				
			||||||
 | 
					      font-size: 15px;
 | 
				
			||||||
 | 
					      line-height: 22px;
 | 
				
			||||||
 | 
					      color: $dark-text-color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &__bullet-points {
 | 
					  &__bullet-points {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
| 
						 | 
					@ -6667,6 +6709,12 @@ a.status-card {
 | 
				
			||||||
    gap: 8px;
 | 
					    gap: 8px;
 | 
				
			||||||
    justify-content: flex-end;
 | 
					    justify-content: flex-end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &__hint {
 | 
				
			||||||
 | 
					      font-size: 14px;
 | 
				
			||||||
 | 
					      line-height: 20px;
 | 
				
			||||||
 | 
					      color: $dark-text-color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .link-button {
 | 
					    .link-button {
 | 
				
			||||||
      padding: 10px 12px;
 | 
					      padding: 10px 12px;
 | 
				
			||||||
      font-weight: 600;
 | 
					      font-weight: 600;
 | 
				
			||||||
| 
						 | 
					@ -6674,6 +6722,18 @@ a.status-card {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hotkey-combination {
 | 
				
			||||||
 | 
					  display: inline-flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  gap: 4px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  kbd {
 | 
				
			||||||
 | 
					    padding: 3px 5px;
 | 
				
			||||||
 | 
					    border: 1px solid var(--background-border-color);
 | 
				
			||||||
 | 
					    border-radius: 4px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.doodle-modal,
 | 
					.doodle-modal,
 | 
				
			||||||
.boost-modal,
 | 
					.boost-modal,
 | 
				
			||||||
.report-modal,
 | 
					.report-modal,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue