Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge upstream changes up to 407287573c9c602c5490224c43639b39edf7d48a #2679

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import PropTypes from 'prop-types';
import { useState, useRef, useCallback } from 'react';

import { FormattedMessage } from 'react-intl';

import classNames from 'classnames';

import Overlay from 'react-overlays/Overlay';



import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import BadgeIcon from '@/material-icons/400-24px/badge.svg?react';
import GlobeIcon from '@/material-icons/400-24px/globe.svg?react';
import { Icon } from 'flavours/glitch/components/icon';

export const DomainPill = ({ domain, username, isSelf }) => {
const [open, setOpen] = useState(false);
const [expanded, setExpanded] = useState(false);
const triggerRef = useRef(null);

const handleClick = useCallback(() => {
setOpen(!open);
}, [open, setOpen]);

const handleExpandClick = useCallback(() => {
setExpanded(!expanded);
}, [expanded, setExpanded]);

return (
<>
<button className={classNames('account__domain-pill', { active: open })} ref={triggerRef} onClick={handleClick}>{domain}</button>

<Overlay show={open} rootClose onHide={handleClick} offset={[5, 5]} target={triggerRef}>
{({ props }) => (
<div {...props} className='account__domain-pill__popout dropdown-animation'>
<div className='account__domain-pill__popout__header'>
<div className='account__domain-pill__popout__header__icon'><Icon icon={BadgeIcon} /></div>
<h3><FormattedMessage id='domain_pill.whats_in_a_handle' defaultMessage="What's in a handle?" /></h3>
</div>

<div className='account__domain-pill__popout__handle'>
<div className='account__domain-pill__popout__handle__label'>{isSelf ? <FormattedMessage id='domain_pill.your_handle' defaultMessage='Your handle:' /> : <FormattedMessage id='domain_pill.their_handle' defaultMessage='Their handle:' />}</div>
<div className='account__domain-pill__popout__handle__handle'>@{username}@{domain}</div>
</div>

<div className='account__domain-pill__popout__parts'>
<div>
<div className='account__domain-pill__popout__parts__icon'><Icon icon={AlternateEmailIcon} /></div>

<div>
<h6><FormattedMessage id='domain_pill.username' defaultMessage='Username' /></h6>
<p>{isSelf ? <FormattedMessage id='domain_pill.your_username' defaultMessage='Your unique identifier on this server. It’s possible to find users with the same username on different servers.' /> : <FormattedMessage id='domain_pill.their_username' defaultMessage='Their unique identifier on their server. It’s possible to find users with the same username on different servers.' />}</p>
</div>
</div>

<div>
<div className='account__domain-pill__popout__parts__icon'><Icon icon={GlobeIcon} /></div>

<div>
<h6><FormattedMessage id='domain_pill.server' defaultMessage='Server' /></h6>
<p>{isSelf ? <FormattedMessage id='domain_pill.your_server' defaultMessage='Your digital home, where all of your posts live. Don’t like this one? Transfer servers at any time and bring your followers, too.' /> : <FormattedMessage id='domain_pill.their_server' defaultMessage='Their digital home, where all of their posts live.' />}</p>
</div>
</div>
</div>

<p>{isSelf ? <FormattedMessage id='domain_pill.who_you_are' defaultMessage='Because your handle says who you are and where you are, people can interact with you across the social web of <button>ActivityPub-powered platforms</button>.' values={{ button: x => <button onClick={handleExpandClick} className='link-button'>{x}</button> }} /> : <FormattedMessage id='domain_pill.who_they_are' defaultMessage='Since handles say who someone is and where they are, you can interact with people across the social web of <button>ActivityPub-powered platforms</button>.' values={{ button: x => <button onClick={handleExpandClick} className='link-button'>{x}</button> }} />}</p>

{expanded && (
<>
<p><FormattedMessage id='domain_pill.activitypub_like_language' defaultMessage='ActivityPub is like the language Mastodon speaks with other social networks.' /></p>
<p><FormattedMessage id='domain_pill.activitypub_lets_connect' defaultMessage='It lets you connect and interact with people not just on Mastodon, but across different social apps too.' /></p>
</>
)}
</div>
)}
</Overlay>
</>
);
};

DomainPill.propTypes = {
username: PropTypes.string.isRequired,
domain: PropTypes.string.isRequired,
isSelf: PropTypes.bool,
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ import { Icon } from 'flavours/glitch/components/icon';
import { IconButton } from 'flavours/glitch/components/icon_button';
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
import { autoPlayGif, me, domain } from 'flavours/glitch/initial_state';
import { autoPlayGif, me, domain as localDomain } from 'flavours/glitch/initial_state';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
import { preferencesLink, profileLink, accountAdminLink } from 'flavours/glitch/utils/backend_links';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';

import AccountNoteContainer from '../containers/account_note_container';
import FollowRequestNoteContainer from '../containers/follow_request_note_container';

import { DomainPill } from './domain_pill';

const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
follow: { id: 'account.follow', defaultMessage: 'Follow' },
Expand Down Expand Up @@ -75,7 +77,7 @@ const messages = defineMessages({

const titleFromAccount = account => {
const displayName = account.get('display_name');
const acct = account.get('acct') === account.get('username') ? `${account.get('username')}@${domain}` : account.get('acct');
const acct = account.get('acct') === account.get('username') ? `${account.get('username')}@${localDomain}` : account.get('acct');
const prefix = displayName.trim().length === 0 ? account.get('username') : displayName;

return `${prefix} (@${acct})`;
Expand Down Expand Up @@ -167,7 +169,7 @@ class Header extends ImmutablePureComponent {
};

render () {
const { account, hidden, intl, domain } = this.props;
const { account, hidden, intl } = this.props;
const { signedIn, permissions } = this.context.identity;

if (!account) {
Expand Down Expand Up @@ -314,7 +316,8 @@ class Header extends ImmutablePureComponent {
const displayNameHtml = { __html: account.get('display_name_html') };
const fields = account.get('fields');
const isLocal = account.get('acct').indexOf('@') === -1;
const acct = isLocal && domain ? `${account.get('acct')}@${domain}` : account.get('acct');
const username = account.get('acct').split('@')[0];
const domain = isLocal ? localDomain : account.get('acct').split('@')[1];
const isIndexable = !account.get('noindex');

const badges = [];
Expand Down Expand Up @@ -359,7 +362,9 @@ class Header extends ImmutablePureComponent {
<h1>
<span dangerouslySetInnerHTML={displayNameHtml} />
<small>
<span>@{acct}</span> {lockedIcon}
<span>@{username}<span className='invisible'>@{domain}</span></span>
<DomainPill username={username} domain={domain} isSelf={me === account.get('id')} />
{lockedIcon}
</small>
</h1>
</div>
Expand Down
127 changes: 120 additions & 7 deletions app/javascript/flavours/glitch/styles/components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2004,6 +2004,118 @@ body > [data-popper-placement] {
}
}

&__domain-pill {
display: inline-flex;
background: rgba($highlight-text-color, 0.2);
border-radius: 4px;
border: 0;
color: $highlight-text-color;
font-weight: 500;
font-size: 12px;
line-height: 16px;
padding: 4px 8px;

&.active {
color: $white;
background: $ui-highlight-color;
}

&__popout {
background: var(--dropdown-background-color);
backdrop-filter: var(--background-filter);
border: 1px solid var(--dropdown-border-color);
box-shadow: var(--dropdown-shadow);
max-width: 320px;
padding: 16px;
border-radius: 8px;
display: flex;
flex-direction: column;
gap: 24px;
font-size: 14px;
line-height: 20px;
color: $darker-text-color;

.link-button {
display: inline;
font-size: inherit;
line-height: inherit;
}

&__header {
display: flex;
align-items: center;
gap: 12px;

&__icon {
width: 40px;
height: 40px;
background: $ui-highlight-color;
color: $white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
flex-shrink: 0;
}

h3 {
font-size: 17px;
line-height: 22px;
color: $primary-text-color;
}
}

&__handle {
border: 2px dashed $highlight-text-color;
background: rgba($highlight-text-color, 0.1);
padding: 12px 8px;
color: $highlight-text-color;
border-radius: 4px;

&__label {
font-size: 11px;
line-height: 16px;
font-weight: 500;
}

&__handle {
user-select: all;
}
}

&__parts {
display: flex;
flex-direction: column;
gap: 8px;
font-size: 12px;
line-height: 16px;

& > div {
display: flex;
align-items: flex-start;
gap: 12px;
}

&__icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: $highlight-text-color;
}

h6 {
font-size: 14px;
line-height: 20px;
font-weight: 500;
color: $primary-text-color;
}
}
}
}

&__note {
font-size: 14px;
font-weight: 400;
Expand Down Expand Up @@ -8126,14 +8238,17 @@ noscript {
font-size: 17px;
line-height: 22px;
color: $primary-text-color;
font-weight: 700;
font-weight: 600;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;

small {
display: block;
font-size: 15px;
display: flex;
align-items: center;
gap: 4px;
font-size: 14px;
line-height: 20px;
color: $darker-text-color;
font-weight: 400;
overflow: hidden;
Expand All @@ -8144,10 +8259,8 @@ noscript {
}

.icon-lock {
height: 16px;
width: 16px;
position: relative;
top: 3px;
height: 18px;
width: 18px;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import PropTypes from 'prop-types';
import { useState, useRef, useCallback } from 'react';

import { FormattedMessage } from 'react-intl';

import classNames from 'classnames';

import Overlay from 'react-overlays/Overlay';



import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import BadgeIcon from '@/material-icons/400-24px/badge.svg?react';
import GlobeIcon from '@/material-icons/400-24px/globe.svg?react';
import { Icon } from 'mastodon/components/icon';

export const DomainPill = ({ domain, username, isSelf }) => {
const [open, setOpen] = useState(false);
const [expanded, setExpanded] = useState(false);
const triggerRef = useRef(null);

const handleClick = useCallback(() => {
setOpen(!open);
}, [open, setOpen]);

const handleExpandClick = useCallback(() => {
setExpanded(!expanded);
}, [expanded, setExpanded]);

return (
<>
<button className={classNames('account__domain-pill', { active: open })} ref={triggerRef} onClick={handleClick}>{domain}</button>

<Overlay show={open} rootClose onHide={handleClick} offset={[5, 5]} target={triggerRef}>
{({ props }) => (
<div {...props} className='account__domain-pill__popout dropdown-animation'>
<div className='account__domain-pill__popout__header'>
<div className='account__domain-pill__popout__header__icon'><Icon icon={BadgeIcon} /></div>
<h3><FormattedMessage id='domain_pill.whats_in_a_handle' defaultMessage="What's in a handle?" /></h3>
</div>

<div className='account__domain-pill__popout__handle'>
<div className='account__domain-pill__popout__handle__label'>{isSelf ? <FormattedMessage id='domain_pill.your_handle' defaultMessage='Your handle:' /> : <FormattedMessage id='domain_pill.their_handle' defaultMessage='Their handle:' />}</div>
<div className='account__domain-pill__popout__handle__handle'>@{username}@{domain}</div>
</div>

<div className='account__domain-pill__popout__parts'>
<div>
<div className='account__domain-pill__popout__parts__icon'><Icon icon={AlternateEmailIcon} /></div>

<div>
<h6><FormattedMessage id='domain_pill.username' defaultMessage='Username' /></h6>
<p>{isSelf ? <FormattedMessage id='domain_pill.your_username' defaultMessage='Your unique identifier on this server. It’s possible to find users with the same username on different servers.' /> : <FormattedMessage id='domain_pill.their_username' defaultMessage='Their unique identifier on their server. It’s possible to find users with the same username on different servers.' />}</p>
</div>
</div>

<div>
<div className='account__domain-pill__popout__parts__icon'><Icon icon={GlobeIcon} /></div>

<div>
<h6><FormattedMessage id='domain_pill.server' defaultMessage='Server' /></h6>
<p>{isSelf ? <FormattedMessage id='domain_pill.your_server' defaultMessage='Your digital home, where all of your posts live. Don’t like this one? Transfer servers at any time and bring your followers, too.' /> : <FormattedMessage id='domain_pill.their_server' defaultMessage='Their digital home, where all of their posts live.' />}</p>
</div>
</div>
</div>

<p>{isSelf ? <FormattedMessage id='domain_pill.who_you_are' defaultMessage='Because your handle says who you are and where you are, people can interact with you across the social web of <button>ActivityPub-powered platforms</button>.' values={{ button: x => <button onClick={handleExpandClick} className='link-button'>{x}</button> }} /> : <FormattedMessage id='domain_pill.who_they_are' defaultMessage='Since handles say who someone is and where they are, you can interact with people across the social web of <button>ActivityPub-powered platforms</button>.' values={{ button: x => <button onClick={handleExpandClick} className='link-button'>{x}</button> }} />}</p>

{expanded && (
<>
<p><FormattedMessage id='domain_pill.activitypub_like_language' defaultMessage='ActivityPub is like the language Mastodon speaks with other social networks.' /></p>
<p><FormattedMessage id='domain_pill.activitypub_lets_connect' defaultMessage='It lets you connect and interact with people not just on Mastodon, but across different social apps too.' /></p>
</>
)}
</div>
)}
</Overlay>
</>
);
};

DomainPill.propTypes = {
username: PropTypes.string.isRequired,
domain: PropTypes.string.isRequired,
isSelf: PropTypes.bool,
};
Loading
Loading