nicolium: Allow assigning nicknames to users
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -4,7 +4,7 @@ import { defineMessages, FormattedDate, useIntl } from 'react-intl';
|
||||
import IconButton from '@/components/ui/icon-button';
|
||||
import { DatePicker } from '@/features/ui/util/async-components';
|
||||
import { useFeatures } from '@/hooks/use-features';
|
||||
import { useInstance } from '@/stores/instance';
|
||||
import { useInstance } from '@/hooks/use-instance';
|
||||
|
||||
const messages = defineMessages({
|
||||
birthdayPlaceholder: {
|
||||
|
||||
@ -415,39 +415,21 @@ const Status: React.FC<IStatus> = React.memo((props) => {
|
||||
/>
|
||||
);
|
||||
} else if (isReblog) {
|
||||
const accounts = status.accounts ?? [status.account];
|
||||
|
||||
const renderedAccounts = accounts.slice(0, 2).map(
|
||||
(account) =>
|
||||
!!account && (
|
||||
<Link
|
||||
key={account.acct}
|
||||
to='/@{$username}'
|
||||
params={{ username: account.acct }}
|
||||
className='hover:underline'
|
||||
>
|
||||
<bdi className='truncate'>
|
||||
<strong className='text-gray-800 dark:text-gray-200'>
|
||||
<Emojify text={account.display_name} emojis={account.emojis} />
|
||||
</strong>
|
||||
</bdi>
|
||||
</Link>
|
||||
),
|
||||
);
|
||||
|
||||
if (accounts.length > 2) {
|
||||
renderedAccounts.push(
|
||||
<FormattedMessage
|
||||
id='notification.more'
|
||||
defaultMessage='{count, plural, one {# other} other {# others}}'
|
||||
values={{ count: accounts.length - renderedAccounts.length }}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
const values = {
|
||||
name: <FormattedList type='conjunction' value={renderedAccounts} />,
|
||||
count: accounts.length,
|
||||
name: (
|
||||
<Link
|
||||
key={status.account.acct}
|
||||
to='/@{$username}'
|
||||
params={{ username: status.account.acct }}
|
||||
className='hover:underline'
|
||||
>
|
||||
<bdi className='truncate'>
|
||||
<strong className='text-gray-800 dark:text-gray-200'>
|
||||
<Emojify text={status.account.display_name} emojis={status.account.emojis} />
|
||||
</strong>
|
||||
</bdi>
|
||||
</Link>
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
@ -535,7 +517,7 @@ const Status: React.FC<IStatus> = React.memo((props) => {
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [status.accounts, group?.id]);
|
||||
}, [status.account, group?.id]);
|
||||
|
||||
if (!status) return null;
|
||||
|
||||
|
||||
@ -4,8 +4,10 @@ import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { initReport, ReportableEntities } from '@/actions/reports';
|
||||
import { changeSetting } from '@/actions/settings';
|
||||
import DropdownMenu, { type Menu } from '@/components/dropdown-menu';
|
||||
import IconButton from '@/components/ui/icon-button';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useClient } from '@/hooks/use-client';
|
||||
import { useFeatures } from '@/hooks/use-features';
|
||||
import { useOwnAccount } from '@/hooks/use-own-account';
|
||||
@ -91,6 +93,10 @@ const messages = defineMessages({
|
||||
id: 'account.load_activities.fail',
|
||||
defaultMessage: 'Failed to fetch latest posts',
|
||||
},
|
||||
nickname: { id: 'account.nickname.modal_header', defaultMessage: 'Set nickname for @{name}' },
|
||||
nicknamePlaceholder: { id: 'account.nickname.placeholder', defaultMessage: 'Enter a nickname' },
|
||||
nicknameSave: { id: 'account.nickname.save', defaultMessage: 'Save nickname' },
|
||||
nicknameSaved: { id: 'account.nickname.success', defaultMessage: 'Nickname saved' },
|
||||
note: { id: 'account_note.modal_header', defaultMessage: 'Edit note for @{name}' },
|
||||
notePlaceholder: { id: 'account_note.placeholder', defaultMessage: 'Add a note' },
|
||||
noteSaved: { id: 'account_note.success', defaultMessage: 'Note saved' },
|
||||
@ -105,6 +111,7 @@ interface IAccountMenu {
|
||||
|
||||
const AccountMenu: React.FC<IAccountMenu> = ({ account }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const { mentionCompose, directCompose } = useComposeActions();
|
||||
const client = useClient();
|
||||
|
||||
@ -212,6 +219,28 @@ const AccountMenu: React.FC<IAccountMenu> = ({ account }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const onEditNickname = () => {
|
||||
const currentNickname = settings.accountNicknames[account.id] ?? '';
|
||||
openModal('TEXT_FIELD', {
|
||||
heading: (
|
||||
<FormattedMessage
|
||||
id='account.nickname.modal_header'
|
||||
defaultMessage='Set nickname for @{name}'
|
||||
values={{ name: account.acct }}
|
||||
/>
|
||||
),
|
||||
placeholder: intl.formatMessage(messages.nicknamePlaceholder),
|
||||
confirm: <FormattedMessage id='account.nickname.save' defaultMessage='Save nickname' />,
|
||||
onConfirm: (value) => {
|
||||
const trimmed = value.trim();
|
||||
dispatch(changeSetting(['accountNicknames', account.id], trimmed || undefined));
|
||||
toast.success(messages.nicknameSaved);
|
||||
},
|
||||
text: currentNickname,
|
||||
singleLine: true,
|
||||
});
|
||||
};
|
||||
|
||||
const onReport = () => {
|
||||
initReport(ReportableEntities.ACCOUNT, account);
|
||||
};
|
||||
@ -461,6 +490,12 @@ const AccountMenu: React.FC<IAccountMenu> = ({ account }) => {
|
||||
});
|
||||
}
|
||||
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.nickname, { name: account.acct }),
|
||||
action: onEditNickname,
|
||||
icon: require('@phosphor-icons/core/regular/tag.svg'),
|
||||
});
|
||||
|
||||
menu.push(null);
|
||||
|
||||
if (features.removeFromFollowers && account.relationship?.followed_by) {
|
||||
|
||||
@ -33,10 +33,14 @@ const messages = defineMessages({
|
||||
deactivated: { id: 'account.deactivated', defaultMessage: 'Deactivated' },
|
||||
bot: { id: 'account.badges.bot', defaultMessage: 'Bot' },
|
||||
pronouns: { id: 'account.pronouns.with_label', defaultMessage: 'Pronouns: {pronouns}' },
|
||||
originalDisplayName: {
|
||||
id: 'account.original_display_name',
|
||||
defaultMessage: 'You have assigned a nickname to this user.',
|
||||
},
|
||||
});
|
||||
|
||||
interface IProfileInfoPanel {
|
||||
account?: Account;
|
||||
account?: Account & { original_display_name?: string };
|
||||
/** Username from URL params, in case the account isn't found. */
|
||||
username: string;
|
||||
}
|
||||
@ -177,6 +181,19 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
|
||||
)}
|
||||
</Text>
|
||||
|
||||
{account.original_display_name &&
|
||||
account.original_display_name !== account.display_name && (
|
||||
<Text
|
||||
theme='muted'
|
||||
truncate
|
||||
title={intl.formatMessage(messages.originalDisplayName)}
|
||||
>
|
||||
{'('}
|
||||
<Emojify text={account.original_display_name} emojis={account.emojis} />
|
||||
{')'}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{account.bot && <Badge slug='bot' title={intl.formatMessage(messages.bot)} />}
|
||||
|
||||
{badges.length > 0 && <div className='flex items-center gap-1'>{badges}</div>}
|
||||
|
||||
@ -20,6 +20,10 @@ const messages = defineMessages({
|
||||
defaultMessage:
|
||||
'This account privacy status is set to locked. The owner manually reviews who can follow them.',
|
||||
},
|
||||
originalDisplayName: {
|
||||
id: 'account.original_display_name',
|
||||
defaultMessage: 'You have assigned a nickname to this user.',
|
||||
},
|
||||
});
|
||||
|
||||
interface IUserPanel {
|
||||
@ -83,6 +87,19 @@ const UserPanel: React.FC<IUserPanel> = ({ accountId, action, badges, domain })
|
||||
<Emojify text={account.display_name} emojis={account.emojis} />
|
||||
</Text>
|
||||
|
||||
{account.original_display_name &&
|
||||
account.original_display_name !== account.display_name && (
|
||||
<Text
|
||||
theme='muted'
|
||||
truncate
|
||||
title={intl.formatMessage(messages.originalDisplayName)}
|
||||
>
|
||||
{'('}
|
||||
<Emojify text={account.original_display_name} emojis={account.emojis} />
|
||||
{')'}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{verified && <VerificationBadge />}
|
||||
|
||||
{badges && badges.length > 0 && (
|
||||
|
||||
@ -53,6 +53,11 @@
|
||||
"account.mute": "Mute @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.never_active": "Never",
|
||||
"account.nickname.modal_header": "Set nickname for @{name}",
|
||||
"account.nickname.placeholder": "Enter a nickname",
|
||||
"account.nickname.save": "Save nickname",
|
||||
"account.nickname.success": "Nickname saved",
|
||||
"account.original_display_name": "You have assigned a nickname to this user.",
|
||||
"account.posts": "Posts",
|
||||
"account.posts_with_replies": "Posts & replies",
|
||||
"account.profile": "Profile",
|
||||
|
||||
@ -69,7 +69,6 @@ const FrontendConfigEditor: React.FC = () => {
|
||||
const features = useFeatures();
|
||||
|
||||
const initialData = useAppSelector((state) => state.frontendConfig);
|
||||
console.log(initialData);
|
||||
const { mutate: updateConfig, isPending } = useUpdateAdminConfig();
|
||||
|
||||
const [data, setData] = useState(v.parse(frontendConfigSchema, initialData));
|
||||
|
||||
@ -7,6 +7,7 @@ import { useLoggedIn } from '@/hooks/use-logged-in';
|
||||
import { useCredentialAccount } from '@/queries/accounts/use-account-credentials';
|
||||
import { useRelationshipQuery } from '@/queries/accounts/use-relationship';
|
||||
import { queryKeys } from '@/queries/keys';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
import type { NicoliumResponse } from '@/api';
|
||||
|
||||
@ -30,6 +31,9 @@ const useAccount = (accountId?: string, withRelationship = false) => {
|
||||
const features = useFeatures();
|
||||
const { me } = useLoggedIn();
|
||||
const queryClient = useQueryClient();
|
||||
const { accountNicknames } = useSettings();
|
||||
|
||||
const nickname = accountNicknames[accountId ?? ''];
|
||||
|
||||
const accountQuery = useQuery({
|
||||
queryKey: queryKeys.accounts.show(accountId!),
|
||||
@ -63,19 +67,14 @@ const useAccount = (accountId?: string, withRelationship = false) => {
|
||||
const mergedRelationship = relationship ?? accountQuery.data.relationship;
|
||||
const mergedIsAdmin = credentialIsAdmin ?? accountQuery.data.is_admin;
|
||||
|
||||
if (
|
||||
mergedRelationship === accountQuery.data.relationship &&
|
||||
mergedIsAdmin === accountQuery.data.is_admin
|
||||
) {
|
||||
return accountQuery.data;
|
||||
}
|
||||
|
||||
return {
|
||||
...accountQuery.data,
|
||||
display_name: nickname ?? accountQuery.data.display_name,
|
||||
original_display_name: accountQuery.data.display_name,
|
||||
relationship: mergedRelationship,
|
||||
is_admin: mergedIsAdmin,
|
||||
};
|
||||
}, [accountQuery.data, relationship, credentialIsAdmin]);
|
||||
}, [accountQuery.data, relationship, credentialIsAdmin, nickname]);
|
||||
|
||||
return {
|
||||
...accountQuery,
|
||||
|
||||
@ -66,7 +66,6 @@ const useStatusQuery = (statusId?: string) => {
|
||||
data: {
|
||||
...statusQuery.data,
|
||||
account: account.data!,
|
||||
accounts: [account.data!],
|
||||
},
|
||||
};
|
||||
}, [statusQuery.data, account.data]) as unknown as UseQueryResult<NormalizedStatus>;
|
||||
|
||||
@ -55,6 +55,7 @@ const settingsSchema = v.object({
|
||||
storeSettingsInNotes: v.fallback(v.boolean(), false),
|
||||
composeInTimelines: v.fallback(v.boolean(), true),
|
||||
rememberTimelinePosition: v.fallback(v.boolean(), true),
|
||||
accountNicknames: v.fallback(v.record(v.string(), v.string()), {}),
|
||||
|
||||
theme: v.optional(
|
||||
coerceObject({
|
||||
|
||||
Reference in New Issue
Block a user