[Glitch] Profile redesign: Quality pass
Port fb89198460f9cebf624cc5ca0b139563ae4fd120 to glitch-soc Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
d89db37e93
commit
b7d5607dd1
@ -5,7 +5,6 @@ import { useIntl, defineMessages } from 'react-intl';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { useIdentity } from '@/flavours/glitch/identity_context';
|
import { useIdentity } from '@/flavours/glitch/identity_context';
|
||||||
import { isClientFeatureEnabled } from '@/flavours/glitch/utils/environment';
|
|
||||||
import {
|
import {
|
||||||
fetchRelationships,
|
fetchRelationships,
|
||||||
followAccount,
|
followAccount,
|
||||||
@ -60,7 +59,14 @@ export const FollowButton: React.FC<{
|
|||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
labelLength?: 'auto' | 'short' | 'long';
|
labelLength?: 'auto' | 'short' | 'long';
|
||||||
className?: string;
|
className?: string;
|
||||||
}> = ({ accountId, compact, labelLength = 'auto', className }) => {
|
withUnmute?: boolean;
|
||||||
|
}> = ({
|
||||||
|
accountId,
|
||||||
|
compact,
|
||||||
|
labelLength = 'auto',
|
||||||
|
className,
|
||||||
|
withUnmute = true,
|
||||||
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { signedIn } = useIdentity();
|
const { signedIn } = useIdentity();
|
||||||
@ -102,10 +108,7 @@ export const FollowButton: React.FC<{
|
|||||||
modalProps: { account },
|
modalProps: { account },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (relationship.muting && withUnmute) {
|
||||||
relationship.muting &&
|
|
||||||
!isClientFeatureEnabled('profile_redesign')
|
|
||||||
) {
|
|
||||||
dispatch(unmuteAccount(accountId));
|
dispatch(unmuteAccount(accountId));
|
||||||
} else if (account && relationship.following) {
|
} else if (account && relationship.following) {
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -121,7 +124,7 @@ export const FollowButton: React.FC<{
|
|||||||
} else {
|
} else {
|
||||||
dispatch(followAccount(accountId));
|
dispatch(followAccount(accountId));
|
||||||
}
|
}
|
||||||
}, [dispatch, accountId, relationship, account, signedIn]);
|
}, [signedIn, relationship, accountId, withUnmute, account, dispatch]);
|
||||||
|
|
||||||
const isNarrow = useBreakpoint('narrow');
|
const isNarrow = useBreakpoint('narrow');
|
||||||
const useShortLabel =
|
const useShortLabel =
|
||||||
@ -140,10 +143,7 @@ export const FollowButton: React.FC<{
|
|||||||
label = intl.formatMessage(messages.edit_profile);
|
label = intl.formatMessage(messages.edit_profile);
|
||||||
} else if (!relationship) {
|
} else if (!relationship) {
|
||||||
label = <LoadingIndicator />;
|
label = <LoadingIndicator />;
|
||||||
} else if (
|
} else if (relationship.muting && withUnmute) {
|
||||||
relationship.muting &&
|
|
||||||
!isClientFeatureEnabled('profile_redesign')
|
|
||||||
) {
|
|
||||||
label = intl.formatMessage(messages.unmute);
|
label = intl.formatMessage(messages.unmute);
|
||||||
} else if (relationship.following) {
|
} else if (relationship.following) {
|
||||||
label = intl.formatMessage(messages.unfollow);
|
label = intl.formatMessage(messages.unfollow);
|
||||||
|
|||||||
@ -1,23 +1,24 @@
|
|||||||
import type { RefCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
|
import { openModal } from '@/flavours/glitch/actions/modal';
|
||||||
import { AccountBio } from '@/flavours/glitch/components/account_bio';
|
import { AccountBio } from '@/flavours/glitch/components/account_bio';
|
||||||
|
import { Avatar } from '@/flavours/glitch/components/avatar';
|
||||||
import { AnimateEmojiProvider } from '@/flavours/glitch/components/emoji/context';
|
import { AnimateEmojiProvider } from '@/flavours/glitch/components/emoji/context';
|
||||||
import { openModal } from 'flavours/glitch/actions/modal';
|
import { AccountNote } from '@/flavours/glitch/features/account/components/account_note';
|
||||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
import FollowRequestNoteContainer from '@/flavours/glitch/features/account/containers/follow_request_note_container';
|
||||||
import { AccountNote } from 'flavours/glitch/features/account/components/account_note';
|
import { useLayout } from '@/flavours/glitch/hooks/useLayout';
|
||||||
import FollowRequestNoteContainer from 'flavours/glitch/features/account/containers/follow_request_note_container';
|
import { useVisibility } from '@/flavours/glitch/hooks/useVisibility';
|
||||||
import {
|
import {
|
||||||
autoPlayGif,
|
autoPlayGif,
|
||||||
me,
|
me,
|
||||||
domain as localDomain,
|
domain as localDomain,
|
||||||
} from 'flavours/glitch/initial_state';
|
} from '@/flavours/glitch/initial_state';
|
||||||
import type { Account } from 'flavours/glitch/models/account';
|
import type { Account } from '@/flavours/glitch/models/account';
|
||||||
import { getAccountHidden } from 'flavours/glitch/selectors/accounts';
|
import { getAccountHidden } from '@/flavours/glitch/selectors/accounts';
|
||||||
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
import { useAppSelector, useAppDispatch } from '@/flavours/glitch/store';
|
||||||
|
|
||||||
import { ActionBar } from '../../account/components/action_bar';
|
import { ActionBar } from '../../account/components/action_bar';
|
||||||
import { isRedesignEnabled } from '../common';
|
import { isRedesignEnabled } from '../common';
|
||||||
@ -50,6 +51,8 @@ export const AccountHeader: React.FC<{
|
|||||||
accountId: string;
|
accountId: string;
|
||||||
hideTabs?: boolean;
|
hideTabs?: boolean;
|
||||||
}> = ({ accountId, hideTabs }) => {
|
}> = ({ accountId, hideTabs }) => {
|
||||||
|
const isRedesign = isRedesignEnabled();
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const account = useAppSelector((state) => state.accounts.get(accountId));
|
const account = useAppSelector((state) => state.accounts.get(accountId));
|
||||||
const relationship = useAppSelector((state) =>
|
const relationship = useAppSelector((state) =>
|
||||||
@ -82,39 +85,12 @@ export const AccountHeader: React.FC<{
|
|||||||
[dispatch, account],
|
[dispatch, account],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [isFooterIntersecting, setIsIntersecting] = useState(false);
|
const { layout } = useLayout();
|
||||||
const handleIntersect: IntersectionObserverCallback = useCallback(
|
const { observedRef, isIntersecting } = useVisibility({
|
||||||
(entries) => {
|
observerOptions: {
|
||||||
const entry = entries.at(0);
|
rootMargin: layout === 'mobile' ? '0px 0px -55px 0px' : '', // Height of bottom nav bar.
|
||||||
if (!entry) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsIntersecting(entry.isIntersecting);
|
|
||||||
},
|
},
|
||||||
[],
|
});
|
||||||
);
|
|
||||||
const [observer] = useState(
|
|
||||||
() =>
|
|
||||||
new IntersectionObserver(handleIntersect, {
|
|
||||||
rootMargin: '0px 0px -55px 0px', // Height of bottom nav bar.
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleObserverRef: RefCallback<HTMLDivElement> = useCallback(
|
|
||||||
(node) => {
|
|
||||||
if (node) {
|
|
||||||
observer.observe(node);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[observer],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
observer.disconnect();
|
|
||||||
};
|
|
||||||
}, [observer]);
|
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return null;
|
return null;
|
||||||
@ -122,6 +98,7 @@ export const AccountHeader: React.FC<{
|
|||||||
|
|
||||||
const suspendedOrHidden = hidden || account.suspended;
|
const suspendedOrHidden = hidden || account.suspended;
|
||||||
const isLocal = !account.acct.includes('@');
|
const isLocal = !account.acct.includes('@');
|
||||||
|
const isMe = me && account.id === me;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='account-timeline__header'>
|
<div className='account-timeline__header'>
|
||||||
@ -139,8 +116,13 @@ export const AccountHeader: React.FC<{
|
|||||||
<FollowRequestNoteContainer account={account} />
|
<FollowRequestNoteContainer account={account} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='account__header__image'>
|
<div
|
||||||
{me !== account.id && relationship && !isRedesignEnabled() && (
|
className={classNames(
|
||||||
|
'account__header__image',
|
||||||
|
isRedesign && redesignClasses.header,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{me !== account.id && relationship && !isRedesign && (
|
||||||
<AccountInfo relationship={relationship} />
|
<AccountInfo relationship={relationship} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -156,10 +138,15 @@ export const AccountHeader: React.FC<{
|
|||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'account__header__bar',
|
'account__header__bar',
|
||||||
isRedesignEnabled() && redesignClasses.barWrapper,
|
isRedesign && redesignClasses.barWrapper,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className='account__header__tabs'>
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'account__header__tabs',
|
||||||
|
isRedesign && redesignClasses.avatarWrapper,
|
||||||
|
)}
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
className='avatar'
|
className='avatar'
|
||||||
href={account.avatar}
|
href={account.avatar}
|
||||||
@ -169,11 +156,11 @@ export const AccountHeader: React.FC<{
|
|||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
account={suspendedOrHidden ? undefined : account}
|
account={suspendedOrHidden ? undefined : account}
|
||||||
size={92}
|
size={isRedesign ? 80 : 92}
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{!isRedesignEnabled() && (
|
{!isRedesign && (
|
||||||
<AccountButtons
|
<AccountButtons
|
||||||
accountId={accountId}
|
accountId={accountId}
|
||||||
className='account__header__buttons--desktop'
|
className='account__header__buttons--desktop'
|
||||||
@ -184,26 +171,27 @@ export const AccountHeader: React.FC<{
|
|||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'account__header__tabs__name',
|
'account__header__tabs__name',
|
||||||
isRedesignEnabled() && redesignClasses.nameWrapper,
|
isRedesign && redesignClasses.nameWrapper,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<AccountName accountId={accountId} />
|
<AccountName accountId={accountId} />
|
||||||
{isRedesignEnabled() && (
|
{isRedesign && (
|
||||||
<AccountButtons
|
<AccountButtons
|
||||||
accountId={accountId}
|
accountId={accountId}
|
||||||
className={redesignClasses.buttonsDesktop}
|
className={redesignClasses.buttonsDesktop}
|
||||||
noShare
|
noShare={!isMe || 'share' in navigator}
|
||||||
|
forceMenu={'share' in navigator}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AccountBadges accountId={accountId} />
|
<AccountBadges accountId={accountId} />
|
||||||
|
|
||||||
{me && account.id !== me && !suspendedOrHidden && (
|
{!isMe && !suspendedOrHidden && (
|
||||||
<FamiliarFollowers accountId={accountId} />
|
<FamiliarFollowers accountId={accountId} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isRedesignEnabled() && (
|
{!isRedesign && (
|
||||||
<AccountButtons
|
<AccountButtons
|
||||||
className='account__header__buttons--mobile'
|
className='account__header__buttons--mobile'
|
||||||
accountId={accountId}
|
accountId={accountId}
|
||||||
@ -216,7 +204,7 @@ export const AccountHeader: React.FC<{
|
|||||||
<div className='account__header__bio'>
|
<div className='account__header__bio'>
|
||||||
{me &&
|
{me &&
|
||||||
account.id !== me &&
|
account.id !== me &&
|
||||||
(isRedesignEnabled() ? (
|
(isRedesign ? (
|
||||||
<AccountNoteRedesign accountId={accountId} />
|
<AccountNoteRedesign accountId={accountId} />
|
||||||
) : (
|
) : (
|
||||||
<AccountNote accountId={accountId} />
|
<AccountNote accountId={accountId} />
|
||||||
@ -232,11 +220,11 @@ export const AccountHeader: React.FC<{
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isRedesignEnabled() && (
|
{isRedesign && (
|
||||||
<AccountButtons
|
<AccountButtons
|
||||||
className={classNames(
|
className={classNames(
|
||||||
redesignClasses.buttonsMobile,
|
redesignClasses.buttonsMobile,
|
||||||
!isFooterIntersecting && redesignClasses.buttonsMobileIsStuck,
|
!isIntersecting && redesignClasses.buttonsMobileIsStuck,
|
||||||
)}
|
)}
|
||||||
accountId={accountId}
|
accountId={accountId}
|
||||||
noShare
|
noShare
|
||||||
@ -248,7 +236,7 @@ export const AccountHeader: React.FC<{
|
|||||||
<ActionBar account={account} />
|
<ActionBar account={account} />
|
||||||
|
|
||||||
{!hideTabs && !hidden && <AccountTabs acct={account.acct} />}
|
{!hideTabs && !hidden && <AccountTabs acct={account.acct} />}
|
||||||
<div ref={handleObserverRef} />
|
<div ref={observedRef} />
|
||||||
|
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{titleFromAccount(account)}</title>
|
<title>{titleFromAccount(account)}</title>
|
||||||
|
|||||||
@ -1,12 +1,19 @@
|
|||||||
|
import { useCallback, useId, useRef, useState } from 'react';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import Overlay from 'react-overlays/esm/Overlay';
|
||||||
|
|
||||||
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { Icon } from '@/flavours/glitch/components/icon';
|
import { Icon } from '@/flavours/glitch/components/icon';
|
||||||
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
|
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
|
||||||
import { useAppSelector } from '@/flavours/glitch/store';
|
import { useAppSelector } from '@/flavours/glitch/store';
|
||||||
|
import AtIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||||
import HelpIcon from '@/material-icons/400-24px/help.svg?react';
|
import HelpIcon from '@/material-icons/400-24px/help.svg?react';
|
||||||
|
import DomainIcon from '@/material-icons/400-24px/language.svg?react';
|
||||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||||
|
|
||||||
import { DomainPill } from '../../account/components/domain_pill';
|
import { DomainPill } from '../../account/components/domain_pill';
|
||||||
@ -14,6 +21,18 @@ import { isRedesignEnabled } from '../common';
|
|||||||
|
|
||||||
import classes from './redesign.module.scss';
|
import classes from './redesign.module.scss';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
lockedInfo: {
|
||||||
|
id: 'account.locked_info',
|
||||||
|
defaultMessage:
|
||||||
|
'This account privacy status is set to locked. The owner manually reviews who can follow them.',
|
||||||
|
},
|
||||||
|
nameInfo: {
|
||||||
|
id: 'account.name_info',
|
||||||
|
defaultMessage: 'What does this mean?',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const AccountName: FC<{ accountId: string }> = ({ accountId }) => {
|
export const AccountName: FC<{ accountId: string }> = ({ accountId }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const account = useAccount(accountId);
|
const account = useAccount(accountId);
|
||||||
@ -46,11 +65,7 @@ export const AccountName: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
<Icon
|
<Icon
|
||||||
id='lock'
|
id='lock'
|
||||||
icon={LockIcon}
|
icon={LockIcon}
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage(messages.lockedInfo)}
|
||||||
id: 'account.locked_info',
|
|
||||||
defaultMessage:
|
|
||||||
'This account privacy status is set to locked. The owner manually reviews who can follow them.',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</small>
|
</small>
|
||||||
@ -65,15 +80,112 @@ export const AccountName: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
</h1>
|
</h1>
|
||||||
<p className={classes.username}>
|
<p className={classes.username}>
|
||||||
@{username}@{domain}
|
@{username}@{domain}
|
||||||
<DomainPill
|
<AccountNameHelp
|
||||||
username={username}
|
username={username}
|
||||||
domain={domain}
|
domain={domain}
|
||||||
isSelf={me === account.id}
|
isSelf={account.id === me}
|
||||||
className={classes.domainPill}
|
/>
|
||||||
>
|
|
||||||
<Icon id='help' icon={HelpIcon} />
|
|
||||||
</DomainPill>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AccountNameHelp: FC<{
|
||||||
|
username: string;
|
||||||
|
domain: string;
|
||||||
|
isSelf: boolean;
|
||||||
|
}> = ({ username, domain, isSelf }) => {
|
||||||
|
const accessibilityId = useId();
|
||||||
|
const intl = useIntl();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
setOpen((prev) => !prev);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
ref={triggerRef}
|
||||||
|
className={classes.handleHelpButton}
|
||||||
|
onClick={handleClick}
|
||||||
|
aria-expanded={open}
|
||||||
|
aria-controls={accessibilityId}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
id='help'
|
||||||
|
icon={HelpIcon}
|
||||||
|
aria-label={intl.formatMessage(messages.nameInfo)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Overlay
|
||||||
|
show={open}
|
||||||
|
rootClose
|
||||||
|
target={triggerRef}
|
||||||
|
onHide={handleClick}
|
||||||
|
offset={[5, 5]}
|
||||||
|
>
|
||||||
|
{({ props }) => (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
role='region'
|
||||||
|
id={accessibilityId}
|
||||||
|
className={classNames('dropdown-animation', classes.handleHelp)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.name.help.header'
|
||||||
|
defaultMessage='A handle is like an email address'
|
||||||
|
tagName='h3'
|
||||||
|
/>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<Icon id='at' icon={AtIcon} />
|
||||||
|
{isSelf ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.name.help.username_self'
|
||||||
|
defaultMessage='{username} is your username on this server. Someone on another server might have the same username.'
|
||||||
|
values={{ username: <strong>{username}</strong> }}
|
||||||
|
tagName='p'
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.name.help.username'
|
||||||
|
defaultMessage='{username} is this account’s username on their server. Someone on another server might have the same username.'
|
||||||
|
values={{ username: <strong>{username}</strong> }}
|
||||||
|
tagName='p'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon id='domain' icon={DomainIcon} />
|
||||||
|
{isSelf ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.name.help.domain_self'
|
||||||
|
defaultMessage='{domain} is your server that hosts your profile and posts.'
|
||||||
|
values={{ domain: <strong>{domain}</strong> }}
|
||||||
|
tagName='p'
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.name.help.domain'
|
||||||
|
defaultMessage='{domain} is the server that hosts the user’s profile and posts.'
|
||||||
|
values={{ domain: <strong>{domain}</strong> }}
|
||||||
|
tagName='p'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.name.help.footer'
|
||||||
|
defaultMessage='Just like you can send emails to people using different email clients, you can interact with people on other Mastodon servers – and with anyone on other social apps powered by the same set of rules as Mastodon uses (the ActivityPub protocol).'
|
||||||
|
tagName='p'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Overlay>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@ -46,7 +46,8 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const className = isRedesignEnabled() ? classes.badge : '';
|
const isRedesign = isRedesignEnabled();
|
||||||
|
const className = isRedesign ? classes.badge : '';
|
||||||
|
|
||||||
const domain = account.acct.includes('@')
|
const domain = account.acct.includes('@')
|
||||||
? account.acct.split('@')[1]
|
? account.acct.split('@')[1]
|
||||||
@ -68,7 +69,7 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
key={role.id}
|
key={role.id}
|
||||||
label={role.name}
|
label={role.name}
|
||||||
className={className}
|
className={className}
|
||||||
domain={isRedesignEnabled() ? `(${domain})` : domain}
|
domain={isRedesign ? `(${domain})` : domain}
|
||||||
roleId={role.id}
|
roleId={role.id}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
@ -81,7 +82,7 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
if (account.group) {
|
if (account.group) {
|
||||||
badges.push(<GroupBadge key='group-badge' className={className} />);
|
badges.push(<GroupBadge key='group-badge' className={className} />);
|
||||||
}
|
}
|
||||||
if (isRedesignEnabled() && relationship) {
|
if (isRedesign && relationship) {
|
||||||
if (relationship.blocking) {
|
if (relationship.blocking) {
|
||||||
badges.push(
|
badges.push(
|
||||||
<BlockedBadge
|
<BlockedBadge
|
||||||
@ -89,7 +90,8 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
className={classNames(className, classes.badgeBlocked)}
|
className={classNames(className, classes.badgeBlocked)}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
} else if (relationship.domain_blocking) {
|
}
|
||||||
|
if (relationship.domain_blocking) {
|
||||||
badges.push(
|
badges.push(
|
||||||
<BlockedBadge
|
<BlockedBadge
|
||||||
key='domain-blocking'
|
key='domain-blocking'
|
||||||
@ -103,7 +105,8 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
}
|
}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
} else if (relationship.muting) {
|
}
|
||||||
|
if (relationship.muting) {
|
||||||
badges.push(
|
badges.push(
|
||||||
<MutedBadge
|
<MutedBadge
|
||||||
key='muted-badge'
|
key='muted-badge'
|
||||||
|
|||||||
@ -16,6 +16,8 @@ import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react
|
|||||||
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
|
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
|
||||||
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
|
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
|
||||||
|
|
||||||
|
import { isRedesignEnabled } from '../common';
|
||||||
|
|
||||||
import { AccountMenu } from './menu';
|
import { AccountMenu } from './menu';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
@ -35,12 +37,14 @@ interface AccountButtonsProps {
|
|||||||
accountId: string;
|
accountId: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
noShare?: boolean;
|
noShare?: boolean;
|
||||||
|
forceMenu?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AccountButtons: FC<AccountButtonsProps> = ({
|
export const AccountButtons: FC<AccountButtonsProps> = ({
|
||||||
accountId,
|
accountId,
|
||||||
className,
|
className,
|
||||||
noShare,
|
noShare,
|
||||||
|
forceMenu,
|
||||||
}) => {
|
}) => {
|
||||||
const hidden = useAppSelector((state) => getAccountHidden(state, accountId));
|
const hidden = useAppSelector((state) => getAccountHidden(state, accountId));
|
||||||
const me = useAppSelector((state) => state.meta.get('me') as string);
|
const me = useAppSelector((state) => state.meta.get('me') as string);
|
||||||
@ -50,7 +54,7 @@ export const AccountButtons: FC<AccountButtonsProps> = ({
|
|||||||
{!hidden && (
|
{!hidden && (
|
||||||
<AccountButtonsOther accountId={accountId} noShare={noShare} />
|
<AccountButtonsOther accountId={accountId} noShare={noShare} />
|
||||||
)}
|
)}
|
||||||
{accountId !== me && <AccountMenu accountId={accountId} />}
|
{(accountId !== me || forceMenu) && <AccountMenu accountId={accountId} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -93,6 +97,7 @@ const AccountButtonsOther: FC<
|
|||||||
accountId={accountId}
|
accountId={accountId}
|
||||||
className='account__header__follow-button'
|
className='account__header__follow-button'
|
||||||
labelLength='long'
|
labelLength='long'
|
||||||
|
withUnmute={!isRedesignEnabled()}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isFollowing && (
|
{isFollowing && (
|
||||||
|
|||||||
@ -37,7 +37,6 @@ import {
|
|||||||
import type { AppDispatch } from '@/flavours/glitch/store';
|
import type { AppDispatch } from '@/flavours/glitch/store';
|
||||||
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
||||||
import BlockIcon from '@/material-icons/400-24px/block.svg?react';
|
import BlockIcon from '@/material-icons/400-24px/block.svg?react';
|
||||||
import EditIcon from '@/material-icons/400-24px/edit_square.svg?react';
|
|
||||||
import LinkIcon from '@/material-icons/400-24px/link_2.svg?react';
|
import LinkIcon from '@/material-icons/400-24px/link_2.svg?react';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react';
|
import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react';
|
||||||
@ -54,6 +53,10 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
const relationship = useAppSelector((state) =>
|
const relationship = useAppSelector((state) =>
|
||||||
state.relationships.get(accountId),
|
state.relationships.get(accountId),
|
||||||
);
|
);
|
||||||
|
const currentAccountId = useAppSelector(
|
||||||
|
(state) => state.meta.get('me') as string,
|
||||||
|
);
|
||||||
|
const isMe = currentAccountId === accountId;
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const menuItems = useMemo(() => {
|
const menuItems = useMemo(() => {
|
||||||
@ -64,7 +67,7 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
if (isRedesignEnabled()) {
|
if (isRedesignEnabled()) {
|
||||||
return redesignMenuItems({
|
return redesignMenuItems({
|
||||||
account,
|
account,
|
||||||
signedIn,
|
signedIn: !isMe && signedIn,
|
||||||
permissions,
|
permissions,
|
||||||
intl,
|
intl,
|
||||||
relationship,
|
relationship,
|
||||||
@ -79,7 +82,7 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
relationship,
|
relationship,
|
||||||
dispatch,
|
dispatch,
|
||||||
});
|
});
|
||||||
}, [account, signedIn, permissions, intl, relationship, dispatch]);
|
}, [account, signedIn, isMe, permissions, intl, relationship, dispatch]);
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
disabled={menuItems.length === 0}
|
disabled={menuItems.length === 0}
|
||||||
@ -446,6 +449,10 @@ const redesignMessages = defineMessages({
|
|||||||
defaultMessage: 'Copied account link to clipboard',
|
defaultMessage: 'Copied account link to clipboard',
|
||||||
},
|
},
|
||||||
mention: { id: 'account.menu.mention', defaultMessage: 'Mention' },
|
mention: { id: 'account.menu.mention', defaultMessage: 'Mention' },
|
||||||
|
noteDescription: {
|
||||||
|
id: 'account.menu.note.description',
|
||||||
|
defaultMessage: 'Visible only to you',
|
||||||
|
},
|
||||||
direct: {
|
direct: {
|
||||||
id: 'account.menu.direct',
|
id: 'account.menu.direct',
|
||||||
defaultMessage: 'Privately mention',
|
defaultMessage: 'Privately mention',
|
||||||
@ -516,22 +523,30 @@ function redesignMenuItems({
|
|||||||
icon: ShareIcon,
|
icon: ShareIcon,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
items.push(
|
items.push({
|
||||||
{
|
text: intl.formatMessage(redesignMessages.copy),
|
||||||
text: intl.formatMessage(redesignMessages.copy),
|
action: () => {
|
||||||
action: () => {
|
void navigator.clipboard.writeText(account.url);
|
||||||
void navigator.clipboard.writeText(account.url);
|
dispatch(showAlert({ message: redesignMessages.copied }));
|
||||||
dispatch(showAlert({ message: redesignMessages.copied }));
|
|
||||||
},
|
|
||||||
icon: LinkIcon,
|
|
||||||
},
|
},
|
||||||
null,
|
icon: LinkIcon,
|
||||||
);
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open on remote page.
|
||||||
|
if (isRemote) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(redesignMessages.openOriginalPage, {
|
||||||
|
domain: remoteDomain,
|
||||||
|
}),
|
||||||
|
href: account.url,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mention and direct message options
|
// Mention and direct message options
|
||||||
if (signedIn && !account.suspended) {
|
if (signedIn && !account.suspended) {
|
||||||
items.push(
|
items.push(
|
||||||
|
null,
|
||||||
{
|
{
|
||||||
text: intl.formatMessage(redesignMessages.mention),
|
text: intl.formatMessage(redesignMessages.mention),
|
||||||
action: () => {
|
action: () => {
|
||||||
@ -546,36 +561,6 @@ function redesignMenuItems({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
{
|
|
||||||
text: intl.formatMessage(
|
|
||||||
relationship?.note ? messages.editNote : messages.addNote,
|
|
||||||
),
|
|
||||||
action: () => {
|
|
||||||
dispatch(
|
|
||||||
openModal({
|
|
||||||
modalType: 'ACCOUNT_NOTE',
|
|
||||||
modalProps: {
|
|
||||||
accountId: account.id,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: EditIcon,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open on remote page.
|
|
||||||
if (isRemote) {
|
|
||||||
items.push(
|
|
||||||
{
|
|
||||||
text: intl.formatMessage(redesignMessages.openOriginalPage, {
|
|
||||||
domain: remoteDomain,
|
|
||||||
}),
|
|
||||||
href: account.url,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,59 +596,78 @@ function redesignMenuItems({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Timeline options
|
items.push(
|
||||||
if (!relationship.muting) {
|
{
|
||||||
items.push(
|
text: intl.formatMessage(
|
||||||
{
|
relationship?.note ? messages.editNote : messages.addNote,
|
||||||
text: intl.formatMessage(
|
),
|
||||||
relationship.showing_reblogs
|
description: intl.formatMessage(redesignMessages.noteDescription),
|
||||||
? redesignMessages.hideReblogs
|
action: () => {
|
||||||
: redesignMessages.showReblogs,
|
dispatch(
|
||||||
),
|
openModal({
|
||||||
action: () => {
|
modalType: 'ACCOUNT_NOTE',
|
||||||
dispatch(
|
modalProps: {
|
||||||
followAccount(account.id, {
|
accountId: account.id,
|
||||||
reblogs: !relationship.showing_reblogs,
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
null,
|
||||||
text: intl.formatMessage(messages.languages),
|
);
|
||||||
action: () => {
|
|
||||||
dispatch(
|
|
||||||
openModal({
|
|
||||||
modalType: 'SUBSCRIBED_LANGUAGES',
|
|
||||||
modalProps: {
|
|
||||||
accountId: account.id,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Timeline options
|
||||||
|
if (relationship && !relationship.muting) {
|
||||||
items.push(
|
items.push(
|
||||||
{
|
{
|
||||||
text: intl.formatMessage(
|
text: intl.formatMessage(
|
||||||
relationship.muting ? redesignMessages.unmute : redesignMessages.mute,
|
relationship.showing_reblogs
|
||||||
|
? redesignMessages.hideReblogs
|
||||||
|
: redesignMessages.showReblogs,
|
||||||
),
|
),
|
||||||
action: () => {
|
action: () => {
|
||||||
if (relationship.muting) {
|
dispatch(
|
||||||
dispatch(unmuteAccount(account.id));
|
followAccount(account.id, {
|
||||||
} else {
|
reblogs: !relationship.showing_reblogs,
|
||||||
dispatch(initMuteModal(account));
|
}),
|
||||||
}
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(messages.languages),
|
||||||
|
action: () => {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'SUBSCRIBED_LANGUAGES',
|
||||||
|
modalProps: {
|
||||||
|
accountId: account.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items.push(
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(
|
||||||
|
relationship?.muting ? redesignMessages.unmute : redesignMessages.mute,
|
||||||
|
),
|
||||||
|
action: () => {
|
||||||
|
if (relationship?.muting) {
|
||||||
|
dispatch(unmuteAccount(account.id));
|
||||||
|
} else {
|
||||||
|
dispatch(initMuteModal(account));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
if (relationship?.followed_by) {
|
if (relationship?.followed_by) {
|
||||||
items.push({
|
items.push({
|
||||||
text: intl.formatMessage(redesignMessages.removeFollower),
|
text: intl.formatMessage(redesignMessages.removeFollower),
|
||||||
@ -725,7 +729,7 @@ function redesignMenuItems({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (remoteDomain) {
|
if (remoteDomain) {
|
||||||
items.push({
|
items.push(null, {
|
||||||
text: intl.formatMessage(
|
text: intl.formatMessage(
|
||||||
relationship?.domain_blocking
|
relationship?.domain_blocking
|
||||||
? redesignMessages.domainUnblock
|
? redesignMessages.domainUnblock
|
||||||
|
|||||||
@ -63,7 +63,7 @@ export const AccountNote: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{relationship.note}
|
<div className={classes.noteContent}>{relationship.note}</div>
|
||||||
</Callout>
|
</Callout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -70,24 +70,22 @@ export const AccountNumberFields: FC<{ accountId: string }> = ({
|
|||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
{isRedesignEnabled() && (
|
{isRedesignEnabled() && (
|
||||||
<NavLink exact to={`/@${account.acct}`}>
|
<FormattedMessage
|
||||||
<FormattedMessage
|
id='account.joined_long'
|
||||||
id='account.joined_long'
|
defaultMessage='Joined on {date}'
|
||||||
defaultMessage='Joined on {date}'
|
values={{
|
||||||
values={{
|
date: (
|
||||||
date: (
|
<strong>
|
||||||
<strong>
|
<FormattedDateWrapper
|
||||||
<FormattedDateWrapper
|
value={account.created_at}
|
||||||
value={account.created_at}
|
year='numeric'
|
||||||
year='numeric'
|
month='short'
|
||||||
month='short'
|
day='2-digit'
|
||||||
day='2-digit'
|
/>
|
||||||
/>
|
</strong>
|
||||||
</strong>
|
),
|
||||||
),
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
</NavLink>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,9 +1,24 @@
|
|||||||
|
.header {
|
||||||
|
height: 120px;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
|
||||||
|
@container (width >= 500px) {
|
||||||
|
height: 160px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.barWrapper {
|
.barWrapper {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatarWrapper {
|
||||||
|
margin-top: -64px;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.nameWrapper {
|
.nameWrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,6 +27,10 @@
|
|||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
white-space: initial;
|
white-space: initial;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
|
|
||||||
|
> h1 {
|
||||||
|
white-space: initial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.username {
|
.username {
|
||||||
@ -23,15 +42,13 @@
|
|||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.domainPill {
|
.handleHelpButton {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
text-decoration: underline;
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
font-weight: initial;
|
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
@ -43,12 +60,53 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:global(.active) {
|
&:focus {
|
||||||
background: none;
|
|
||||||
color: var(--color-text-brand-soft);
|
color: var(--color-text-brand-soft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.handleHelp {
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: var(--dropdown-shadow);
|
||||||
|
max-width: 400px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
> h3 {
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
> ol {
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
background: var(--color-bg-brand-softer);
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$button-breakpoint: 420px;
|
$button-breakpoint: 420px;
|
||||||
$button-fallback-breakpoint: #{$button-breakpoint} + 55px;
|
$button-fallback-breakpoint: #{$button-breakpoint} + 55px;
|
||||||
|
|
||||||
@ -66,7 +124,9 @@ $button-fallback-breakpoint: #{$button-breakpoint} + 55px;
|
|||||||
|
|
||||||
.buttonsMobile {
|
.buttonsMobile {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 55px; // Height of bottom nav bar.
|
bottom: var(--mobile-bottom-nav-height);
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin: 0 -20px;
|
||||||
|
|
||||||
@container (width >= #{$button-breakpoint}) {
|
@container (width >= #{$button-breakpoint}) {
|
||||||
display: none;
|
display: none;
|
||||||
@ -77,13 +137,16 @@ $button-fallback-breakpoint: #{$button-breakpoint} + 55px;
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Multi-column layout
|
||||||
|
@media (width >= #{$button-breakpoint}) {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttonsMobileIsStuck {
|
.buttonsMobileIsStuck {
|
||||||
padding: 12px 16px;
|
|
||||||
background-color: var(--color-bg-primary);
|
background-color: var(--color-bg-primary);
|
||||||
border-top: 1px solid var(--color-border-primary);
|
border-top: 1px solid var(--color-border-primary);
|
||||||
margin: 0 -20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
@ -116,6 +179,10 @@ svg.badgeIcon {
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.noteContent {
|
||||||
|
white-space-collapse: preserve-breaks;
|
||||||
|
}
|
||||||
|
|
||||||
.noteEditButton {
|
.noteEditButton {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
||||||
@ -237,6 +304,11 @@ svg.badgeIcon {
|
|||||||
a {
|
a {
|
||||||
font-weight: unset;
|
font-weight: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modalCloseButton {
|
.modalCloseButton {
|
||||||
|
|||||||
@ -100,7 +100,7 @@
|
|||||||
|
|
||||||
.pinnedStatusHeader {
|
.pinnedStatusHeader {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: max-content auto;
|
grid-template-columns: 1fr auto;
|
||||||
grid-template-rows: 1fr 1fr;
|
grid-template-rows: 1fr 1fr;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
|
||||||
|
|||||||
45
app/javascript/flavours/glitch/hooks/useVisibility.ts
Normal file
45
app/javascript/flavours/glitch/hooks/useVisibility.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import type { RefCallback } from 'react';
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
export function useVisibility({
|
||||||
|
observerOptions,
|
||||||
|
}: {
|
||||||
|
observerOptions?: IntersectionObserverInit;
|
||||||
|
} = {}) {
|
||||||
|
const [isIntersecting, setIsIntersecting] = useState(false);
|
||||||
|
const handleIntersect: IntersectionObserverCallback = useCallback(
|
||||||
|
(entries) => {
|
||||||
|
const entry = entries.at(0);
|
||||||
|
if (!entry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsIntersecting(entry.isIntersecting);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const observer = useMemo(
|
||||||
|
() => new IntersectionObserver(handleIntersect, observerOptions),
|
||||||
|
[handleIntersect, observerOptions],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleObserverRef: RefCallback<HTMLElement> = useCallback(
|
||||||
|
(node) => {
|
||||||
|
if (node) {
|
||||||
|
observer.observe(node);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[observer],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
};
|
||||||
|
}, [observer]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isIntersecting,
|
||||||
|
observedRef: handleObserverRef,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -2934,6 +2934,7 @@ a.account__display-name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-subtitle {
|
&-subtitle {
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2976,6 +2977,10 @@ a.account__display-name {
|
|||||||
outline: 0;
|
outline: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button:is(:disabled, [aria-disabled='true']) &-subtitle {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.reblog-menu-item {
|
.reblog-menu-item {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user