Make valid-i18n-keys rule strict and fix most exceptions
This commit is contained in:
parent
18a6da310f
commit
11cfcb4e32
36 changed files with 796 additions and 687 deletions
|
@ -14,9 +14,11 @@ const messagesCacheKey = hashSum.digest('hex');
|
|||
|
||||
function isI18nCall(node) {
|
||||
return (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'i18n'
|
||||
(node.type === 'CallExpression' &&
|
||||
node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'i18n') ||
|
||||
(node.callee.type === 'MemberExpression' &&
|
||||
node.callee.property.name === 'i18n')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -36,20 +38,7 @@ function valueToMessageKey(node) {
|
|||
if (isStringLiteral(node)) {
|
||||
return node.value;
|
||||
}
|
||||
|
||||
if (node.type !== 'TemplateLiteral') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.quasis.length === 1) {
|
||||
return node.quasis[0].value.cooked;
|
||||
}
|
||||
|
||||
const parts = node.quasis.map(element => {
|
||||
return element.value.cooked;
|
||||
});
|
||||
|
||||
return new RegExp(`^${parts.join('(.*)')}$`);
|
||||
return null;
|
||||
}
|
||||
|
||||
function getI18nCallMessageKey(node) {
|
||||
|
@ -80,24 +69,11 @@ function getIntlElementMessageKey(node) {
|
|||
|
||||
let value = idAttribute.value;
|
||||
|
||||
if (value.type === 'JSXExpressionContainer') {
|
||||
value = value.expression;
|
||||
}
|
||||
|
||||
return valueToMessageKey(value);
|
||||
}
|
||||
|
||||
function isValidMessageKey(key) {
|
||||
if (typeof key === 'string') {
|
||||
if (Object.hasOwn(messages, key)) {
|
||||
return true;
|
||||
}
|
||||
} else if (key instanceof RegExp) {
|
||||
if (messageKeys.some(k => key.test(k))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Object.hasOwn(messages, key);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -27,27 +27,68 @@ ruleTester.run('valid-i18n-keys', rule, {
|
|||
options: [{ messagesCacheKey }],
|
||||
},
|
||||
{
|
||||
code: 'i18n(`AddCaptionModal__${title}`)',
|
||||
code: `window.i18n("AddCaptionModal__title")`,
|
||||
options: [{ messagesCacheKey }],
|
||||
},
|
||||
{
|
||||
code: `let jsx = <Intl id="AddCaptionModal__title"/>`,
|
||||
options: [{ messagesCacheKey }],
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: 'i18n(`AddCaptionModal__${title}`)',
|
||||
options: [{ messagesCacheKey }],
|
||||
errors: [
|
||||
{
|
||||
message: "i18n()'s first argument should always be a literal string",
|
||||
type: 'CallExpression',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'window.i18n(`AddCaptionModal__${title}`)',
|
||||
options: [{ messagesCacheKey }],
|
||||
errors: [
|
||||
{
|
||||
message: "i18n()'s first argument should always be a literal string",
|
||||
type: 'CallExpression',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `let jsx = <Intl id={"AddCaptionModal__title"}/>`,
|
||||
options: [{ messagesCacheKey }],
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
"<Intl> must always be provided an 'id' attribute with a literal string",
|
||||
type: 'JSXOpeningElement',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'let jsx = <Intl id={`AddCaptionModal__title`}/>',
|
||||
options: [{ messagesCacheKey }],
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
"<Intl> must always be provided an 'id' attribute with a literal string",
|
||||
type: 'JSXOpeningElement',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'let jsx = <Intl id={`AddCaptionModal__${title}`}/>',
|
||||
options: [{ messagesCacheKey }],
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
"<Intl> must always be provided an 'id' attribute with a literal string",
|
||||
type: 'JSXOpeningElement',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: `i18n("THIS_KEY_SHOULD_NEVER_EXIST")`,
|
||||
options: [{ messagesCacheKey }],
|
||||
|
|
|
@ -604,19 +604,23 @@ export function Headers(): JSX.Element {
|
|||
rows={[
|
||||
{
|
||||
type: RowType.Header,
|
||||
i18nKey: 'conversationsHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('conversationsHeader'),
|
||||
},
|
||||
{
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('messagesHeader'),
|
||||
},
|
||||
{
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('findByUsernameHeader'),
|
||||
},
|
||||
{
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByPhoneNumberHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('findByPhoneNumberHeader'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
@ -629,7 +633,8 @@ export function FindByPhoneNumber(): JSX.Element {
|
|||
rows={[
|
||||
{
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByPhoneNumberHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('findByPhoneNumberHeader'),
|
||||
},
|
||||
{
|
||||
type: RowType.StartNewConversation,
|
||||
|
@ -673,7 +678,8 @@ export function FindByUsername(): JSX.Element {
|
|||
rows={[
|
||||
{
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('findByUsernameHeader'),
|
||||
},
|
||||
{
|
||||
type: RowType.UsernameSearchResult,
|
||||
|
@ -745,7 +751,8 @@ export function KitchenSink(): JSX.Element {
|
|||
},
|
||||
{
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('contactsHeader'),
|
||||
},
|
||||
{
|
||||
type: RowType.Contact,
|
||||
|
@ -753,7 +760,8 @@ export function KitchenSink(): JSX.Element {
|
|||
},
|
||||
{
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('messagesHeader'),
|
||||
},
|
||||
{
|
||||
type: RowType.Conversation,
|
||||
|
@ -765,7 +773,8 @@ export function KitchenSink(): JSX.Element {
|
|||
},
|
||||
{
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('findByUsernameHeader'),
|
||||
},
|
||||
{
|
||||
type: RowType.UsernameSearchResult,
|
||||
|
|
|
@ -103,9 +103,17 @@ type MessageRowType = {
|
|||
|
||||
type HeaderRowType = {
|
||||
type: RowType.Header;
|
||||
i18nKey: string;
|
||||
getHeaderText: (i18n: LocalizerType) => string;
|
||||
};
|
||||
|
||||
// Exported for tests across multiple files
|
||||
export function _testHeaderText(row: Row | void): string | null {
|
||||
if (row?.type === RowType.Header) {
|
||||
return row.getHeaderText(((key: string) => key) as LocalizerType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
type SearchResultsLoadingFakeHeaderType = {
|
||||
type: RowType.SearchResultsLoadingFakeHeader;
|
||||
};
|
||||
|
@ -375,18 +383,18 @@ export function ConversationList({
|
|||
/>
|
||||
);
|
||||
break;
|
||||
case RowType.Header:
|
||||
case RowType.Header: {
|
||||
const headerText = row.getHeaderText(i18n);
|
||||
result = (
|
||||
<div
|
||||
className="module-conversation-list__item--header"
|
||||
// eslint-disable-next-line local-rules/valid-i18n-keys
|
||||
aria-label={i18n(row.i18nKey)}
|
||||
aria-label={headerText}
|
||||
>
|
||||
{/* eslint-disable-next-line local-rules/valid-i18n-keys */}
|
||||
{i18n(row.i18nKey)}
|
||||
{headerText}
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case RowType.MessageSearchResult:
|
||||
result = <>{renderMessageSearchResult?.(row.messageId)}</>;
|
||||
break;
|
||||
|
|
|
@ -7,6 +7,7 @@ import type { ConversationType } from '../state/ducks/conversations';
|
|||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import { GroupDialog } from './GroupDialog';
|
||||
import { sortByTitle } from '../util/sortByTitle';
|
||||
import { missingCaseError } from '../util';
|
||||
|
||||
export type DataPropsType = {
|
||||
conversationId: string;
|
||||
|
@ -70,8 +71,6 @@ export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
|
|||
const keepHistory = hasMigrated
|
||||
? i18n('GroupV1--Migration--info--keep-history')
|
||||
: i18n('GroupV1--Migration--migrate--keep-history');
|
||||
const migrationKey = hasMigrated ? 'after' : 'before';
|
||||
const droppedMembersKey = `GroupV1--Migration--info--removed--${migrationKey}`;
|
||||
|
||||
let primaryButtonText: string;
|
||||
let onClickPrimaryButton: () => void;
|
||||
|
@ -116,14 +115,16 @@ export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
|
|||
getPreferredBadge,
|
||||
i18n,
|
||||
members: invitedMembers,
|
||||
prefix: 'GroupV1--Migration--info--invited',
|
||||
hasMigrated,
|
||||
kind: 'invited',
|
||||
theme,
|
||||
})}
|
||||
{renderMembers({
|
||||
getPreferredBadge,
|
||||
i18n,
|
||||
members: droppedMembers,
|
||||
prefix: droppedMembersKey,
|
||||
hasMigrated,
|
||||
kind: 'dropped',
|
||||
theme,
|
||||
})}
|
||||
</>
|
||||
|
@ -136,26 +137,49 @@ function renderMembers({
|
|||
getPreferredBadge,
|
||||
i18n,
|
||||
members,
|
||||
prefix,
|
||||
hasMigrated,
|
||||
kind,
|
||||
theme,
|
||||
}: Readonly<{
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
i18n: LocalizerType;
|
||||
members: Array<ConversationType>;
|
||||
prefix: string;
|
||||
hasMigrated: boolean;
|
||||
kind: 'invited' | 'dropped';
|
||||
theme: ThemeType;
|
||||
}>): React.ReactNode {
|
||||
if (!members.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const postfix = members.length === 1 ? '--one' : '--many';
|
||||
const key = `${prefix}${postfix}`;
|
||||
let text: string;
|
||||
switch (kind) {
|
||||
case 'invited':
|
||||
text =
|
||||
members.length === 1
|
||||
? i18n('GroupV1--Migration--info--invited--one')
|
||||
: i18n('GroupV1--Migration--info--invited--many');
|
||||
break;
|
||||
case 'dropped':
|
||||
if (hasMigrated) {
|
||||
text =
|
||||
members.length === 1
|
||||
? i18n('GroupV1--Migration--info--removed--before--one')
|
||||
: i18n('GroupV1--Migration--info--removed--before--many');
|
||||
} else {
|
||||
text =
|
||||
members.length === 1
|
||||
? i18n('GroupV1--Migration--info--removed--after--one')
|
||||
: i18n('GroupV1--Migration--info--removed--after--many');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw missingCaseError(kind);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* eslint-disable-next-line local-rules/valid-i18n-keys */}
|
||||
<GroupDialog.Paragraph>{i18n(key)}</GroupDialog.Paragraph>
|
||||
<GroupDialog.Paragraph>{text}</GroupDialog.Paragraph>
|
||||
<GroupDialog.Contacts
|
||||
contacts={sortByTitle(members)}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
|
|
|
@ -71,24 +71,29 @@ export class Intl extends React.Component<Props> {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
public override render() {
|
||||
const { components, id, i18n, renderText = defaultRenderText } = this.props;
|
||||
const {
|
||||
components,
|
||||
id,
|
||||
// Indirection for linter/migration tooling
|
||||
i18n: localizer,
|
||||
renderText = defaultRenderText,
|
||||
} = this.props;
|
||||
|
||||
if (!id) {
|
||||
log.error('Error: Intl id prop not provided');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!i18n.isLegacyFormat(id)) {
|
||||
if (!localizer.isLegacyFormat(id)) {
|
||||
strictAssert(
|
||||
!Array.isArray(components),
|
||||
`components cannot be an array for ICU message ${id}`
|
||||
);
|
||||
const intl = i18n.getIntl();
|
||||
const intl = localizer.getIntl();
|
||||
return intl.formatMessage({ id }, components);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line local-rules/valid-i18n-keys
|
||||
const text = i18n(id);
|
||||
const text = localizer(id);
|
||||
const results: Array<
|
||||
string | JSX.Element | Array<string | JSX.Element> | null
|
||||
> = [];
|
||||
|
|
|
@ -339,7 +339,7 @@ export function SendStoryModal({
|
|||
<EditMyStoryPrivacy
|
||||
hasDisclaimerAbove
|
||||
i18n={i18n}
|
||||
learnMore="SendStoryModal__privacy-disclaimer"
|
||||
kind="privacy"
|
||||
myStories={stagedMyStories}
|
||||
signalConnectionsCount={signalConnections.length}
|
||||
onClickExclude={() => {
|
||||
|
|
|
@ -627,7 +627,7 @@ export function DistributionListSettingsModal({
|
|||
{isMyStory && (
|
||||
<EditMyStoryPrivacy
|
||||
i18n={i18n}
|
||||
learnMore="StoriesSettings__mine__disclaimer"
|
||||
kind="mine"
|
||||
myStories={listToEdit}
|
||||
onClickExclude={() => {
|
||||
setPage(Page.HideStoryFrom);
|
||||
|
@ -791,7 +791,7 @@ function CheckboxRender({
|
|||
type EditMyStoryPrivacyPropsType = {
|
||||
hasDisclaimerAbove?: boolean;
|
||||
i18n: LocalizerType;
|
||||
learnMore: string;
|
||||
kind: 'privacy' | 'mine';
|
||||
myStories: StoryDistributionListWithMembersDataType;
|
||||
onClickExclude: () => unknown;
|
||||
onClickOnlyShareWith: () => unknown;
|
||||
|
@ -805,7 +805,7 @@ type EditMyStoryPrivacyPropsType = {
|
|||
export function EditMyStoryPrivacy({
|
||||
hasDisclaimerAbove,
|
||||
i18n,
|
||||
learnMore,
|
||||
kind,
|
||||
myStories,
|
||||
onClickExclude,
|
||||
onClickOnlyShareWith,
|
||||
|
@ -814,24 +814,30 @@ export function EditMyStoryPrivacy({
|
|||
toggleSignalConnectionsModal,
|
||||
signalConnectionsCount,
|
||||
}: EditMyStoryPrivacyPropsType): JSX.Element {
|
||||
const learnMore = (
|
||||
<button
|
||||
className="StoriesSettingsModal__disclaimer__learn-more"
|
||||
onClick={toggleSignalConnectionsModal}
|
||||
type="button"
|
||||
>
|
||||
{i18n('StoriesSettings__mine__disclaimer--learn-more')}
|
||||
</button>
|
||||
);
|
||||
const disclaimerElement = (
|
||||
<div className="StoriesSettingsModal__disclaimer">
|
||||
{/* eslint-disable-next-line local-rules/valid-i18n-keys */}
|
||||
<Intl
|
||||
components={{
|
||||
learnMore: (
|
||||
<button
|
||||
className="StoriesSettingsModal__disclaimer__learn-more"
|
||||
onClick={toggleSignalConnectionsModal}
|
||||
type="button"
|
||||
>
|
||||
{i18n('StoriesSettings__mine__disclaimer--learn-more')}
|
||||
</button>
|
||||
),
|
||||
}}
|
||||
i18n={i18n}
|
||||
id={learnMore}
|
||||
/>
|
||||
{kind === 'mine' ? (
|
||||
<Intl
|
||||
components={{ learnMore }}
|
||||
i18n={i18n}
|
||||
id="StoriesSettings__mine__disclaimer"
|
||||
/>
|
||||
) : (
|
||||
<Intl
|
||||
components={{ learnMore }}
|
||||
i18n={i18n}
|
||||
id="SendStoryModal__privacy-disclaimer"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
@ -40,10 +40,9 @@ export function ToastManager({
|
|||
if (toastType === ToastType.AddingUserToGroup) {
|
||||
return (
|
||||
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>
|
||||
{i18n(
|
||||
'AddUserToAnotherGroupModal__toast--adding-user-to-group',
|
||||
toast.parameters
|
||||
)}
|
||||
{i18n('AddUserToAnotherGroupModal__toast--adding-user-to-group', {
|
||||
...toast.parameters,
|
||||
})}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
@ -107,7 +106,9 @@ export function ToastManager({
|
|||
if (toastType === ToastType.CannotStartGroupCall) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('GroupV2--cannot-start-group-call', toast.parameters)}
|
||||
{i18n('GroupV2--cannot-start-group-call', {
|
||||
...toast.parameters,
|
||||
})}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
@ -344,10 +345,9 @@ export function ToastManager({
|
|||
if (toastType === ToastType.UserAddedToGroup) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n(
|
||||
'AddUserToAnotherGroupModal__toast--user-added-to-group',
|
||||
toast.parameters
|
||||
)}
|
||||
{i18n('AddUserToAnotherGroupModal__toast--user-added-to-group', {
|
||||
...toast.parameters,
|
||||
})}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import React from 'react';
|
|||
import moment from 'moment';
|
||||
|
||||
import { Modal } from './Modal';
|
||||
import type { IntlComponentsType } from './Intl';
|
||||
import { Intl } from './Intl';
|
||||
import { Emojify } from './conversation/Emojify';
|
||||
import type { LocalizerType, RenderTextCallbackType } from '../types/Util';
|
||||
|
@ -19,61 +18,46 @@ export type PropsType = {
|
|||
type ReleaseNotesType = {
|
||||
date: Date;
|
||||
version: string;
|
||||
features: Array<{ key: string; components: IntlComponentsType }>;
|
||||
features: Array<JSX.Element>;
|
||||
};
|
||||
|
||||
const renderText: RenderTextCallbackType = ({ key, text }) => (
|
||||
<Emojify key={key} text={text} />
|
||||
);
|
||||
|
||||
const releaseNotes: ReleaseNotesType = {
|
||||
date: new Date(window.getBuildCreation?.() || Date.now()),
|
||||
version: window.getVersion?.(),
|
||||
features: [
|
||||
{
|
||||
key: 'icu:WhatsNew__v6.12--0',
|
||||
components: {},
|
||||
},
|
||||
{
|
||||
key: 'icu:WhatsNew__v6.12--1',
|
||||
components: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export function WhatsNewModal({
|
||||
i18n,
|
||||
hideWhatsNewModal,
|
||||
}: PropsType): JSX.Element {
|
||||
let contentNode: ReactChild;
|
||||
|
||||
const releaseNotes: ReleaseNotesType = {
|
||||
date: new Date(window.getBuildCreation?.() || Date.now()),
|
||||
version: window.getVersion?.(),
|
||||
features: [
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="icu:WhatsNew__v6.12--0"
|
||||
renderText={renderText}
|
||||
components={{}}
|
||||
/>,
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="icu:WhatsNew__v6.12--1"
|
||||
renderText={renderText}
|
||||
components={{}}
|
||||
/>,
|
||||
],
|
||||
};
|
||||
|
||||
if (releaseNotes.features.length === 1) {
|
||||
const { key, components } = releaseNotes.features[0];
|
||||
contentNode = (
|
||||
<p>
|
||||
{/* eslint-disable-next-line local-rules/valid-i18n-keys */}
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id={key}
|
||||
renderText={renderText}
|
||||
components={components}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
contentNode = <p>{releaseNotes.features[0]}</p>;
|
||||
} else {
|
||||
contentNode = (
|
||||
<ul>
|
||||
{releaseNotes.features.map(({ key, components }) => (
|
||||
<li key={key}>
|
||||
{/* eslint-disable-next-line local-rules/valid-i18n-keys */}
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id={key}
|
||||
renderText={renderText}
|
||||
components={components}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
{releaseNotes.features.map(element => {
|
||||
return <li key={element.props.id}>{element}</li>;
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -60,16 +60,8 @@ export function GroupV1Migration(props: PropsType): React.ReactElement {
|
|||
i18n('GroupV1--Migration--invited--you')
|
||||
) : (
|
||||
<>
|
||||
{renderUsers(
|
||||
invitedMembers,
|
||||
i18n,
|
||||
'GroupV1--Migration--invited'
|
||||
)}
|
||||
{renderUsers(
|
||||
droppedMembers,
|
||||
i18n,
|
||||
'GroupV1--Migration--removed'
|
||||
)}
|
||||
{renderUsers(invitedMembers, i18n, 'invited')}
|
||||
{renderUsers(droppedMembers, i18n, 'removed')}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
|
@ -106,31 +98,52 @@ export function GroupV1Migration(props: PropsType): React.ReactElement {
|
|||
function renderUsers(
|
||||
members: Array<ConversationType>,
|
||||
i18n: LocalizerType,
|
||||
keyPrefix: string
|
||||
kind: 'invited' | 'removed'
|
||||
): React.ReactElement | null {
|
||||
if (!members || members.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (members.length === 1) {
|
||||
const contact = <ContactName title={members[0].title} />;
|
||||
return (
|
||||
<p>
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id={`${keyPrefix}--one`}
|
||||
components={{
|
||||
contact: <ContactName title={members[0].title} />,
|
||||
}}
|
||||
/>
|
||||
{kind === 'invited' && (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="GroupV1--Migration--invited--one"
|
||||
components={{ contact }}
|
||||
/>
|
||||
)}
|
||||
{kind === 'removed' && (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="GroupV1--Migration--removed--one"
|
||||
components={{ contact }}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
const count = members.length.toString();
|
||||
|
||||
return (
|
||||
<p>
|
||||
{i18n(`${keyPrefix}--many`, {
|
||||
count: members.length.toString(),
|
||||
})}
|
||||
{kind === 'invited' && members.length > 1 && (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="GroupV1--Migration--invited--many"
|
||||
components={{ count }}
|
||||
/>
|
||||
)}
|
||||
{kind === 'removed' && members.length > 1 && (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="GroupV1--Migration--removed--many"
|
||||
components={{ count }}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,26 @@ export function MandatoryProfileSharingActions({
|
|||
}: Props): JSX.Element {
|
||||
const [mrState, setMrState] = React.useState(MessageRequestState.default);
|
||||
|
||||
const firstNameContact = (
|
||||
<strong
|
||||
key="name"
|
||||
className="module-message-request-actions__message__name"
|
||||
>
|
||||
<ContactName firstName={firstName} title={title} preferFirstName />
|
||||
</strong>
|
||||
);
|
||||
|
||||
const learnMore = (
|
||||
<a
|
||||
href="https://support.signal.org/hc/articles/360007459591"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="module-message-request-actions__message__learn-more"
|
||||
>
|
||||
{i18n('MessageRequests--learn-more')}
|
||||
</a>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{mrState !== MessageRequestState.default ? (
|
||||
|
@ -62,34 +82,19 @@ export function MandatoryProfileSharingActions({
|
|||
) : null}
|
||||
<div className="module-message-request-actions">
|
||||
<p className="module-message-request-actions__message">
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id={`MessageRequests--profile-sharing--${conversationType}`}
|
||||
components={{
|
||||
firstName: (
|
||||
<strong
|
||||
key="name"
|
||||
className="module-message-request-actions__message__name"
|
||||
>
|
||||
<ContactName
|
||||
firstName={firstName}
|
||||
title={title}
|
||||
preferFirstName
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
learnMore: (
|
||||
<a
|
||||
href="https://support.signal.org/hc/articles/360007459591"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="module-message-request-actions__message__learn-more"
|
||||
>
|
||||
{i18n('MessageRequests--learn-more')}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{conversationType === 'direct' ? (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="MessageRequests--profile-sharing--direct"
|
||||
components={{ firstName: firstNameContact, learnMore }}
|
||||
/>
|
||||
) : (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="MessageRequests--profile-sharing--group"
|
||||
components={{ firstName: firstNameContact, learnMore }}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
<div className="module-message-request-actions__buttons">
|
||||
<Button
|
||||
|
|
|
@ -1251,7 +1251,10 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
if (giftBadge.state === GiftBadgeStates.Unopened) {
|
||||
const description = i18n(`icu:message--donation--unopened--${direction}`);
|
||||
const description =
|
||||
direction === 'incoming'
|
||||
? i18n('icu:message--donation--unopened--incoming')
|
||||
: i18n('icu:message--donation--unopened--outgoing');
|
||||
const isRTL = getDirection(description) === 'rtl';
|
||||
const { metadataWidth } = this.state;
|
||||
|
||||
|
@ -1931,26 +1934,23 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
isTapToViewError,
|
||||
} = this.props;
|
||||
|
||||
const incomingString = isTapToViewExpired
|
||||
? i18n('Message--tap-to-view-expired')
|
||||
: i18n(
|
||||
`Message--tap-to-view--incoming${
|
||||
isVideo(attachments) ? '-video' : ''
|
||||
}`
|
||||
);
|
||||
const outgoingString = i18n('Message--tap-to-view--outgoing');
|
||||
const isDownloadPending = this.isAttachmentPending();
|
||||
|
||||
if (isDownloadPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
return isTapToViewError
|
||||
? i18n('incomingError')
|
||||
: direction === 'outgoing'
|
||||
? outgoingString
|
||||
: incomingString;
|
||||
if (isTapToViewError) {
|
||||
return i18n('incomingError');
|
||||
}
|
||||
if (direction === 'outgoing') {
|
||||
return i18n('Message--tap-to-view--outgoing');
|
||||
}
|
||||
if (isTapToViewExpired) {
|
||||
return i18n('Message--tap-to-view-expired');
|
||||
}
|
||||
if (isVideo(attachments)) {
|
||||
return i18n('Message--tap-to-view--incoming-video');
|
||||
}
|
||||
return i18n('Message--tap-to-view--incoming');
|
||||
}
|
||||
|
||||
public renderTapToView(): JSX.Element {
|
||||
|
|
|
@ -26,6 +26,7 @@ import * as log from '../../logging/log';
|
|||
import { formatDateTimeLong } from '../../util/timestamp';
|
||||
import { DurationInSeconds } from '../../util/durations';
|
||||
import { format as formatRelativeTime } from '../../util/expirationTimer';
|
||||
import { missingCaseError } from '../../util';
|
||||
|
||||
export type Contact = Pick<
|
||||
ConversationType,
|
||||
|
@ -200,24 +201,49 @@ export class MessageDetail extends React.Component<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
private renderContactGroupHeaderText(
|
||||
sendStatus: undefined | SendStatus
|
||||
): string {
|
||||
const { i18n } = this.props;
|
||||
|
||||
if (sendStatus === undefined) {
|
||||
return i18n('from');
|
||||
}
|
||||
|
||||
switch (sendStatus) {
|
||||
case SendStatus.Failed:
|
||||
return i18n('MessageDetailsHeader--Failed');
|
||||
case SendStatus.Pending:
|
||||
return i18n('MessageDetailsHeader--Pending');
|
||||
case SendStatus.Sent:
|
||||
return i18n('MessageDetailsHeader--Sent');
|
||||
case SendStatus.Delivered:
|
||||
return i18n('MessageDetailsHeader--Delivered');
|
||||
case SendStatus.Read:
|
||||
return i18n('MessageDetailsHeader--Read');
|
||||
case SendStatus.Viewed:
|
||||
return i18n('MessageDetailsHeader--Viewed');
|
||||
default:
|
||||
throw missingCaseError(sendStatus);
|
||||
}
|
||||
}
|
||||
|
||||
private renderContactGroup(
|
||||
sendStatus: undefined | SendStatus,
|
||||
contacts: undefined | ReadonlyArray<Contact>
|
||||
): ReactNode {
|
||||
const { i18n } = this.props;
|
||||
if (!contacts || !contacts.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const i18nKey =
|
||||
sendStatus === undefined ? 'from' : `MessageDetailsHeader--${sendStatus}`;
|
||||
|
||||
const sortedContacts = [...contacts].sort((a, b) =>
|
||||
contactSortCollator.compare(a.title, b.title)
|
||||
);
|
||||
|
||||
const headerText = this.renderContactGroupHeaderText(sendStatus);
|
||||
|
||||
return (
|
||||
<div key={i18nKey} className="module-message-detail__contact-group">
|
||||
<div key={headerText} className="module-message-detail__contact-group">
|
||||
<div
|
||||
className={classNames(
|
||||
'module-message-detail__contact-group__header',
|
||||
|
@ -225,8 +251,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
`module-message-detail__contact-group__header--${sendStatus}`
|
||||
)}
|
||||
>
|
||||
{/* eslint-disable-next-line local-rules/valid-i18n-keys */}
|
||||
{i18n(i18nKey)}
|
||||
{headerText}
|
||||
</div>
|
||||
{sortedContacts.map(contact => this.renderContact(contact))}
|
||||
</div>
|
||||
|
|
|
@ -35,6 +35,15 @@ export function MessageRequestActions({
|
|||
}: Props): JSX.Element {
|
||||
const [mrState, setMrState] = React.useState(MessageRequestState.default);
|
||||
|
||||
const name = (
|
||||
<strong
|
||||
key="name"
|
||||
className="module-message-request-actions__message__name"
|
||||
>
|
||||
<ContactName firstName={firstName} title={title} preferFirstName />
|
||||
</strong>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{mrState !== MessageRequestState.default ? (
|
||||
|
@ -53,26 +62,34 @@ export function MessageRequestActions({
|
|||
) : null}
|
||||
<div className="module-message-request-actions">
|
||||
<p className="module-message-request-actions__message">
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id={`MessageRequests--message-${conversationType}${
|
||||
isBlocked ? '-blocked' : ''
|
||||
}`}
|
||||
components={{
|
||||
name: (
|
||||
<strong
|
||||
key="name"
|
||||
className="module-message-request-actions__message__name"
|
||||
>
|
||||
<ContactName
|
||||
firstName={firstName}
|
||||
title={title}
|
||||
preferFirstName
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{conversationType === 'direct' && isBlocked && (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="MessageRequests--message-direct-blocked"
|
||||
components={{ name }}
|
||||
/>
|
||||
)}
|
||||
{conversationType === 'direct' && !isBlocked && (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="MessageRequests--message-direct"
|
||||
components={{ name }}
|
||||
/>
|
||||
)}
|
||||
{conversationType === 'group' && isBlocked && (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="MessageRequests--message-group-blocked"
|
||||
components={{ name }}
|
||||
/>
|
||||
)}
|
||||
{conversationType === 'group' && !isBlocked && (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="MessageRequests--message-group"
|
||||
components={{ name }}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
<div className="module-message-request-actions__buttons">
|
||||
<Button
|
||||
|
|
|
@ -132,13 +132,23 @@ export function MessageRequestActionsConfirmation({
|
|||
onChangeState(MessageRequestState.default);
|
||||
}}
|
||||
title={
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id={`MessageRequests--delete-${conversationType}-confirm-title`}
|
||||
components={{
|
||||
title: <ContactName key="name" title={title} />,
|
||||
}}
|
||||
/>
|
||||
conversationType === 'direct' ? (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="MessageRequests--delete-direct-confirm-title"
|
||||
components={{
|
||||
title: <ContactName key="name" title={title} />,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="MessageRequests--delete-group-confirm-title"
|
||||
components={{
|
||||
title: <ContactName key="name" title={title} />,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
actions={[
|
||||
{
|
||||
|
|
|
@ -35,32 +35,30 @@ export function SafetyNumberNotification({
|
|||
i18n,
|
||||
toggleSafetyNumberModal,
|
||||
}: Props): JSX.Element {
|
||||
const changeKey = isGroup
|
||||
? 'safetyNumberChangedGroup'
|
||||
: 'safetyNumberChanged';
|
||||
|
||||
const name = (
|
||||
<span
|
||||
key="external-1"
|
||||
className="module-safety-number-notification__contact"
|
||||
>
|
||||
<ContactName
|
||||
title={contact.title}
|
||||
module="module-safety-number-notification__contact"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<SystemMessage
|
||||
icon="safety-number"
|
||||
contents={
|
||||
// eslint-disable-next-line local-rules/valid-i18n-keys
|
||||
<Intl
|
||||
id={changeKey}
|
||||
components={{
|
||||
name: (
|
||||
<span
|
||||
key="external-1"
|
||||
className="module-safety-number-notification__contact"
|
||||
>
|
||||
<ContactName
|
||||
title={contact.title}
|
||||
module="module-safety-number-notification__contact"
|
||||
/>
|
||||
</span>
|
||||
),
|
||||
}}
|
||||
i18n={i18n}
|
||||
/>
|
||||
isGroup ? (
|
||||
<Intl
|
||||
id="safetyNumberChangedGroup"
|
||||
components={{ name }}
|
||||
i18n={i18n}
|
||||
/>
|
||||
) : (
|
||||
<Intl id="safetyNumberChanged" components={{ name }} i18n={i18n} />
|
||||
)
|
||||
}
|
||||
button={
|
||||
<Button
|
||||
|
|
|
@ -41,51 +41,46 @@ export type Props = PropsData & PropsHousekeeping;
|
|||
export function TimerNotification(props: Props): JSX.Element {
|
||||
const { disabled, i18n, title, type } = props;
|
||||
|
||||
let changeKey: string;
|
||||
let timespan: string;
|
||||
if (props.disabled) {
|
||||
changeKey = 'disabledDisappearingMessages';
|
||||
timespan = ''; // Set to the empty string to satisfy types
|
||||
} else {
|
||||
changeKey = 'theyChangedTheTimer';
|
||||
timespan = expirationTimer.format(i18n, props.expireTimer);
|
||||
}
|
||||
|
||||
const name = <ContactName key="external-1" title={title} />;
|
||||
|
||||
let message: ReactNode;
|
||||
switch (type) {
|
||||
case 'fromOther':
|
||||
message = (
|
||||
// eslint-disable-next-line local-rules/valid-i18n-keys
|
||||
message = props.disabled ? (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id={changeKey}
|
||||
components={{
|
||||
name: <ContactName key="external-1" title={title} />,
|
||||
time: timespan,
|
||||
}}
|
||||
id="disabledDisappearingMessages"
|
||||
components={{ name }}
|
||||
/>
|
||||
) : (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="theyChangedTheTimer"
|
||||
components={{ name, time: timespan }}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case 'fromMe':
|
||||
message = disabled
|
||||
? i18n('youDisabledDisappearingMessages')
|
||||
: i18n('youChangedTheTimer', {
|
||||
time: timespan,
|
||||
});
|
||||
: i18n('youChangedTheTimer', { time: timespan });
|
||||
break;
|
||||
case 'fromSync':
|
||||
message = disabled
|
||||
? i18n('disappearingMessagesDisabled')
|
||||
: i18n('timerSetOnSync', {
|
||||
time: timespan,
|
||||
});
|
||||
: i18n('timerSetOnSync', { time: timespan });
|
||||
break;
|
||||
case 'fromMember':
|
||||
message = disabled
|
||||
? i18n('disappearingMessagesDisabledByMember')
|
||||
: i18n('timerSetByMember', {
|
||||
time: timespan,
|
||||
});
|
||||
: i18n('timerSetByMember', { time: timespan });
|
||||
break;
|
||||
default:
|
||||
log.warn('TimerNotification: unsupported type provided:', type);
|
||||
|
|
|
@ -30,42 +30,64 @@ type PropsHousekeeping = {
|
|||
|
||||
export type Props = PropsData & PropsHousekeeping;
|
||||
|
||||
function UnsupportedMessageContents({ canProcessNow, contact, i18n }: Props) {
|
||||
const { isMe } = contact;
|
||||
const contactName = (
|
||||
<span key="external-1" className="module-unsupported-message__contact">
|
||||
<ContactName
|
||||
title={contact.title}
|
||||
module="module-unsupported-message__contact"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
if (isMe) {
|
||||
if (canProcessNow) {
|
||||
return (
|
||||
<Intl
|
||||
id="Message--unsupported-message-ask-to-resend"
|
||||
components={{ contact: contactName }}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Intl
|
||||
id="Message--from-me-unsupported-message"
|
||||
components={{ contact: contactName }}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (canProcessNow) {
|
||||
return (
|
||||
<Intl
|
||||
id="Message--from-me-unsupported-message-ask-to-resend"
|
||||
components={{ contact: contactName }}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Intl
|
||||
id="Message--from-me-unsupported-message"
|
||||
components={{ contact: contactName }}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function UnsupportedMessage({
|
||||
canProcessNow,
|
||||
contact,
|
||||
i18n,
|
||||
}: Props): JSX.Element {
|
||||
const { isMe } = contact;
|
||||
|
||||
const otherStringId = canProcessNow
|
||||
? 'Message--unsupported-message-ask-to-resend'
|
||||
: 'Message--unsupported-message';
|
||||
const meStringId = canProcessNow
|
||||
? 'Message--from-me-unsupported-message-ask-to-resend'
|
||||
: 'Message--from-me-unsupported-message';
|
||||
const stringId = isMe ? meStringId : otherStringId;
|
||||
const icon = canProcessNow ? 'unsupported--can-process' : 'unsupported';
|
||||
|
||||
return (
|
||||
<SystemMessage
|
||||
icon={icon}
|
||||
icon={canProcessNow ? 'unsupported--can-process' : 'unsupported'}
|
||||
contents={
|
||||
// eslint-disable-next-line local-rules/valid-i18n-keys
|
||||
<Intl
|
||||
id={stringId}
|
||||
components={{
|
||||
contact: (
|
||||
<span
|
||||
key="external-1"
|
||||
className="module-unsupported-message__contact"
|
||||
>
|
||||
<ContactName
|
||||
title={contact.title}
|
||||
module="module-unsupported-message__contact"
|
||||
/>
|
||||
</span>
|
||||
),
|
||||
}}
|
||||
<UnsupportedMessageContents
|
||||
canProcessNow={canProcessNow}
|
||||
contact={contact}
|
||||
i18n={i18n}
|
||||
/>
|
||||
}
|
||||
|
|
|
@ -25,45 +25,43 @@ type PropsHousekeeping = {
|
|||
export type Props = PropsData & PropsHousekeeping;
|
||||
|
||||
export class VerificationNotification extends React.Component<Props> {
|
||||
public getStringId(): string {
|
||||
const { isLocal, type } = this.props;
|
||||
public renderContents(): JSX.Element {
|
||||
const { contact, isLocal, type, i18n } = this.props;
|
||||
|
||||
const name = (
|
||||
<ContactName
|
||||
key="external-1"
|
||||
title={contact.title}
|
||||
module="module-verification-notification__contact"
|
||||
/>
|
||||
);
|
||||
|
||||
switch (type) {
|
||||
case 'markVerified':
|
||||
return isLocal
|
||||
? 'youMarkedAsVerified'
|
||||
: 'youMarkedAsVerifiedOtherDevice';
|
||||
return isLocal ? (
|
||||
<Intl id="youMarkedAsVerified" components={{ name }} i18n={i18n} />
|
||||
) : (
|
||||
<Intl
|
||||
id="youMarkedAsVerifiedOtherDevice"
|
||||
components={{ name }}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
case 'markNotVerified':
|
||||
return isLocal
|
||||
? 'youMarkedAsNotVerified'
|
||||
: 'youMarkedAsNotVerifiedOtherDevice';
|
||||
return isLocal ? (
|
||||
<Intl id="youMarkedAsNotVerified" components={{ name }} i18n={i18n} />
|
||||
) : (
|
||||
<Intl
|
||||
id="youMarkedAsNotVerifiedOtherDevice"
|
||||
components={{ name }}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
throw missingCaseError(type);
|
||||
}
|
||||
}
|
||||
|
||||
public renderContents(): JSX.Element {
|
||||
const { contact, i18n } = this.props;
|
||||
const id = this.getStringId();
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line local-rules/valid-i18n-keys
|
||||
<Intl
|
||||
id={id}
|
||||
components={{
|
||||
name: (
|
||||
<ContactName
|
||||
key="external-1"
|
||||
title={contact.title}
|
||||
module="module-verification-notification__contact"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
const { type } = this.props;
|
||||
const icon = type === 'markVerified' ? 'verified' : 'verified-not';
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
/* eslint-disable local-rules/valid-i18n-keys */
|
||||
|
||||
import React, {
|
||||
useEffect,
|
||||
useMemo,
|
||||
|
@ -212,7 +210,8 @@ export function ChooseGroupMembersModal({
|
|||
if (virtualIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('contactsHeader'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -250,7 +249,8 @@ export function ChooseGroupMembersModal({
|
|||
if (virtualIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByPhoneNumberHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('findByPhoneNumberHeader'),
|
||||
};
|
||||
}
|
||||
if (virtualIndex === 1) {
|
||||
|
@ -268,7 +268,8 @@ export function ChooseGroupMembersModal({
|
|||
if (virtualIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
getHeaderText: i18n => i18n('findByUsernameHeader'),
|
||||
};
|
||||
}
|
||||
if (virtualIndex === 1) {
|
||||
|
@ -307,16 +308,18 @@ export function ChooseGroupMembersModal({
|
|||
|
||||
let item;
|
||||
switch (row?.type) {
|
||||
case RowType.Header:
|
||||
case RowType.Header: {
|
||||
const headerText = row.getHeaderText(i18n);
|
||||
item = (
|
||||
<div
|
||||
className="module-conversation-list__item--header"
|
||||
aria-label={i18n(row.i18nKey)}
|
||||
aria-label={headerText}
|
||||
>
|
||||
{i18n(row.i18nKey)}
|
||||
{headerText}
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case RowType.ContactCheckbox:
|
||||
item = (
|
||||
<ContactCheckbox
|
||||
|
|
|
@ -618,8 +618,7 @@ function ConversationDetailsCallButton({
|
|||
onClick={onClick}
|
||||
variant={ButtonVariant.Details}
|
||||
>
|
||||
{/* eslint-disable-next-line local-rules/valid-i18n-keys */}
|
||||
{i18n(type)}
|
||||
{type === 'audio' ? i18n('audio') : i18n('video')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
|
|
|
@ -71,11 +71,25 @@ function MediaSection({
|
|||
const first = section.mediaItems[0];
|
||||
const { message } = first;
|
||||
const date = moment(getMessageTimestamp(message));
|
||||
const header =
|
||||
section.type === 'yearMonth'
|
||||
? date.format(MONTH_FORMAT)
|
||||
: // eslint-disable-next-line local-rules/valid-i18n-keys
|
||||
i18n(section.type);
|
||||
|
||||
function getHeader(): string {
|
||||
switch (section.type) {
|
||||
case 'yearMonth':
|
||||
return date.format(MONTH_FORMAT);
|
||||
case 'today':
|
||||
return i18n('today');
|
||||
case 'yesterday':
|
||||
return i18n('yesterday');
|
||||
case 'thisWeek':
|
||||
return i18n('thisWeek');
|
||||
case 'thisMonth':
|
||||
return i18n('thisMonth');
|
||||
default:
|
||||
throw missingCaseError(section);
|
||||
}
|
||||
}
|
||||
|
||||
const header = getHeader();
|
||||
|
||||
return (
|
||||
<AttachmentSection
|
||||
|
|
|
@ -24,6 +24,7 @@ import { Emoji } from './Emoji';
|
|||
import { dataByCategory, search } from './lib';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { isSingleGrapheme } from '../../util/grapheme';
|
||||
import { missingCaseError } from '../../util';
|
||||
|
||||
export type EmojiPickDataType = {
|
||||
skinTone?: number;
|
||||
|
@ -61,7 +62,9 @@ const categories = [
|
|||
'object',
|
||||
'symbol',
|
||||
'flag',
|
||||
];
|
||||
] as const;
|
||||
|
||||
type Category = typeof categories[number];
|
||||
|
||||
export const EmojiPicker = React.memo(
|
||||
React.forwardRef<HTMLDivElement, Props>(
|
||||
|
@ -80,7 +83,7 @@ export const EmojiPicker = React.memo(
|
|||
ref
|
||||
) => {
|
||||
const [firstRecent] = React.useState(recentEmojis);
|
||||
const [selectedCategory, setSelectedCategory] = React.useState(
|
||||
const [selectedCategory, setSelectedCategory] = React.useState<Category>(
|
||||
categories[0]
|
||||
);
|
||||
const [searchMode, setSearchMode] = React.useState(false);
|
||||
|
@ -277,7 +280,7 @@ export const EmojiPicker = React.memo(
|
|||
|
||||
const { category } = e.currentTarget.dataset;
|
||||
if (category) {
|
||||
setSelectedCategory(category);
|
||||
setSelectedCategory(category as Category);
|
||||
setScrollToRow(catToRowOffsets[category]);
|
||||
}
|
||||
},
|
||||
|
@ -332,11 +335,36 @@ export const EmojiPicker = React.memo(
|
|||
findLast(catOffsetEntries, ([, row]) => rowStartIndex >= row) ||
|
||||
categories;
|
||||
|
||||
setSelectedCategory(cat);
|
||||
setSelectedCategory(cat as Category);
|
||||
}, 10),
|
||||
[catOffsetEntries]
|
||||
);
|
||||
|
||||
function getCategoryButtonLabel(category: Category): string {
|
||||
switch (category) {
|
||||
case 'recents':
|
||||
return i18n('EmojiPicker__button--recents');
|
||||
case 'emoji':
|
||||
return i18n('EmojiPicker__button--emoji');
|
||||
case 'animal':
|
||||
return i18n('EmojiPicker__button--animal');
|
||||
case 'food':
|
||||
return i18n('EmojiPicker__button--food');
|
||||
case 'activity':
|
||||
return i18n('EmojiPicker__button--activity');
|
||||
case 'travel':
|
||||
return i18n('EmojiPicker__button--travel');
|
||||
case 'object':
|
||||
return i18n('EmojiPicker__button--object');
|
||||
case 'symbol':
|
||||
return i18n('EmojiPicker__button--symbol');
|
||||
case 'flag':
|
||||
return i18n('EmojiPicker__button--flag');
|
||||
default:
|
||||
throw missingCaseError(category);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
|
@ -394,7 +422,7 @@ export const EmojiPicker = React.memo(
|
|||
? 'module-emoji-picker__button--selected'
|
||||
: null
|
||||
)}
|
||||
aria-label={i18n(`EmojiPicker__button--${cat}`)}
|
||||
aria-label={getCategoryButtonLabel(cat)}
|
||||
/>
|
||||
)
|
||||
)
|
||||
|
|
|
@ -314,7 +314,7 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
getHeaderText: i18n => i18n('contactsHeader'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -342,7 +342,7 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByPhoneNumberHeader',
|
||||
getHeaderText: i18n => i18n('findByPhoneNumberHeader'),
|
||||
};
|
||||
}
|
||||
if (virtualRowIndex === 1) {
|
||||
|
@ -363,7 +363,7 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
getHeaderText: i18n => i18n('findByUsernameHeader'),
|
||||
};
|
||||
}
|
||||
if (virtualRowIndex === 1) {
|
||||
|
|
|
@ -194,7 +194,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
getHeaderText: i18n => i18n('contactsHeader'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'groupsHeader',
|
||||
getHeaderText: i18n => i18n('groupsHeader'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -236,7 +236,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
getHeaderText: i18n => i18n('findByUsernameHeader'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -258,7 +258,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByPhoneNumberHeader',
|
||||
getHeaderText: i18n => i18n('findByPhoneNumberHeader'),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -150,12 +150,12 @@ export class LeftPaneInboxHelper extends LeftPaneHelper<LeftPaneInboxPropsType>
|
|||
case 0:
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--pinned',
|
||||
getHeaderText: i18n => i18n('LeftPane--pinned'),
|
||||
};
|
||||
case pinnedConversations.length + 1:
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--chats',
|
||||
getHeaderText: i18n => i18n('LeftPane--chats'),
|
||||
};
|
||||
case pinnedConversations.length + conversations.length + 2:
|
||||
if (archivedConversationsCount) {
|
||||
|
|
|
@ -229,7 +229,7 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
|
|||
if (rowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'conversationsHeader',
|
||||
getHeaderText: i18n => i18n('conversationsHeader'),
|
||||
};
|
||||
}
|
||||
assertDev(
|
||||
|
@ -250,7 +250,7 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
|
|||
if (localIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
getHeaderText: i18n => i18n('contactsHeader'),
|
||||
};
|
||||
}
|
||||
assertDev(
|
||||
|
@ -274,7 +274,7 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
|
|||
if (localIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
getHeaderText: i18n => i18n('messagesHeader'),
|
||||
};
|
||||
}
|
||||
assertDev(
|
||||
|
|
|
@ -258,7 +258,7 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper<LeftPaneSetGr
|
|||
if (rowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'setGroupMetadata__members-header',
|
||||
getHeaderText: i18n => i18n('setGroupMetadata__members-header'),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,18 @@ export function renderChangeDetail<T>(
|
|||
detail: GroupV2ChangeDetailType,
|
||||
options: RenderOptionsType<T>
|
||||
): T | string | ReadonlyArray<T | string> {
|
||||
const { from, i18n, ourACI, ourPNI, renderContact, renderString } = options;
|
||||
const {
|
||||
from,
|
||||
i18n: localizer,
|
||||
ourACI,
|
||||
ourPNI,
|
||||
renderContact,
|
||||
renderString,
|
||||
} = options;
|
||||
|
||||
function i18n(id: string, components?: ReplacementValuesType<T | string>) {
|
||||
return renderString(id, localizer, components);
|
||||
}
|
||||
|
||||
const isOurUuid = (uuid?: UUIDStringType): boolean => {
|
||||
if (!uuid) {
|
||||
|
@ -79,88 +90,88 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (detail.type === 'create') {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--create--you', i18n);
|
||||
return i18n('GroupV2--create--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--create--other', i18n, {
|
||||
return i18n('GroupV2--create--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--create--unknown', i18n);
|
||||
return i18n('GroupV2--create--unknown');
|
||||
}
|
||||
if (detail.type === 'title') {
|
||||
const { newTitle } = detail;
|
||||
|
||||
if (newTitle) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--title--change--you', i18n, { newTitle });
|
||||
return i18n('GroupV2--title--change--you', { newTitle });
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--title--change--other', i18n, {
|
||||
return i18n('GroupV2--title--change--other', {
|
||||
memberName: renderContact(from),
|
||||
newTitle,
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--title--change--unknown', i18n, {
|
||||
return i18n('GroupV2--title--change--unknown', {
|
||||
newTitle,
|
||||
});
|
||||
}
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--title--remove--you', i18n);
|
||||
return i18n('GroupV2--title--remove--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--title--remove--other', i18n, {
|
||||
return i18n('GroupV2--title--remove--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--title--remove--unknown', i18n);
|
||||
return i18n('GroupV2--title--remove--unknown');
|
||||
}
|
||||
if (detail.type === 'avatar') {
|
||||
if (detail.removed) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--avatar--remove--you', i18n);
|
||||
return i18n('GroupV2--avatar--remove--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--avatar--remove--other', i18n, {
|
||||
return i18n('GroupV2--avatar--remove--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--avatar--remove--unknown', i18n);
|
||||
return i18n('GroupV2--avatar--remove--unknown');
|
||||
}
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--avatar--change--you', i18n);
|
||||
return i18n('GroupV2--avatar--change--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--avatar--change--other', i18n, {
|
||||
return i18n('GroupV2--avatar--change--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--avatar--change--unknown', i18n);
|
||||
return i18n('GroupV2--avatar--change--unknown');
|
||||
}
|
||||
if (detail.type === 'access-attributes') {
|
||||
const { newPrivilege } = detail;
|
||||
|
||||
if (newPrivilege === AccessControlEnum.ADMINISTRATOR) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--access-attributes--admins--you', i18n);
|
||||
return i18n('GroupV2--access-attributes--admins--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--access-attributes--admins--other', i18n, {
|
||||
return i18n('GroupV2--access-attributes--admins--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--access-attributes--admins--unknown', i18n);
|
||||
return i18n('GroupV2--access-attributes--admins--unknown');
|
||||
}
|
||||
if (newPrivilege === AccessControlEnum.MEMBER) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--access-attributes--all--you', i18n);
|
||||
return i18n('GroupV2--access-attributes--all--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--access-attributes--all--other', i18n, {
|
||||
return i18n('GroupV2--access-attributes--all--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--access-attributes--all--unknown', i18n);
|
||||
return i18n('GroupV2--access-attributes--all--unknown');
|
||||
}
|
||||
log.warn(
|
||||
`access-attributes change type, privilege ${newPrivilege} is unknown`
|
||||
|
@ -172,25 +183,25 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (newPrivilege === AccessControlEnum.ADMINISTRATOR) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--access-members--admins--you', i18n);
|
||||
return i18n('GroupV2--access-members--admins--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--access-members--admins--other', i18n, {
|
||||
return i18n('GroupV2--access-members--admins--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--access-members--admins--unknown', i18n);
|
||||
return i18n('GroupV2--access-members--admins--unknown');
|
||||
}
|
||||
if (newPrivilege === AccessControlEnum.MEMBER) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--access-members--all--you', i18n);
|
||||
return i18n('GroupV2--access-members--all--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--access-members--all--other', i18n, {
|
||||
return i18n('GroupV2--access-members--all--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--access-members--all--unknown', i18n);
|
||||
return i18n('GroupV2--access-members--all--unknown');
|
||||
}
|
||||
log.warn(
|
||||
`access-members change type, privilege ${newPrivilege} is unknown`
|
||||
|
@ -202,35 +213,29 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (newPrivilege === AccessControlEnum.ADMINISTRATOR) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--access-invite-link--enabled--you', i18n);
|
||||
return i18n('GroupV2--access-invite-link--enabled--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--access-invite-link--enabled--other',
|
||||
i18n,
|
||||
|
||||
{ adminName: renderContact(from) }
|
||||
);
|
||||
}
|
||||
return renderString(
|
||||
'GroupV2--access-invite-link--enabled--unknown',
|
||||
i18n
|
||||
);
|
||||
return i18n('GroupV2--access-invite-link--enabled--unknown');
|
||||
}
|
||||
if (newPrivilege === AccessControlEnum.ANY) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--access-invite-link--disabled--you', i18n);
|
||||
return i18n('GroupV2--access-invite-link--disabled--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--access-invite-link--disabled--other',
|
||||
i18n,
|
||||
|
||||
{ adminName: renderContact(from) }
|
||||
);
|
||||
}
|
||||
return renderString(
|
||||
'GroupV2--access-invite-link--disabled--unknown',
|
||||
i18n
|
||||
);
|
||||
return i18n('GroupV2--access-invite-link--disabled--unknown');
|
||||
}
|
||||
log.warn(
|
||||
`access-invite-link change type, privilege ${newPrivilege} is unknown`
|
||||
|
@ -243,27 +248,27 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (weAreJoiner) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--member-add--you--you', i18n);
|
||||
return i18n('GroupV2--member-add--you--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--member-add--you--other', i18n, {
|
||||
return i18n('GroupV2--member-add--you--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--member-add--you--unknown', i18n);
|
||||
return i18n('GroupV2--member-add--you--unknown');
|
||||
}
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--member-add--other--you', i18n, {
|
||||
return i18n('GroupV2--member-add--other--you', {
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--member-add--other--other', i18n, {
|
||||
return i18n('GroupV2--member-add--other--other', {
|
||||
adderName: renderContact(from),
|
||||
addeeName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--member-add--other--unknown', i18n, {
|
||||
return i18n('GroupV2--member-add--other--unknown', {
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
|
@ -276,54 +281,51 @@ export function renderChangeDetail<T>(
|
|||
if (weAreJoiner) {
|
||||
// They can't be the same, no fromYou check here
|
||||
if (from) {
|
||||
return renderString('GroupV2--member-add--you--other', i18n, {
|
||||
return i18n('GroupV2--member-add--you--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--member-add--you--unknown', i18n);
|
||||
return i18n('GroupV2--member-add--you--unknown');
|
||||
}
|
||||
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--member-add--invited--you', i18n, {
|
||||
return i18n('GroupV2--member-add--invited--you', {
|
||||
inviteeName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--member-add--invited--other', i18n, {
|
||||
return i18n('GroupV2--member-add--invited--other', {
|
||||
memberName: renderContact(from),
|
||||
inviteeName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--member-add--invited--unknown', i18n, {
|
||||
return i18n('GroupV2--member-add--invited--unknown', {
|
||||
inviteeName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
|
||||
if (weAreJoiner) {
|
||||
if (inviter) {
|
||||
return renderString('GroupV2--member-add--from-invite--you', i18n, {
|
||||
return i18n('GroupV2--member-add--from-invite--you', {
|
||||
inviterName: renderContact(inviter),
|
||||
});
|
||||
}
|
||||
return renderString(
|
||||
'GroupV2--member-add--from-invite--you-no-from',
|
||||
i18n
|
||||
);
|
||||
return i18n('GroupV2--member-add--from-invite--you-no-from');
|
||||
}
|
||||
if (weAreInviter) {
|
||||
return renderString('GroupV2--member-add--from-invite--from-you', i18n, {
|
||||
return i18n('GroupV2--member-add--from-invite--from-you', {
|
||||
inviteeName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
if (inviter) {
|
||||
return renderString('GroupV2--member-add--from-invite--other', i18n, {
|
||||
return i18n('GroupV2--member-add--from-invite--other', {
|
||||
inviteeName: renderContact(uuid),
|
||||
inviterName: renderContact(inviter),
|
||||
});
|
||||
}
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--member-add--from-invite--other-no-from',
|
||||
i18n,
|
||||
|
||||
{
|
||||
inviteeName: renderContact(uuid),
|
||||
}
|
||||
|
@ -333,10 +335,10 @@ export function renderChangeDetail<T>(
|
|||
const { uuid } = detail;
|
||||
|
||||
if (fromYou && isOurUuid(uuid)) {
|
||||
return renderString('GroupV2--member-add-from-link--you--you', i18n);
|
||||
return i18n('GroupV2--member-add-from-link--you--you');
|
||||
}
|
||||
if (from && uuid === from) {
|
||||
return renderString('GroupV2--member-add-from-link--other', i18n, {
|
||||
return i18n('GroupV2--member-add-from-link--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
|
@ -344,7 +346,7 @@ export function renderChangeDetail<T>(
|
|||
// Note: this shouldn't happen, because we only capture 'add-from-link' status
|
||||
// from group change events, which always have a sender.
|
||||
log.warn('member-add-from-link change type; we have no from!');
|
||||
return renderString('GroupV2--member-add--other--unknown', i18n, {
|
||||
return i18n('GroupV2--member-add--other--unknown', {
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
|
@ -354,9 +356,9 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (weAreJoiner) {
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--member-add-from-admin-approval--you--other',
|
||||
i18n,
|
||||
|
||||
{ adminName: renderContact(from) }
|
||||
);
|
||||
}
|
||||
|
@ -366,23 +368,20 @@ export function renderChangeDetail<T>(
|
|||
log.warn(
|
||||
'member-add-from-admin-approval change type; we have no from, and we are joiner!'
|
||||
);
|
||||
return renderString(
|
||||
'GroupV2--member-add-from-admin-approval--you--unknown',
|
||||
i18n
|
||||
);
|
||||
return i18n('GroupV2--member-add-from-admin-approval--you--unknown');
|
||||
}
|
||||
|
||||
if (fromYou) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--member-add-from-admin-approval--other--you',
|
||||
i18n,
|
||||
|
||||
{ joinerName: renderContact(uuid) }
|
||||
);
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--member-add-from-admin-approval--other--other',
|
||||
i18n,
|
||||
|
||||
{
|
||||
adminName: renderContact(from),
|
||||
joinerName: renderContact(uuid),
|
||||
|
@ -393,9 +392,9 @@ export function renderChangeDetail<T>(
|
|||
// Note: this shouldn't happen, because we only capture 'add-from-admin-approval'
|
||||
// status from group change events, which always have a sender.
|
||||
log.warn('member-add-from-admin-approval change type; we have no from');
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--member-add-from-admin-approval--other--unknown',
|
||||
i18n,
|
||||
|
||||
{ joinerName: renderContact(uuid) }
|
||||
);
|
||||
}
|
||||
|
@ -405,33 +404,33 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (weAreLeaver) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--member-remove--you--you', i18n);
|
||||
return i18n('GroupV2--member-remove--you--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--member-remove--you--other', i18n, {
|
||||
return i18n('GroupV2--member-remove--you--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--member-remove--you--unknown', i18n);
|
||||
return i18n('GroupV2--member-remove--you--unknown');
|
||||
}
|
||||
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--member-remove--other--you', i18n, {
|
||||
return i18n('GroupV2--member-remove--other--you', {
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
if (from && from === uuid) {
|
||||
return renderString('GroupV2--member-remove--other--self', i18n, {
|
||||
return i18n('GroupV2--member-remove--other--self', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--member-remove--other--other', i18n, {
|
||||
return i18n('GroupV2--member-remove--other--other', {
|
||||
adminName: renderContact(from),
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--member-remove--other--unknown', i18n, {
|
||||
return i18n('GroupV2--member-remove--other--unknown', {
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
|
@ -442,77 +441,59 @@ export function renderChangeDetail<T>(
|
|||
if (newPrivilege === RoleEnum.ADMINISTRATOR) {
|
||||
if (weAreMember) {
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--member-privilege--promote--you--other',
|
||||
i18n,
|
||||
|
||||
{ adminName: renderContact(from) }
|
||||
);
|
||||
}
|
||||
|
||||
return renderString(
|
||||
'GroupV2--member-privilege--promote--you--unknown',
|
||||
i18n
|
||||
);
|
||||
return i18n('GroupV2--member-privilege--promote--you--unknown');
|
||||
}
|
||||
|
||||
if (fromYou) {
|
||||
return renderString(
|
||||
'GroupV2--member-privilege--promote--other--you',
|
||||
i18n,
|
||||
{ memberName: renderContact(uuid) }
|
||||
);
|
||||
return i18n('GroupV2--member-privilege--promote--other--you', {
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
'GroupV2--member-privilege--promote--other--other',
|
||||
i18n,
|
||||
{
|
||||
adminName: renderContact(from),
|
||||
memberName: renderContact(uuid),
|
||||
}
|
||||
);
|
||||
return i18n('GroupV2--member-privilege--promote--other--other', {
|
||||
adminName: renderContact(from),
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
return renderString(
|
||||
'GroupV2--member-privilege--promote--other--unknown',
|
||||
i18n,
|
||||
{ memberName: renderContact(uuid) }
|
||||
);
|
||||
return i18n('GroupV2--member-privilege--promote--other--unknown', {
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
if (newPrivilege === RoleEnum.DEFAULT) {
|
||||
if (weAreMember) {
|
||||
if (from) {
|
||||
return renderString(
|
||||
'GroupV2--member-privilege--demote--you--other',
|
||||
i18n,
|
||||
{ adminName: renderContact(from) }
|
||||
);
|
||||
return i18n('GroupV2--member-privilege--demote--you--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString(
|
||||
'GroupV2--member-privilege--demote--you--unknown',
|
||||
i18n
|
||||
);
|
||||
return i18n('GroupV2--member-privilege--demote--you--unknown');
|
||||
}
|
||||
|
||||
if (fromYou) {
|
||||
return renderString(
|
||||
'GroupV2--member-privilege--demote--other--you',
|
||||
i18n,
|
||||
{ memberName: renderContact(uuid) }
|
||||
);
|
||||
return i18n('GroupV2--member-privilege--demote--other--you', {
|
||||
memberName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--member-privilege--demote--other--other',
|
||||
i18n,
|
||||
|
||||
{
|
||||
adminName: renderContact(from),
|
||||
memberName: renderContact(uuid),
|
||||
}
|
||||
);
|
||||
}
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--member-privilege--demote--other--unknown',
|
||||
i18n,
|
||||
|
||||
{ memberName: renderContact(uuid) }
|
||||
);
|
||||
}
|
||||
|
@ -526,39 +507,39 @@ export function renderChangeDetail<T>(
|
|||
const weAreInvited = isOurUuid(uuid);
|
||||
if (weAreInvited) {
|
||||
if (from) {
|
||||
return renderString('GroupV2--pending-add--one--you--other', i18n, {
|
||||
return i18n('GroupV2--pending-add--one--you--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--pending-add--one--you--unknown', i18n);
|
||||
return i18n('GroupV2--pending-add--one--you--unknown');
|
||||
}
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--pending-add--one--other--you', i18n, {
|
||||
return i18n('GroupV2--pending-add--one--other--you', {
|
||||
inviteeName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--pending-add--one--other--other', i18n, {
|
||||
return i18n('GroupV2--pending-add--one--other--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--pending-add--one--other--unknown', i18n);
|
||||
return i18n('GroupV2--pending-add--one--other--unknown');
|
||||
}
|
||||
if (detail.type === 'pending-add-many') {
|
||||
const { count } = detail;
|
||||
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--pending-add--many--you', i18n, {
|
||||
return i18n('GroupV2--pending-add--many--you', {
|
||||
count: count.toString(),
|
||||
});
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--pending-add--many--other', i18n, {
|
||||
return i18n('GroupV2--pending-add--many--other', {
|
||||
memberName: renderContact(from),
|
||||
count: count.toString(),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--pending-add--many--unknown', i18n, {
|
||||
return i18n('GroupV2--pending-add--many--unknown', {
|
||||
count: count.toString(),
|
||||
});
|
||||
}
|
||||
|
@ -571,91 +552,91 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (weAreInviter) {
|
||||
if (sentByInvited) {
|
||||
return renderString('GroupV2--pending-remove--decline--you', i18n, {
|
||||
return i18n('GroupV2--pending-remove--decline--you', {
|
||||
inviteeName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
if (fromYou) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from-you--one--you',
|
||||
i18n,
|
||||
|
||||
{ inviteeName: renderContact(uuid) }
|
||||
);
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from-you--one--other',
|
||||
i18n,
|
||||
|
||||
{
|
||||
adminName: renderContact(from),
|
||||
inviteeName: renderContact(uuid),
|
||||
}
|
||||
);
|
||||
}
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from-you--one--unknown',
|
||||
i18n,
|
||||
|
||||
{ inviteeName: renderContact(uuid) }
|
||||
);
|
||||
}
|
||||
if (sentByInvited) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--pending-remove--decline--from-you', i18n);
|
||||
return i18n('GroupV2--pending-remove--decline--from-you');
|
||||
}
|
||||
if (inviter) {
|
||||
return renderString('GroupV2--pending-remove--decline--other', i18n, {
|
||||
return i18n('GroupV2--pending-remove--decline--other', {
|
||||
memberName: renderContact(inviter),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--pending-remove--decline--unknown', i18n);
|
||||
return i18n('GroupV2--pending-remove--decline--unknown');
|
||||
}
|
||||
if (inviter && sentByInviter) {
|
||||
if (weAreInvited) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-own--to-you',
|
||||
i18n,
|
||||
|
||||
{ inviterName: renderContact(inviter) }
|
||||
);
|
||||
}
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-own--unknown',
|
||||
i18n,
|
||||
|
||||
{ inviterName: renderContact(inviter) }
|
||||
);
|
||||
}
|
||||
if (inviter) {
|
||||
if (fromYou) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from--one--you',
|
||||
i18n,
|
||||
|
||||
{ memberName: renderContact(inviter) }
|
||||
);
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from--one--other',
|
||||
i18n,
|
||||
|
||||
{
|
||||
adminName: renderContact(from),
|
||||
memberName: renderContact(inviter),
|
||||
}
|
||||
);
|
||||
}
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from--one--unknown',
|
||||
i18n,
|
||||
|
||||
{ memberName: renderContact(inviter) }
|
||||
);
|
||||
}
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--pending-remove--revoke--one--you', i18n);
|
||||
return i18n('GroupV2--pending-remove--revoke--one--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--pending-remove--revoke--one--other', i18n, {
|
||||
return i18n('GroupV2--pending-remove--revoke--one--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--pending-remove--revoke--one--unknown', i18n);
|
||||
return i18n('GroupV2--pending-remove--revoke--one--unknown');
|
||||
}
|
||||
if (detail.type === 'pending-remove-many') {
|
||||
const { count, inviter } = detail;
|
||||
|
@ -663,33 +644,33 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (weAreInviter) {
|
||||
if (fromYou) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from-you--many--you',
|
||||
i18n,
|
||||
|
||||
{ count: count.toString() }
|
||||
);
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from-you--many--other',
|
||||
i18n,
|
||||
|
||||
{
|
||||
adminName: renderContact(from),
|
||||
count: count.toString(),
|
||||
}
|
||||
);
|
||||
}
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from-you--many--unknown',
|
||||
i18n,
|
||||
|
||||
{ count: count.toString() }
|
||||
);
|
||||
}
|
||||
if (inviter) {
|
||||
if (fromYou) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from--many--you',
|
||||
i18n,
|
||||
|
||||
{
|
||||
count: count.toString(),
|
||||
memberName: renderContact(inviter),
|
||||
|
@ -697,9 +678,9 @@ export function renderChangeDetail<T>(
|
|||
);
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from--many--other',
|
||||
i18n,
|
||||
|
||||
{
|
||||
adminName: renderContact(from),
|
||||
count: count.toString(),
|
||||
|
@ -707,9 +688,9 @@ export function renderChangeDetail<T>(
|
|||
}
|
||||
);
|
||||
}
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke-invite-from--many--unknown',
|
||||
i18n,
|
||||
|
||||
{
|
||||
count: count.toString(),
|
||||
memberName: renderContact(inviter),
|
||||
|
@ -717,23 +698,23 @@ export function renderChangeDetail<T>(
|
|||
);
|
||||
}
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--pending-remove--revoke--many--you', i18n, {
|
||||
return i18n('GroupV2--pending-remove--revoke--many--you', {
|
||||
count: count.toString(),
|
||||
});
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke--many--other',
|
||||
i18n,
|
||||
|
||||
{
|
||||
memberName: renderContact(from),
|
||||
count: count.toString(),
|
||||
}
|
||||
);
|
||||
}
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--pending-remove--revoke--many--unknown',
|
||||
i18n,
|
||||
|
||||
{ count: count.toString() }
|
||||
);
|
||||
}
|
||||
|
@ -742,9 +723,9 @@ export function renderChangeDetail<T>(
|
|||
const weAreJoiner = isOurUuid(uuid);
|
||||
|
||||
if (weAreJoiner) {
|
||||
return renderString('GroupV2--admin-approval-add-one--you', i18n);
|
||||
return i18n('GroupV2--admin-approval-add-one--you');
|
||||
}
|
||||
return renderString('GroupV2--admin-approval-add-one--other', i18n, {
|
||||
return i18n('GroupV2--admin-approval-add-one--other', {
|
||||
joinerName: renderContact(uuid),
|
||||
});
|
||||
}
|
||||
|
@ -754,35 +735,29 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (weAreJoiner) {
|
||||
if (fromYou) {
|
||||
return renderString(
|
||||
'GroupV2--admin-approval-remove-one--you--you',
|
||||
i18n
|
||||
);
|
||||
return i18n('GroupV2--admin-approval-remove-one--you--you');
|
||||
}
|
||||
return renderString(
|
||||
'GroupV2--admin-approval-remove-one--you--unknown',
|
||||
i18n
|
||||
);
|
||||
return i18n('GroupV2--admin-approval-remove-one--you--unknown');
|
||||
}
|
||||
|
||||
if (fromYou) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--admin-approval-remove-one--other--you',
|
||||
i18n,
|
||||
|
||||
{ joinerName: renderContact(uuid) }
|
||||
);
|
||||
}
|
||||
if (from && from === uuid) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--admin-approval-remove-one--other--own',
|
||||
i18n,
|
||||
|
||||
{ joinerName: renderContact(uuid) }
|
||||
);
|
||||
}
|
||||
if (from) {
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--admin-approval-remove-one--other--other',
|
||||
i18n,
|
||||
|
||||
{
|
||||
adminName: renderContact(from),
|
||||
joinerName: renderContact(uuid),
|
||||
|
@ -792,9 +767,9 @@ export function renderChangeDetail<T>(
|
|||
|
||||
// We default to the user canceling their request, because it is far more likely that
|
||||
// if an admin does the denial, we'll get a change event from them.
|
||||
return renderString(
|
||||
return i18n(
|
||||
'GroupV2--admin-approval-remove-one--other--own',
|
||||
i18n,
|
||||
|
||||
{ joinerName: renderContact(uuid) }
|
||||
);
|
||||
}
|
||||
|
@ -803,11 +778,11 @@ export function renderChangeDetail<T>(
|
|||
|
||||
let firstMessage: T | string;
|
||||
if (times === 1) {
|
||||
firstMessage = renderString('GroupV2--admin-approval-bounce--one', i18n, {
|
||||
firstMessage = i18n('GroupV2--admin-approval-bounce--one', {
|
||||
joinerName: renderContact(uuid),
|
||||
});
|
||||
} else {
|
||||
firstMessage = renderString('GroupV2--admin-approval-bounce', i18n, {
|
||||
firstMessage = i18n('GroupV2--admin-approval-bounce', {
|
||||
joinerName: renderContact(uuid),
|
||||
numberOfRequests: String(times),
|
||||
});
|
||||
|
@ -835,99 +810,99 @@ export function renderChangeDetail<T>(
|
|||
|
||||
if (privilege === AccessControlEnum.ADMINISTRATOR) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--group-link-add--enabled--you', i18n);
|
||||
return i18n('GroupV2--group-link-add--enabled--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--group-link-add--enabled--other', i18n, {
|
||||
return i18n('GroupV2--group-link-add--enabled--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--group-link-add--enabled--unknown', i18n);
|
||||
return i18n('GroupV2--group-link-add--enabled--unknown');
|
||||
}
|
||||
if (privilege === AccessControlEnum.ANY) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--group-link-add--disabled--you', i18n);
|
||||
return i18n('GroupV2--group-link-add--disabled--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--group-link-add--disabled--other', i18n, {
|
||||
return i18n('GroupV2--group-link-add--disabled--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--group-link-add--disabled--unknown', i18n);
|
||||
return i18n('GroupV2--group-link-add--disabled--unknown');
|
||||
}
|
||||
log.warn(`group-link-add change type, privilege ${privilege} is unknown`);
|
||||
return '';
|
||||
}
|
||||
if (detail.type === 'group-link-reset') {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--group-link-reset--you', i18n);
|
||||
return i18n('GroupV2--group-link-reset--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--group-link-reset--other', i18n, {
|
||||
return i18n('GroupV2--group-link-reset--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--group-link-reset--unknown', i18n);
|
||||
return i18n('GroupV2--group-link-reset--unknown');
|
||||
}
|
||||
if (detail.type === 'group-link-remove') {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--group-link-remove--you', i18n);
|
||||
return i18n('GroupV2--group-link-remove--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--group-link-remove--other', i18n, {
|
||||
return i18n('GroupV2--group-link-remove--other', {
|
||||
adminName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--group-link-remove--unknown', i18n);
|
||||
return i18n('GroupV2--group-link-remove--unknown');
|
||||
}
|
||||
if (detail.type === 'description') {
|
||||
if (detail.removed) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--description--remove--you', i18n);
|
||||
return i18n('GroupV2--description--remove--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--description--remove--other', i18n, {
|
||||
return i18n('GroupV2--description--remove--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--description--remove--unknown', i18n);
|
||||
return i18n('GroupV2--description--remove--unknown');
|
||||
}
|
||||
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--description--change--you', i18n);
|
||||
return i18n('GroupV2--description--change--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--description--change--other', i18n, {
|
||||
return i18n('GroupV2--description--change--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--description--change--unknown', i18n);
|
||||
return i18n('GroupV2--description--change--unknown');
|
||||
}
|
||||
if (detail.type === 'announcements-only') {
|
||||
if (detail.announcementsOnly) {
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--announcements--admin--you', i18n);
|
||||
return i18n('GroupV2--announcements--admin--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--announcements--admin--other', i18n, {
|
||||
return i18n('GroupV2--announcements--admin--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--announcements--admin--unknown', i18n);
|
||||
return i18n('GroupV2--announcements--admin--unknown');
|
||||
}
|
||||
|
||||
if (fromYou) {
|
||||
return renderString('GroupV2--announcements--member--you', i18n);
|
||||
return i18n('GroupV2--announcements--member--you');
|
||||
}
|
||||
if (from) {
|
||||
return renderString('GroupV2--announcements--member--other', i18n, {
|
||||
return i18n('GroupV2--announcements--member--other', {
|
||||
memberName: renderContact(from),
|
||||
});
|
||||
}
|
||||
return renderString('GroupV2--announcements--member--unknown', i18n);
|
||||
return i18n('GroupV2--announcements--member--unknown');
|
||||
}
|
||||
if (detail.type === 'summary') {
|
||||
return renderString('icu:GroupV2--summary', i18n);
|
||||
return i18n('icu:GroupV2--summary');
|
||||
}
|
||||
|
||||
throw missingCaseError(detail);
|
||||
|
|
|
@ -511,7 +511,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
key: string,
|
||||
_i18n: unknown,
|
||||
components: ReplacementValuesType<string> | undefined
|
||||
) => window.i18n(key, components),
|
||||
) => {
|
||||
// eslint-disable-next-line local-rules/valid-i18n-keys
|
||||
return window.i18n(key, components);
|
||||
},
|
||||
});
|
||||
|
||||
return { text: changes.map(({ text }) => text).join(' ') };
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { times } from 'lodash';
|
||||
import { RowType } from '../../../components/ConversationList';
|
||||
import { RowType, _testHeaderText } from '../../../components/ConversationList';
|
||||
import { ContactCheckboxDisabledReason } from '../../../components/conversationList/ContactCheckbox';
|
||||
import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation';
|
||||
|
||||
|
@ -117,10 +117,7 @@ describe('LeftPaneChooseGroupMembersHelper', () => {
|
|||
selectedContacts: [candidateContacts[1]],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(0)), 'contactsHeader');
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.ContactCheckbox,
|
||||
contact: candidateContacts[0],
|
||||
|
@ -167,10 +164,10 @@ describe('LeftPaneChooseGroupMembersHelper', () => {
|
|||
selectedContacts: [],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByPhoneNumberHeader',
|
||||
});
|
||||
assert.deepEqual(
|
||||
_testHeaderText(helper.getRow(0)),
|
||||
'findByPhoneNumberHeader'
|
||||
);
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.PhoneNumberCheckbox,
|
||||
phoneNumber: {
|
||||
|
@ -192,10 +189,10 @@ describe('LeftPaneChooseGroupMembersHelper', () => {
|
|||
selectedContacts: [],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
});
|
||||
assert.deepEqual(
|
||||
_testHeaderText(helper.getRow(0)),
|
||||
'findByUsernameHeader'
|
||||
);
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.UsernameCheckbox,
|
||||
username: 'signal',
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { RowType } from '../../../components/ConversationList';
|
||||
import { RowType, _testHeaderText } from '../../../components/ConversationList';
|
||||
import { FindDirection } from '../../../components/leftPane/LeftPaneHelper';
|
||||
import {
|
||||
getDefaultConversation,
|
||||
|
@ -223,10 +223,7 @@ describe('LeftPaneComposeHelper', () => {
|
|||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.CreateNewGroup,
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(1)), 'contactsHeader');
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Contact,
|
||||
contact: composeContacts[0],
|
||||
|
@ -258,11 +255,7 @@ describe('LeftPaneComposeHelper', () => {
|
|||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.CreateNewGroup,
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(1)), 'contactsHeader');
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Contact,
|
||||
contact: composeContacts[0],
|
||||
|
@ -271,10 +264,7 @@ describe('LeftPaneComposeHelper', () => {
|
|||
type: RowType.Contact,
|
||||
contact: composeContacts[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'groupsHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(4)), 'groupsHeader');
|
||||
assert.deepEqual(helper.getRow(5), {
|
||||
type: RowType.SelectSingleGroup,
|
||||
group: composeGroups[0],
|
||||
|
@ -333,10 +323,10 @@ describe('LeftPaneComposeHelper', () => {
|
|||
uuidFetchState: {},
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByPhoneNumberHeader',
|
||||
});
|
||||
assert.deepEqual(
|
||||
_testHeaderText(helper.getRow(0)),
|
||||
'findByPhoneNumberHeader'
|
||||
);
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.StartNewConversation,
|
||||
phoneNumber: {
|
||||
|
@ -363,10 +353,10 @@ describe('LeftPaneComposeHelper', () => {
|
|||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
});
|
||||
assert.deepEqual(
|
||||
_testHeaderText(helper.getRow(0)),
|
||||
'findByUsernameHeader'
|
||||
);
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.UsernameSearchResult,
|
||||
username,
|
||||
|
@ -389,10 +379,7 @@ describe('LeftPaneComposeHelper', () => {
|
|||
uuidFetchState: {},
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(0)), 'contactsHeader');
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Contact,
|
||||
contact: composeContacts[0],
|
||||
|
@ -401,10 +388,10 @@ describe('LeftPaneComposeHelper', () => {
|
|||
type: RowType.Contact,
|
||||
contact: composeContacts[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByPhoneNumberHeader',
|
||||
});
|
||||
assert.deepEqual(
|
||||
_testHeaderText(helper.getRow(3)),
|
||||
'findByPhoneNumberHeader'
|
||||
);
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.StartNewConversation,
|
||||
phoneNumber: {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { RowType } from '../../../components/ConversationList';
|
||||
import { RowType, _testHeaderText } from '../../../components/ConversationList';
|
||||
import { FindDirection } from '../../../components/leftPane/LeftPaneHelper';
|
||||
import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation';
|
||||
|
||||
|
@ -339,10 +339,7 @@ describe('LeftPaneInboxHelper', () => {
|
|||
pinnedConversations,
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--pinned',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(0)), 'LeftPane--pinned');
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[0],
|
||||
|
@ -351,10 +348,7 @@ describe('LeftPaneInboxHelper', () => {
|
|||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--chats',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(3)), 'LeftPane--chats');
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
|
@ -388,10 +382,7 @@ describe('LeftPaneInboxHelper', () => {
|
|||
archivedConversations: [getDefaultConversation()],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--pinned',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(0)), 'LeftPane--pinned');
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[0],
|
||||
|
@ -400,10 +391,7 @@ describe('LeftPaneInboxHelper', () => {
|
|||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--chats',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(3)), 'LeftPane--chats');
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { RowType } from '../../../components/ConversationList';
|
||||
import { RowType, _testHeaderText } from '../../../components/ConversationList';
|
||||
import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation';
|
||||
|
||||
import { LeftPaneSearchHelper } from '../../../components/leftPane/LeftPaneSearchHelper';
|
||||
|
@ -192,10 +192,10 @@ describe('LeftPaneSearchHelper', () => {
|
|||
startSearchCounter: 0,
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'conversationsHeader',
|
||||
});
|
||||
assert.deepEqual(
|
||||
_testHeaderText(helper.getRow(0)),
|
||||
'conversationsHeader'
|
||||
);
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
|
@ -204,18 +204,12 @@ describe('LeftPaneSearchHelper', () => {
|
|||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(3)), 'contactsHeader');
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Conversation,
|
||||
conversation: contacts[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(5), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(5)), 'messagesHeader');
|
||||
assert.deepEqual(helper.getRow(6), {
|
||||
type: RowType.MessageSearchResult,
|
||||
messageId: messages[0].id,
|
||||
|
@ -244,18 +238,12 @@ describe('LeftPaneSearchHelper', () => {
|
|||
startSearchCounter: 0,
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(0)), 'contactsHeader');
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: contacts[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(2)), 'messagesHeader');
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.MessageSearchResult,
|
||||
messageId: messages[0].id,
|
||||
|
@ -287,10 +275,10 @@ describe('LeftPaneSearchHelper', () => {
|
|||
startSearchCounter: 0,
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'conversationsHeader',
|
||||
});
|
||||
assert.deepEqual(
|
||||
_testHeaderText(helper.getRow(0)),
|
||||
'conversationsHeader'
|
||||
);
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
|
@ -299,10 +287,7 @@ describe('LeftPaneSearchHelper', () => {
|
|||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(3)), 'messagesHeader');
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.MessageSearchResult,
|
||||
messageId: messages[0].id,
|
||||
|
@ -332,10 +317,7 @@ describe('LeftPaneSearchHelper', () => {
|
|||
startSearchCounter: 0,
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'conversationsHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(0)), 'conversationsHeader');
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
|
@ -344,10 +326,7 @@ describe('LeftPaneSearchHelper', () => {
|
|||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(_testHeaderText(helper.getRow(3)), 'contactsHeader');
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Conversation,
|
||||
conversation: contacts[0],
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { RowType } from '../../../components/ConversationList';
|
||||
import { RowType, _testHeaderText } from '../../../components/ConversationList';
|
||||
import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation';
|
||||
import { DurationInSeconds } from '../../../util/durations';
|
||||
|
||||
|
@ -92,10 +92,10 @@ describe('LeftPaneSetGroupMetadataHelper', () => {
|
|||
selectedContacts,
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'setGroupMetadata__members-header',
|
||||
});
|
||||
assert.deepEqual(
|
||||
_testHeaderText(helper.getRow(0)),
|
||||
'setGroupMetadata__members-header'
|
||||
);
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Contact,
|
||||
contact: selectedContacts[0],
|
||||
|
|
Loading…
Reference in a new issue