Profile redesign: Account notes (#37593)
This commit is contained in:
parent
c0616bcab6
commit
34f0482ad4
@ -36,6 +36,7 @@ export interface CalloutProps {
|
|||||||
secondaryLabel?: string;
|
secondaryLabel?: string;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
id?: string;
|
id?: string;
|
||||||
|
extraContent?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const variantClasses = {
|
const variantClasses = {
|
||||||
@ -59,6 +60,7 @@ export const Callout: FC<CalloutProps> = ({
|
|||||||
onSecondary: secondaryAction,
|
onSecondary: secondaryAction,
|
||||||
secondaryLabel,
|
secondaryLabel,
|
||||||
onClose,
|
onClose,
|
||||||
|
extraContent,
|
||||||
id,
|
id,
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@ -105,6 +107,8 @@ export const Callout: FC<CalloutProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{extraContent}
|
||||||
|
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon='close'
|
icon='close'
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
|
margin-top: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@ -28,16 +29,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon + .content,
|
|
||||||
.wrapper:has(.close) .content {
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +59,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.action {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.close {
|
.close {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import { AccountHeaderFields } from './fields';
|
|||||||
import { AccountInfo } from './info';
|
import { AccountInfo } from './info';
|
||||||
import { MemorialNote } from './memorial_note';
|
import { MemorialNote } from './memorial_note';
|
||||||
import { MovedNote } from './moved_note';
|
import { MovedNote } from './moved_note';
|
||||||
|
import { AccountNote as AccountNoteRedesign } from './note';
|
||||||
import { AccountNumberFields } from './number_fields';
|
import { AccountNumberFields } from './number_fields';
|
||||||
import redesignClasses from './redesign.module.scss';
|
import redesignClasses from './redesign.module.scss';
|
||||||
import { AccountTabs } from './tabs';
|
import { AccountTabs } from './tabs';
|
||||||
@ -161,7 +162,9 @@ export const AccountHeader: React.FC<{
|
|||||||
{!suspendedOrHidden && (
|
{!suspendedOrHidden && (
|
||||||
<div className='account__header__extra'>
|
<div className='account__header__extra'>
|
||||||
<div className='account__header__bio'>
|
<div className='account__header__bio'>
|
||||||
{me && account.id !== me && (
|
{me && account.id !== me && isRedesignEnabled() ? (
|
||||||
|
<AccountNoteRedesign accountId={accountId} />
|
||||||
|
) : (
|
||||||
<AccountNote accountId={accountId} />
|
<AccountNote accountId={accountId} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,8 @@ import {
|
|||||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
|
|
||||||
|
import { isRedesignEnabled } from '../common';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||||
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
|
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
|
||||||
@ -55,6 +57,14 @@ const messages = defineMessages({
|
|||||||
id: 'account.show_reblogs',
|
id: 'account.show_reblogs',
|
||||||
defaultMessage: 'Show boosts from @{name}',
|
defaultMessage: 'Show boosts from @{name}',
|
||||||
},
|
},
|
||||||
|
addNote: {
|
||||||
|
id: 'account.add_note',
|
||||||
|
defaultMessage: 'Add a personal note',
|
||||||
|
},
|
||||||
|
editNote: {
|
||||||
|
id: 'account.edit_note',
|
||||||
|
defaultMessage: 'Edit personal note',
|
||||||
|
},
|
||||||
endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' },
|
endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' },
|
||||||
unendorse: {
|
unendorse: {
|
||||||
id: 'account.unendorse',
|
id: 'account.unendorse',
|
||||||
@ -187,7 +197,30 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
});
|
});
|
||||||
arr.push(null);
|
arr.push(null);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRedesignEnabled()) {
|
||||||
|
arr.push({
|
||||||
|
text: intl.formatMessage(
|
||||||
|
relationship?.note ? messages.editNote : messages.addNote,
|
||||||
|
),
|
||||||
|
action: () => {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'ACCOUNT_NOTE',
|
||||||
|
modalProps: {
|
||||||
|
accountId: account.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!relationship?.following) {
|
||||||
|
arr.push(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relationship?.following) {
|
||||||
arr.push({
|
arr.push({
|
||||||
text: intl.formatMessage(
|
text: intl.formatMessage(
|
||||||
relationship.endorsed ? messages.unendorse : messages.endorse,
|
relationship.endorsed ? messages.unendorse : messages.endorse,
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { fetchRelationships } from '@/mastodon/actions/accounts';
|
||||||
|
import { openModal } from '@/mastodon/actions/modal';
|
||||||
|
import { Callout } from '@/mastodon/components/callout';
|
||||||
|
import { IconButton } from '@/mastodon/components/icon_button';
|
||||||
|
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||||
|
import EditIcon from '@/material-icons/400-24px/edit_square.svg?react';
|
||||||
|
|
||||||
|
import classes from './redesign.module.scss';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: {
|
||||||
|
id: 'account.note.title',
|
||||||
|
defaultMessage: 'Personal note (visible only to you)',
|
||||||
|
},
|
||||||
|
editButton: {
|
||||||
|
id: 'account.note.edit_button',
|
||||||
|
defaultMessage: 'Edit',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AccountNote: FC<{ accountId: string }> = ({ accountId }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const relationship = useAppSelector((state) =>
|
||||||
|
state.relationships.get(accountId),
|
||||||
|
);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
useEffect(() => {
|
||||||
|
if (!relationship) {
|
||||||
|
dispatch(fetchRelationships([accountId]));
|
||||||
|
}
|
||||||
|
}, [accountId, dispatch, relationship]);
|
||||||
|
|
||||||
|
const handleEdit = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'ACCOUNT_NOTE',
|
||||||
|
modalProps: { accountId },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}, [accountId, dispatch]);
|
||||||
|
|
||||||
|
if (!relationship?.note) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Callout
|
||||||
|
icon={false}
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
className={classes.note}
|
||||||
|
extraContent={
|
||||||
|
<IconButton
|
||||||
|
icon='edit'
|
||||||
|
iconComponent={EditIcon}
|
||||||
|
title={intl.formatMessage(messages.editButton)}
|
||||||
|
className={classes.noteEditButton}
|
||||||
|
onClick={handleEdit}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{relationship.note}
|
||||||
|
</Callout>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -61,6 +61,19 @@ svg.badgeIcon {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noteEditButton {
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.fieldList {
|
.fieldList {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
.noteCallout {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noteInput {
|
||||||
|
min-height: 70px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
appearance: none;
|
||||||
|
resize: none;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noteInput:focus-visible {
|
||||||
|
outline: var(--outline-focus-default);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
@ -0,0 +1,160 @@
|
|||||||
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import type { ChangeEventHandler, FC } from 'react';
|
||||||
|
|
||||||
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { submitAccountNote } from '@/mastodon/actions/account_notes';
|
||||||
|
import { fetchRelationships } from '@/mastodon/actions/accounts';
|
||||||
|
import { Callout } from '@/mastodon/components/callout';
|
||||||
|
import { TextAreaField } from '@/mastodon/components/form_fields';
|
||||||
|
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
|
||||||
|
import type { Relationship } from '@/mastodon/models/relationship';
|
||||||
|
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||||
|
|
||||||
|
import { ConfirmationModal } from '../../ui/components/confirmation_modals';
|
||||||
|
|
||||||
|
import classes from './modals.module.css';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
newTitle: {
|
||||||
|
id: 'account.node_modal.title',
|
||||||
|
defaultMessage: 'Add a personal note',
|
||||||
|
},
|
||||||
|
editTitle: {
|
||||||
|
id: 'account.node_modal.edit_title',
|
||||||
|
defaultMessage: 'Edit personal note',
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
id: 'account.node_modal.save',
|
||||||
|
defaultMessage: 'Save',
|
||||||
|
},
|
||||||
|
fieldLabel: {
|
||||||
|
id: 'account.node_modal.field_label',
|
||||||
|
defaultMessage: 'Personal Note',
|
||||||
|
},
|
||||||
|
errorUnknown: {
|
||||||
|
id: 'account.node_modal.error_unknown',
|
||||||
|
defaultMessage: 'Could not save the note',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AccountNoteModal: FC<{
|
||||||
|
accountId: string;
|
||||||
|
onClose: () => void;
|
||||||
|
}> = ({ accountId, onClose }) => {
|
||||||
|
const relationship = useAppSelector((state) =>
|
||||||
|
state.relationships.get(accountId),
|
||||||
|
);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
useEffect(() => {
|
||||||
|
if (!relationship) {
|
||||||
|
dispatch(fetchRelationships([accountId]));
|
||||||
|
}
|
||||||
|
}, [accountId, dispatch, relationship]);
|
||||||
|
|
||||||
|
if (!relationship) {
|
||||||
|
return <LoadingIndicator />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InnerNodeModal
|
||||||
|
relationship={relationship}
|
||||||
|
accountId={accountId}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const InnerNodeModal: FC<{
|
||||||
|
relationship: Relationship;
|
||||||
|
accountId: string;
|
||||||
|
onClose: () => void;
|
||||||
|
}> = ({ relationship, accountId, onClose }) => {
|
||||||
|
// Set up the state.
|
||||||
|
const initialContents = relationship.note;
|
||||||
|
const [note, setNote] = useState(initialContents);
|
||||||
|
const [errorText, setErrorText] = useState('');
|
||||||
|
const [state, setState] = useState<'idle' | 'saving' | 'error'>('idle');
|
||||||
|
const isDirty = note !== initialContents;
|
||||||
|
|
||||||
|
const handleChange: ChangeEventHandler<HTMLTextAreaElement> = useCallback(
|
||||||
|
(e) => {
|
||||||
|
if (state !== 'saving') {
|
||||||
|
setNote(e.target.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[state],
|
||||||
|
);
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
// Create an abort controller to cancel the request if the modal is closed.
|
||||||
|
const abortController = useRef(new AbortController());
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const handleSave = useCallback(() => {
|
||||||
|
if (state === 'saving' || !isDirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState('saving');
|
||||||
|
dispatch(
|
||||||
|
submitAccountNote(
|
||||||
|
{ accountId, note },
|
||||||
|
{ signal: abortController.current.signal },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
setState('idle');
|
||||||
|
onClose();
|
||||||
|
})
|
||||||
|
.catch((err: unknown) => {
|
||||||
|
setState('error');
|
||||||
|
if (err instanceof Error) {
|
||||||
|
setErrorText(err.message);
|
||||||
|
} else {
|
||||||
|
setErrorText(intl.formatMessage(messages.errorUnknown));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [accountId, dispatch, intl, isDirty, note, onClose, state]);
|
||||||
|
|
||||||
|
const handleCancel = useCallback(() => {
|
||||||
|
abortController.current.abort();
|
||||||
|
onClose();
|
||||||
|
}, [onClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmationModal
|
||||||
|
title={
|
||||||
|
initialContents
|
||||||
|
? intl.formatMessage(messages.editTitle)
|
||||||
|
: intl.formatMessage(messages.newTitle)
|
||||||
|
}
|
||||||
|
extraContent={
|
||||||
|
<>
|
||||||
|
<Callout className={classes.noteCallout}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.node_modal.callout'
|
||||||
|
defaultMessage='Personal notes are visible only to you.'
|
||||||
|
/>
|
||||||
|
</Callout>
|
||||||
|
<TextAreaField
|
||||||
|
value={note}
|
||||||
|
onChange={handleChange}
|
||||||
|
label={intl.formatMessage(messages.fieldLabel)}
|
||||||
|
className={classes.noteInput}
|
||||||
|
hasError={state === 'error'}
|
||||||
|
hint={errorText}
|
||||||
|
// eslint-disable-next-line jsx-a11y/no-autofocus -- We want to focus here as it's a modal.
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
onClose={handleCancel}
|
||||||
|
confirm={intl.formatMessage(messages.save)}
|
||||||
|
onConfirm={handleSave}
|
||||||
|
updating={state === 'saving'}
|
||||||
|
disabled={!isDirty}
|
||||||
|
closeWhenConfirm={false}
|
||||||
|
noFocusButton
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -19,6 +19,9 @@ export const ConfirmationModal: React.FC<
|
|||||||
onConfirm: () => void;
|
onConfirm: () => void;
|
||||||
closeWhenConfirm?: boolean;
|
closeWhenConfirm?: boolean;
|
||||||
extraContent?: React.ReactNode;
|
extraContent?: React.ReactNode;
|
||||||
|
updating?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
noFocusButton?: boolean;
|
||||||
} & BaseConfirmationModalProps
|
} & BaseConfirmationModalProps
|
||||||
> = ({
|
> = ({
|
||||||
title,
|
title,
|
||||||
@ -31,6 +34,9 @@ export const ConfirmationModal: React.FC<
|
|||||||
onSecondary,
|
onSecondary,
|
||||||
closeWhenConfirm = true,
|
closeWhenConfirm = true,
|
||||||
extraContent,
|
extraContent,
|
||||||
|
updating,
|
||||||
|
disabled,
|
||||||
|
noFocusButton = false,
|
||||||
}) => {
|
}) => {
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
if (closeWhenConfirm) {
|
if (closeWhenConfirm) {
|
||||||
@ -74,16 +80,23 @@ export const ConfirmationModal: React.FC<
|
|||||||
onClick={handleSecondary}
|
onClick={handleSecondary}
|
||||||
className='link-button'
|
className='link-button'
|
||||||
type='button'
|
type='button'
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{secondary}
|
{secondary}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* eslint-disable-next-line jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}
|
{/* eslint-disable jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}
|
||||||
<Button onClick={handleClick} autoFocus>
|
<Button
|
||||||
|
onClick={handleClick}
|
||||||
|
loading={updating}
|
||||||
|
disabled={disabled}
|
||||||
|
autoFocus={!noFocusButton}
|
||||||
|
>
|
||||||
{confirm}
|
{confirm}
|
||||||
</Button>
|
</Button>
|
||||||
|
{/* eslint-enable */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -85,6 +85,7 @@ export const MODAL_COMPONENTS = {
|
|||||||
'IGNORE_NOTIFICATIONS': IgnoreNotificationsModal,
|
'IGNORE_NOTIFICATIONS': IgnoreNotificationsModal,
|
||||||
'ANNUAL_REPORT': AnnualReportModal,
|
'ANNUAL_REPORT': AnnualReportModal,
|
||||||
'COMPOSE_PRIVACY': () => Promise.resolve({ default: VisibilityModal }),
|
'COMPOSE_PRIVACY': () => Promise.resolve({ default: VisibilityModal }),
|
||||||
|
'ACCOUNT_NOTE': () => import('@/mastodon/features/account_timeline/modals/note_modal').then(module => ({ default: module.AccountNoteModal })),
|
||||||
'ACCOUNT_FIELDS': () => import('mastodon/features/account_timeline/components/fields_modal.tsx').then(module => ({ default: module.AccountFieldsModal })),
|
'ACCOUNT_FIELDS': () => import('mastodon/features/account_timeline/components/fields_modal.tsx').then(module => ({ default: module.AccountFieldsModal })),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
"about.powered_by": "Decentralized social media powered by {mastodon}",
|
"about.powered_by": "Decentralized social media powered by {mastodon}",
|
||||||
"about.rules": "Server rules",
|
"about.rules": "Server rules",
|
||||||
"account.account_note_header": "Personal note",
|
"account.account_note_header": "Personal note",
|
||||||
|
"account.add_note": "Add a personal note",
|
||||||
"account.add_or_remove_from_list": "Add or Remove from lists",
|
"account.add_or_remove_from_list": "Add or Remove from lists",
|
||||||
"account.badges.bot": "Automated",
|
"account.badges.bot": "Automated",
|
||||||
"account.badges.group": "Group",
|
"account.badges.group": "Group",
|
||||||
@ -27,6 +28,7 @@
|
|||||||
"account.direct": "Privately mention @{name}",
|
"account.direct": "Privately mention @{name}",
|
||||||
"account.disable_notifications": "Stop notifying me when @{name} posts",
|
"account.disable_notifications": "Stop notifying me when @{name} posts",
|
||||||
"account.domain_blocking": "Blocking domain",
|
"account.domain_blocking": "Blocking domain",
|
||||||
|
"account.edit_note": "Edit personal note",
|
||||||
"account.edit_profile": "Edit profile",
|
"account.edit_profile": "Edit profile",
|
||||||
"account.edit_profile_short": "Edit",
|
"account.edit_profile_short": "Edit",
|
||||||
"account.enable_notifications": "Notify me when @{name} posts",
|
"account.enable_notifications": "Notify me when @{name} posts",
|
||||||
@ -72,6 +74,14 @@
|
|||||||
"account.muting": "Muting",
|
"account.muting": "Muting",
|
||||||
"account.mutual": "You follow each other",
|
"account.mutual": "You follow each other",
|
||||||
"account.no_bio": "No description provided.",
|
"account.no_bio": "No description provided.",
|
||||||
|
"account.node_modal.callout": "Personal notes are visible only to you.",
|
||||||
|
"account.node_modal.edit_title": "Edit personal note",
|
||||||
|
"account.node_modal.error_unknown": "Could not save the note",
|
||||||
|
"account.node_modal.field_label": "Personal Note",
|
||||||
|
"account.node_modal.save": "Save",
|
||||||
|
"account.node_modal.title": "Add a personal note",
|
||||||
|
"account.note.edit_button": "Edit",
|
||||||
|
"account.note.title": "Personal note (visible only to you)",
|
||||||
"account.open_original_page": "Open original page",
|
"account.open_original_page": "Open original page",
|
||||||
"account.posts": "Posts",
|
"account.posts": "Posts",
|
||||||
"account.posts_with_replies": "Posts and replies",
|
"account.posts_with_replies": "Posts and replies",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user