diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 5944b8856f..1f0bcc44e1 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -1826,6 +1826,14 @@
"messageformat": "Choose chats that you want to appear in this folder",
"description": "Preferences > Edit Chat Folder Page > Included Chats Section > Help"
},
+ "icu:Preferences__EditChatFolderPage__IncludedChatsSection__DirectChats": {
+ "messageformat": "1:1 chats",
+ "description": "Preferences > Edit Chat Folder Page > Included Chats Section > Direct Chats"
+ },
+ "icu:Preferences__EditChatFolderPage__IncludedChatsSection__GroupChats": {
+ "messageformat": "Group chats",
+ "description": "Preferences > Edit Chat Folder Page > Included Chats Section > Group Chats"
+ },
"icu:Preferences__EditChatFolderPage__IncludedChatsSection__AddChatsButton": {
"messageformat": "Add chats",
"description": "Preferences > Edit Chat Folder Page > Included Chats Section > Add Chats Button"
diff --git a/protos/SignalStorage.proto b/protos/SignalStorage.proto
index f9b25686f3..1b2f37bb91 100644
--- a/protos/SignalStorage.proto
+++ b/protos/SignalStorage.proto
@@ -373,7 +373,7 @@ message ChatFolderRecord {
FolderType folderType = 8;
repeated Recipient includedRecipients = 9;
repeated Recipient excludedRecipients = 10;
- uint64 deletedAtTimestampMs = 11; // When non-zero, `position` should be set to -1 and `includedRecipients` should be empty
+ uint64 deletedAtTimestampMs = 11; // When non-zero, `position` should be set to 4294967295 (2^32-1) and `includedRecipients` should be empty
}
message NotificationProfile {
diff --git a/ts/components/Preferences.stories.tsx b/ts/components/Preferences.stories.tsx
index 6f420e5aff..b486c7d9c3 100644
--- a/ts/components/Preferences.stories.tsx
+++ b/ts/components/Preferences.stories.tsx
@@ -39,6 +39,11 @@ import type {
OneTimeDonationHumanAmounts,
} from '../types/Donations';
import type { AnyToast } from '../types/Toast';
+import type { SmartPreferencesChatFoldersPageProps } from '../state/smart/PreferencesChatFoldersPage';
+import { PreferencesChatFoldersPage } from './preferences/chatFolders/PreferencesChatFoldersPage';
+import type { SmartPreferencesEditChatFolderPageProps } from '../state/smart/PreferencesEditChatFolderPage';
+import { PreferencesEditChatFolderPage } from './preferences/chatFolders/PreferencesEditChatFoldersPage';
+import { CHAT_FOLDER_DEFAULTS } from '../types/ChatFolder';
const { i18n } = window.SignalContext;
@@ -254,15 +259,47 @@ function renderToastManager(): JSX.Element {
return
;
}
+function renderPreferencesChatFoldersPage(
+ props: SmartPreferencesChatFoldersPageProps
+): JSX.Element {
+ return (
+
+ );
+}
+
+function renderPreferencesEditChatFolderPage(
+ props: SmartPreferencesEditChatFolderPageProps
+): JSX.Element {
+ return (
+ undefined}
+ />
+ );
+}
+
export default {
title: 'Components/Preferences',
component: Preferences,
args: {
i18n,
-
- conversations,
- conversationSelector,
-
accountEntropyPool:
'uy38jh2778hjjhj8lk19ga61s672jsj089r023s6a57809bap92j2yh5t326vv7t',
autoDownloadAttachment: {
@@ -398,8 +435,9 @@ export default {
renderProfileEditor,
renderToastManager,
renderUpdateDialog,
+ renderPreferencesChatFoldersPage,
+ renderPreferencesEditChatFolderPage,
getConversationsWithCustomColor: () => [],
- getPreferredBadge: () => undefined,
addCustomColor: action('addCustomColor'),
doDeleteAllData: action('doDeleteAllData'),
diff --git a/ts/components/Preferences.tsx b/ts/components/Preferences.tsx
index 2a8af52722..8868d00808 100644
--- a/ts/components/Preferences.tsx
+++ b/ts/components/Preferences.tsx
@@ -36,7 +36,7 @@ import { focusableSelector } from '../util/focusableSelectors';
import { Modal } from './Modal';
import { SearchInput } from './SearchInput';
import { removeDiacritics } from '../util/removeDiacritics';
-import { assertDev, strictAssert } from '../util/assert';
+import { assertDev } from '../util/assert';
import { I18n } from './I18n';
import { FunSkinTonesList } from './fun/FunSkinTones';
import { emojiParentKeyConstant, type EmojiSkinTone } from './fun/data/emojis';
@@ -91,27 +91,15 @@ import type {
PromptOSAuthResultType,
} from '../util/os/promptOSAuthMain';
import type { DonationReceipt } from '../types/Donations';
-import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
-import { EditChatFoldersPage } from './preferences/EditChatFoldersPage';
-import { ChatFoldersPage } from './preferences/ChatFoldersPage';
-import type {
- ChatFolderId,
- ChatFolderParams,
- ChatFolderRecord,
-} from '../types/ChatFolder';
-import {
- CHAT_FOLDER_DEFAULTS,
- isChatFoldersEnabled,
-} from '../types/ChatFolder';
-import type { GetConversationByIdType } from '../state/selectors/conversations';
+import type { ChatFolderId } from '../types/ChatFolder';
+import { isChatFoldersEnabled } from '../types/ChatFolder';
+import type { SmartPreferencesEditChatFolderPageProps } from '../state/smart/PreferencesEditChatFolderPage';
+import type { SmartPreferencesChatFoldersPageProps } from '../state/smart/PreferencesChatFoldersPage';
type CheckboxChangeHandlerType = (value: boolean) => unknown;
type SelectChangeHandlerType = (value: T) => unknown;
export type PropsDataType = {
- conversations: ReadonlyArray;
- conversationSelector: GetConversationByIdType;
-
// Settings
accountEntropyPool: string | undefined;
autoDownloadAttachment: AutoDownloadAttachmentType;
@@ -223,6 +211,12 @@ type PropsFunctionType = {
renderUpdateDialog: (
_: Readonly<{ containerWidthBreakpoint: WidthBreakpoint }>
) => JSX.Element;
+ renderPreferencesChatFoldersPage: (
+ props: SmartPreferencesChatFoldersPageProps
+ ) => JSX.Element;
+ renderPreferencesEditChatFolderPage: (
+ props: SmartPreferencesEditChatFolderPageProps
+ ) => JSX.Element;
// Other props
addCustomColor: (color: CustomColorType) => unknown;
@@ -236,7 +230,6 @@ type PropsFunctionType = {
resumeBackupMediaDownload: () => void;
pauseBackupMediaDownload: () => void;
getConversationsWithCustomColor: (colorId: string) => Array;
- getPreferredBadge: PreferredBadgeSelectorType;
makeSyncRequest: () => unknown;
onStartUpdate: () => unknown;
pickLocalBackupFolder: () => Promise;
@@ -362,8 +355,6 @@ const DEFAULT_ZOOM_FACTORS = [
];
export function Preferences({
- conversations,
- conversationSelector,
accountEntropyPool,
addCustomColor,
autoDownloadAttachment,
@@ -393,7 +384,6 @@ export function Preferences({
getConversationsWithCustomColor,
getMessageCountBySchemaVersion,
getMessageSampleForSchemaVersion,
- getPreferredBadge,
hasAudioNotifications,
hasAutoConvertEmoji,
hasAutoDownloadUpdate,
@@ -490,6 +480,8 @@ export function Preferences({
renderProfileEditor,
renderToastManager,
renderUpdateDialog,
+ renderPreferencesChatFoldersPage,
+ renderPreferencesEditChatFolderPage,
promptOSAuth,
resetAllChatColors,
resetDefaultChatColor,
@@ -540,10 +532,6 @@ export function Preferences({
const [confirmPnpNotDiscoverable, setConfirmPnpNoDiscoverable] =
useState(false);
- const [chatFolders, setChatFolders] = useState<
- ReadonlyArray
- >([]);
-
const [editChatFolderPageId, setEditChatFolderPageId] =
useState(null);
@@ -560,34 +548,6 @@ export function Preferences({
setEditChatFolderPageId(null);
}, [setPage]);
- const handleCreateChatFolder = useCallback((params: ChatFolderParams) => {
- setChatFolders(prev => {
- return [...prev, { ...params, id: String(prev.length) as ChatFolderId }];
- });
- }, []);
-
- const handleUpdateChatFolder = useCallback(
- (chatFolderId: ChatFolderId, chatFolderParams: ChatFolderParams) => {
- setChatFolders(prev => {
- return prev.map(chatFolder => {
- if (chatFolder.id === chatFolderId) {
- return { id: chatFolderId, ...chatFolderParams };
- }
- return chatFolder;
- });
- });
- },
- []
- );
-
- const handleDeleteChatFolder = useCallback((chatFolderId: ChatFolderId) => {
- setChatFolders(prev => {
- return prev.filter(chatFolder => {
- return chatFolder.id !== chatFolderId;
- });
- });
- }, []);
-
function closeLanguageDialog() {
setLanguageDialog(null);
setSelectedLanguageLocale(localeOverride);
@@ -1954,43 +1914,17 @@ export function Preferences({
/>
);
} else if (page === SettingsPage.ChatFolders) {
- content = (
- setPage(SettingsPage.Chats)}
- onOpenEditChatFoldersPage={handleOpenEditChatFoldersPage}
- chatFolders={chatFolders}
- onCreateChatFolder={handleCreateChatFolder}
- />
- );
+ content = renderPreferencesChatFoldersPage({
+ onBack: () => setPage(SettingsPage.Chats),
+ onOpenEditChatFoldersPage: handleOpenEditChatFoldersPage,
+ settingsPaneRef,
+ });
} else if (page === SettingsPage.EditChatFolder) {
- let initChatFolderParam: ChatFolderParams;
- if (editChatFolderPageId != null) {
- const found = chatFolders.find(chatFolder => {
- return chatFolder.id === editChatFolderPageId;
- });
- strictAssert(found, 'Missing chat folder');
- initChatFolderParam = found;
- } else {
- initChatFolderParam = CHAT_FOLDER_DEFAULTS;
- }
- content = (
-
- );
+ content = renderPreferencesEditChatFolderPage({
+ onBack: handleCloseEditChatFoldersPage,
+ settingsPaneRef,
+ existingChatFolderId: editChatFolderPageId,
+ });
} else if (page === SettingsPage.PNP) {
let sharingDescription: string;
diff --git a/ts/components/preferences/ChatFoldersPage.tsx b/ts/components/preferences/chatFolders/PreferencesChatFoldersPage.tsx
similarity index 50%
rename from ts/components/preferences/ChatFoldersPage.tsx
rename to ts/components/preferences/chatFolders/PreferencesChatFoldersPage.tsx
index 0fa25d3764..163e6a499b 100644
--- a/ts/components/preferences/ChatFoldersPage.tsx
+++ b/ts/components/preferences/chatFolders/PreferencesChatFoldersPage.tsx
@@ -3,32 +3,34 @@
import React, { useCallback, useMemo } from 'react';
import type { MutableRefObject } from 'react';
-import { ListBox, ListBoxItem } from 'react-aria-components';
-import type { LocalizerType } from '../../types/I18N';
-import { PreferencesContent } from '../Preferences';
-import { SettingsRow } from '../PreferencesUtil';
-import type { ChatFolderId } from '../../types/ChatFolder';
+import type { LocalizerType } from '../../../types/I18N';
+import { PreferencesContent } from '../../Preferences';
+import { SettingsRow } from '../../PreferencesUtil';
+import type { ChatFolderId } from '../../../types/ChatFolder';
import {
CHAT_FOLDER_PRESETS,
matchesChatFolderPreset,
type ChatFolderParams,
type ChatFolderPreset,
- type ChatFolderRecord,
-} from '../../types/ChatFolder';
-import { Button, ButtonVariant } from '../Button';
+ type ChatFolder,
+ ChatFolderType,
+} from '../../../types/ChatFolder';
+import { Button, ButtonVariant } from '../../Button';
// import { showToast } from '../../state/ducks/toast';
-export type ChatFoldersPageProps = Readonly<{
+export type PreferencesChatFoldersPageProps = Readonly<{
i18n: LocalizerType;
onBack: () => void;
onOpenEditChatFoldersPage: (chatFolderId: ChatFolderId | null) => void;
- chatFolders: ReadonlyArray;
+ chatFolders: ReadonlyArray;
onCreateChatFolder: (params: ChatFolderParams) => void;
settingsPaneRef: MutableRefObject;
}>;
-export function ChatFoldersPage(props: ChatFoldersPageProps): JSX.Element {
- const { i18n, onOpenEditChatFoldersPage } = props;
+export function PreferencesChatFoldersPage(
+ props: PreferencesChatFoldersPageProps
+): JSX.Element {
+ const { i18n, onOpenEditChatFoldersPage, chatFolders } = props;
// showToast(
// i18n("icu:Preferences__ChatFoldersPage__SuggestedFoldersSection__AddButton__Toast")
@@ -38,14 +40,57 @@ export function ChatFoldersPage(props: ChatFoldersPageProps): JSX.Element {
onOpenEditChatFoldersPage(null);
}, [onOpenEditChatFoldersPage]);
+ const presetItemsConfigs = useMemo(() => {
+ const initial: ReadonlyArray = [
+ {
+ id: 'UnreadChats',
+ title: i18n(
+ 'icu:Preferences__ChatFoldersPage__SuggestedFoldersSection__UnreadFolder__Title'
+ ),
+ description: i18n(
+ 'icu:Preferences__ChatFoldersPage__SuggestedFoldersSection__UnreadFolder__Description'
+ ),
+ preset: CHAT_FOLDER_PRESETS.UNREAD_CHATS,
+ },
+ {
+ id: 'DirectChats',
+ title: i18n(
+ 'icu:Preferences__ChatFoldersPage__SuggestedFoldersSection__DirectChatsFolder__Title'
+ ),
+ description: i18n(
+ 'icu:Preferences__ChatFoldersPage__SuggestedFoldersSection__DirectChatsFolder__Description'
+ ),
+ preset: CHAT_FOLDER_PRESETS.INDIVIDUAL_CHATS,
+ },
+ {
+ id: 'GroupChats',
+ title: i18n(
+ 'icu:Preferences__ChatFoldersPage__SuggestedFoldersSection__GroupChatsFolder__Title'
+ ),
+ description: i18n(
+ 'icu:Preferences__ChatFoldersPage__SuggestedFoldersSection__GroupChatsFolder__Description'
+ ),
+ preset: CHAT_FOLDER_PRESETS.GROUP_CHATS,
+ },
+ ];
+
+ const filtered = initial.filter(config => {
+ return !chatFolders.some(chatFolder => {
+ return matchesChatFolderPreset(chatFolder, config.preset);
+ });
+ });
+
+ return filtered;
+ }, [i18n, chatFolders]);
+
return (
}
contents={
@@ -58,86 +103,55 @@ export function ChatFoldersPage(props: ChatFoldersPageProps): JSX.Element {
'icu:Preferences__ChatFoldersPage__FoldersSection__Title'
)}
>
-
-
-
-
- {i18n(
- 'icu:Preferences__ChatFoldersPage__FoldersSection__CreateAFolderButton'
- )}
-
-
-
-
-
- {i18n(
- 'icu:Preferences__ChatFoldersPage__FoldersSection__AllChatsFolder__Title'
- )}
-
-
+
+ -
+
+
{props.chatFolders.map(chatFolder => {
return (
);
})}
-
-
-
-
+ {presetItemsConfigs.length > 0 && (
+
+
+ {presetItemsConfigs.map(presetItemConfig => {
+ return (
+
+ );
+ })}
+
+
+ )}
>
}
contentsRef={props.settingsPaneRef}
@@ -146,42 +160,41 @@ export function ChatFoldersPage(props: ChatFoldersPageProps): JSX.Element {
);
}
-function ChatFolderPresetItem(props: {
- i18n: LocalizerType;
- icon: 'UnreadChats' | 'DirectChats' | 'GroupChats';
+type ChatFolderPresetItemConfig = Readonly<{
+ id: 'UnreadChats' | 'DirectChats' | 'GroupChats';
title: string;
description: string;
preset: ChatFolderPreset;
- chatFolders: ReadonlyArray;
+}>;
+
+type ChatFolderPresetItemProps = Readonly<{
+ i18n: LocalizerType;
+ config: ChatFolderPresetItemConfig;
onCreateChatFolder: (params: ChatFolderParams) => void;
-}) {
- const { i18n, title, preset, chatFolders, onCreateChatFolder } = props;
+}>;
+
+function ChatFolderPresetItem(props: ChatFolderPresetItemProps) {
+ const { i18n, config, onCreateChatFolder } = props;
+ const { title, preset } = config;
const handleCreateChatFolder = useCallback(() => {
onCreateChatFolder({ ...preset, name: title });
}, [onCreateChatFolder, title, preset]);
- const hasPreset = useMemo(() => {
- return chatFolders.some(chatFolder => {
- return matchesChatFolderPreset(chatFolder, preset);
- });
- }, [chatFolders, preset]);
-
- if (hasPreset) {
- return null;
- }
-
return (
-
+
- {props.title}
+ {props.config.title}
- {props.description}
+ {props.config.description}