nicolium: Allow displaying avatars next to mentions
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useAccount } from '@/queries/accounts/use-account';
|
||||
|
||||
import Avatar from '../ui/avatar';
|
||||
|
||||
import HoverAccountWrapper from './hover-account-wrapper';
|
||||
|
||||
interface IMentionWithAvatar {
|
||||
id: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
const MentionWithAvatar: React.FC<IMentionWithAvatar> = ({ id, username }) => {
|
||||
const { data: account } = useAccount(id);
|
||||
|
||||
return (
|
||||
<HoverAccountWrapper accountId={id} element='span' className='⁂-mention-with-avatar'>
|
||||
<Avatar size={16} src={account?.avatar || ''} alt={account?.avatar_description} />
|
||||
<span>@{username}</span>
|
||||
</HoverAccountWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export { MentionWithAvatar };
|
||||
@ -73,7 +73,7 @@ const DropdownMenuItem = ({ index, item, onClick, autoFocus, onSetTab }: IDropdo
|
||||
} else if (typeof item.action === 'function') {
|
||||
event.preventDefault();
|
||||
item.action(event);
|
||||
} else if (typeof item.onChange == 'function') {
|
||||
} else if (typeof item.onChange === 'function') {
|
||||
event.preventDefault();
|
||||
item.onChange(!item.checked);
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ import { makeEmojiMap } from '@/utils/normalizers';
|
||||
import Purify from '@/utils/url-purify';
|
||||
|
||||
import HoverAccountWrapper from '../accounts/hover-account-wrapper';
|
||||
import { MentionWithAvatar } from '../accounts/mention-with-avatar';
|
||||
import HashtagLink from '../hashtag-link';
|
||||
|
||||
import StatusMention from './status-mention';
|
||||
@ -131,6 +132,7 @@ interface IParsedContent {
|
||||
displayTargetHost?: boolean;
|
||||
greentext?: boolean;
|
||||
speakAsCat?: boolean;
|
||||
displayMentionAvatars?: boolean;
|
||||
}
|
||||
|
||||
// Adapted from Mastodon https://github.com/mastodon/mastodon/blob/main/app/javascript/mastodon/components/hashtag_bar.tsx
|
||||
@ -175,7 +177,15 @@ function parseContent(
|
||||
};
|
||||
|
||||
function parseContent(
|
||||
{ html, mentions, hasQuote, emojis, greentext = false, speakAsCat = false }: IParsedContent,
|
||||
{
|
||||
html,
|
||||
mentions,
|
||||
hasQuote,
|
||||
emojis,
|
||||
greentext = false,
|
||||
speakAsCat = false,
|
||||
displayMentionAvatars = false,
|
||||
}: IParsedContent,
|
||||
extractHashtags = false,
|
||||
) {
|
||||
if (html.length === 0) {
|
||||
@ -272,9 +282,13 @@ function parseContent(
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<HoverAccountWrapper accountId={mention.id} element='span'>
|
||||
@{mention.username}
|
||||
</HoverAccountWrapper>
|
||||
{displayMentionAvatars ? (
|
||||
<MentionWithAvatar id={mention.id} username={mention.username} />
|
||||
) : (
|
||||
<HoverAccountWrapper accountId={mention.id} element='span'>
|
||||
@{mention.username}
|
||||
</HoverAccountWrapper>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@ -371,13 +385,14 @@ function parseContent(
|
||||
|
||||
const ParsedContent: React.FC<IParsedContent> = React.memo(
|
||||
(props) => {
|
||||
const { urlPrivacy } = useSettings();
|
||||
const { urlPrivacy, displayMentionAvatars } = useSettings();
|
||||
|
||||
props = { ...props };
|
||||
|
||||
props.cleanUrls ??= urlPrivacy.clearLinksInContent;
|
||||
props.redirectUrls ??= urlPrivacy.redirectLinksMode !== 'off';
|
||||
props.displayTargetHost ??= urlPrivacy.displayTargetHost;
|
||||
props.displayMentionAvatars ??= displayMentionAvatars;
|
||||
|
||||
return parseContent(props, false);
|
||||
},
|
||||
|
||||
@ -77,7 +77,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(
|
||||
withMedia,
|
||||
compose = false,
|
||||
}) => {
|
||||
const { urlPrivacy, displaySpoilers, renderMfm } = useSettings();
|
||||
const { urlPrivacy, displaySpoilers, renderMfm, displayMentionAvatars } = useSettings();
|
||||
const { greentext } = useFrontendConfig();
|
||||
const { data: account } = useAccount(status.account_id);
|
||||
|
||||
@ -174,10 +174,11 @@ const StatusContent: React.FC<IStatusContent> = React.memo(
|
||||
displayTargetHost: urlPrivacy.displayTargetHost,
|
||||
greentext,
|
||||
speakAsCat: account?.speak_as_cat,
|
||||
displayMentionAvatars,
|
||||
},
|
||||
true,
|
||||
);
|
||||
}, [content, renderMfm, account?.speak_as_cat]);
|
||||
}, [content, renderMfm, account?.speak_as_cat, displayMentionAvatars]);
|
||||
|
||||
const spoilerText =
|
||||
status.spoiler_text_map && statusMeta.currentLanguage
|
||||
|
||||
@ -2,8 +2,10 @@ import React from 'react';
|
||||
|
||||
import { Link } from '@/components/link';
|
||||
import { useAccount } from '@/queries/accounts/use-account';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
import HoverAccountWrapper from '../accounts/hover-account-wrapper';
|
||||
import { MentionWithAvatar } from '../accounts/mention-with-avatar';
|
||||
|
||||
interface IStatusMention {
|
||||
accountId: string;
|
||||
@ -13,6 +15,8 @@ interface IStatusMention {
|
||||
const StatusMention: React.FC<IStatusMention> = ({ accountId, fallback }) => {
|
||||
const { data: account } = useAccount(accountId);
|
||||
|
||||
const { displayMentionAvatars } = useSettings();
|
||||
|
||||
if (!account)
|
||||
return (
|
||||
<HoverAccountWrapper accountId={accountId} element='span'>
|
||||
@ -29,9 +33,13 @@ const StatusMention: React.FC<IStatusMention> = ({ accountId, fallback }) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<HoverAccountWrapper accountId={accountId} element='span'>
|
||||
@{account.acct}
|
||||
</HoverAccountWrapper>
|
||||
{displayMentionAvatars ? (
|
||||
<MentionWithAvatar id={accountId} username={account.acct} />
|
||||
) : (
|
||||
<HoverAccountWrapper accountId={accountId} element='span'>
|
||||
@{account.acct}
|
||||
</HoverAccountWrapper>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
@ -724,6 +724,21 @@ const Preferences = () => {
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='preferences.fields.display_mention_avatars'
|
||||
defaultMessage='Show avatars next to mentions'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<SettingToggle
|
||||
settings={settings}
|
||||
settingPath={['displayMentionAvatars']}
|
||||
onChange={onToggleChange}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
label={
|
||||
<FormattedMessage
|
||||
|
||||
@ -12,6 +12,7 @@ import { useCurrentAccount } from '@/contexts/current-account-context';
|
||||
import Emojify from '@/features/emoji/emojify';
|
||||
import { useAcct } from '@/hooks/use-acct';
|
||||
import { useAccountScrobbleQuery } from '@/queries/accounts/account-scrobble';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
import { capitalize } from '@/utils/strings';
|
||||
|
||||
import { ProfileField } from '../../util/async-components';
|
||||
@ -51,6 +52,7 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
|
||||
const acct = useAcct(account);
|
||||
const me = useCurrentAccount();
|
||||
const ownAccount = account?.id === me;
|
||||
const { displayMentionAvatars } = useSettings();
|
||||
|
||||
const { data: scrobble } = useAccountScrobbleQuery(account?.id);
|
||||
|
||||
@ -222,6 +224,7 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
|
||||
html={account.note}
|
||||
emojis={account.emojis}
|
||||
speakAsCat={account.speak_as_cat}
|
||||
displayMentionAvatars={displayMentionAvatars}
|
||||
/>
|
||||
</Markup>
|
||||
)}
|
||||
|
||||
@ -1586,6 +1586,7 @@
|
||||
"preferences.fields.display_media.default": "Hide posts marked as sensitive",
|
||||
"preferences.fields.display_media.hide_all": "Always hide media posts",
|
||||
"preferences.fields.display_media.show_all": "Always show posts",
|
||||
"preferences.fields.display_mention_avatars": "Show avatars next to mentions",
|
||||
"preferences.fields.implicit_addressing_label": "Include mentions in post content when replying",
|
||||
"preferences.fields.interface_size": "Interface size",
|
||||
"preferences.fields.known_languages_label": "Languages you know",
|
||||
|
||||
@ -42,6 +42,10 @@ const messages = defineMessages({
|
||||
treeIndentView: { id: 'status.thread.tree_indent_view', defaultMessage: 'Tree (indented)' },
|
||||
linearView: { id: 'status.thread.linear_view', defaultMessage: 'Linear view' },
|
||||
expandAll: { id: 'status.thread.expand_all', defaultMessage: 'Expand all posts' },
|
||||
showAvatars: {
|
||||
id: 'preferences.fields.display_mention_avatars',
|
||||
defaultMessage: 'Show avatars next to mentions',
|
||||
},
|
||||
});
|
||||
|
||||
const StatusPage: React.FC = () => {
|
||||
@ -60,6 +64,7 @@ const StatusPage: React.FC = () => {
|
||||
const {
|
||||
displaySpoilers,
|
||||
threads: { displayMode },
|
||||
displayMentionAvatars,
|
||||
} = useSettings();
|
||||
|
||||
const handleRefresh = () => {
|
||||
@ -98,6 +103,15 @@ const StatusPage: React.FC = () => {
|
||||
},
|
||||
];
|
||||
|
||||
menu.push(null, {
|
||||
text: intl.formatMessage(messages.showAvatars),
|
||||
onChange: (checked) => {
|
||||
changeSetting(['displayMentionAvatars'], checked);
|
||||
},
|
||||
type: 'toggle',
|
||||
checked: displayMentionAvatars,
|
||||
});
|
||||
|
||||
if (!displaySpoilers && expandAllStatuses) {
|
||||
menu.push(null, {
|
||||
text: intl.formatMessage(messages.expandAll),
|
||||
@ -106,7 +120,7 @@ const StatusPage: React.FC = () => {
|
||||
});
|
||||
}
|
||||
return menu;
|
||||
}, [displayMode, expandAllStatuses]);
|
||||
}, [displayMode, expandAllStatuses, displayMentionAvatars]);
|
||||
|
||||
if (status?.event) {
|
||||
return (
|
||||
|
||||
@ -57,6 +57,7 @@ const settingsSchema = v.object({
|
||||
rememberTimelinePosition: v.fallback(v.boolean(), true),
|
||||
accountNicknames: v.fallback(v.record(v.string(), v.string()), {}),
|
||||
useSystemMediaControls: v.fallback(v.boolean(), false),
|
||||
displayMentionAvatars: v.fallback(v.boolean(), false),
|
||||
|
||||
theme: v.optional(
|
||||
coerceObject({
|
||||
|
||||
@ -245,3 +245,43 @@
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.⁂-mention-with-avatar {
|
||||
display: inline-flex;
|
||||
gap: 0.25rem;
|
||||
align-items: center;
|
||||
|
||||
padding: 0.125rem 0.5rem 0.125rem 0.25rem;
|
||||
border-radius: 9999px;
|
||||
|
||||
vertical-align: middle;
|
||||
|
||||
background: rgb(var(--color-gray-200));
|
||||
|
||||
.dark & {
|
||||
background: rgb(var(--color-primary-800));
|
||||
}
|
||||
|
||||
.black & {
|
||||
background: rgb(var(--color-gray-800));
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.⁂-avatar {
|
||||
overflow: hidden;
|
||||
border-radius: 9999px;
|
||||
|
||||
.text-lg & {
|
||||
width: 1.5rem !important;
|
||||
height: 1.5rem !important;
|
||||
}
|
||||
|
||||
&--placeholder {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user