Donations: Show toasts when resuming after startup
Co-authored-by: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com>
This commit is contained in:
parent
ea3a7f70b6
commit
e17bcb2409
36 changed files with 496 additions and 314 deletions
|
@ -8886,6 +8886,14 @@
|
||||||
"messageformat": "Thank you for supporting Signal. Your contribution helps fuel the mission of protecting free expression and enabling secure global communication for millions around the world, through open source privacy technology. If you’re a resident of the United States, please retain this receipt for your tax records. Signal Technology Foundation is a tax-exempt nonprofit organization in the United States under section 501c3 of the Internal Revenue Code. Our Federal Tax ID is 82-4506840.",
|
"messageformat": "Thank you for supporting Signal. Your contribution helps fuel the mission of protecting free expression and enabling secure global communication for millions around the world, through open source privacy technology. If you’re a resident of the United States, please retain this receipt for your tax records. Signal Technology Foundation is a tax-exempt nonprofit organization in the United States under section 501c3 of the Internal Revenue Code. Our Federal Tax ID is 82-4506840.",
|
||||||
"description": "Footer text shown on donation receipts explaining tax deductibility and Signal's mission"
|
"description": "Footer text shown on donation receipts explaining tax deductibility and Signal's mission"
|
||||||
},
|
},
|
||||||
|
"icu:Donations__Toast__Completed": {
|
||||||
|
"messageformat": "Donation completed",
|
||||||
|
"description": "Toast shown when a donation started processing after resuming on startup, and it completed successfully when the user is not on the Preferences/Donations screen"
|
||||||
|
},
|
||||||
|
"icu:Donations__Toast__Processing": {
|
||||||
|
"messageformat": "Processing donation",
|
||||||
|
"description": "Toast shown when a donation starts processing again after resuming on startup"
|
||||||
|
},
|
||||||
"icu:Donations__PaymentMethodDeclined": {
|
"icu:Donations__PaymentMethodDeclined": {
|
||||||
"messageformat": "Payment method declined",
|
"messageformat": "Payment method declined",
|
||||||
"description": "Title of the dialog shown with the user's provided payment method has not worked"
|
"description": "Title of the dialog shown with the user's provided payment method has not worked"
|
||||||
|
|
|
@ -216,10 +216,8 @@ import { waitForEvent } from './shims/events';
|
||||||
import { sendSyncRequests } from './textsecure/syncRequests';
|
import { sendSyncRequests } from './textsecure/syncRequests';
|
||||||
import { handleServerAlerts } from './util/handleServerAlerts';
|
import { handleServerAlerts } from './util/handleServerAlerts';
|
||||||
import { isLocalBackupsEnabled } from './util/isLocalBackupsEnabled';
|
import { isLocalBackupsEnabled } from './util/isLocalBackupsEnabled';
|
||||||
import { NavTab } from './state/ducks/nav';
|
import { NavTab, SettingsPage, ProfileEditorPage } from './types/Nav';
|
||||||
import { Page } from './components/Preferences';
|
import { initialize as initializeDonationService } from './services/donations';
|
||||||
import { EditState } from './components/ProfileEditor';
|
|
||||||
import { runDonationWorkflow } from './services/donations';
|
|
||||||
import { MessageRequestResponseSource } from './types/MessageRequestResponseEvent';
|
import { MessageRequestResponseSource } from './types/MessageRequestResponseEvent';
|
||||||
|
|
||||||
const log = createLogger('background');
|
const log = createLogger('background');
|
||||||
|
@ -1373,8 +1371,8 @@ export async function startApp(): Promise<void> {
|
||||||
window.reduxActions.nav.changeLocation({
|
window.reduxActions.nav.changeLocation({
|
||||||
tab: NavTab.Settings,
|
tab: NavTab.Settings,
|
||||||
details: {
|
details: {
|
||||||
page: Page.Profile,
|
page: SettingsPage.Profile,
|
||||||
state: EditState.None,
|
state: ProfileEditorPage.None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2198,7 +2196,7 @@ export async function startApp(): Promise<void> {
|
||||||
|
|
||||||
drop(ReleaseNotesFetcher.init(window.Whisper.events, newVersion));
|
drop(ReleaseNotesFetcher.init(window.Whisper.events, newVersion));
|
||||||
|
|
||||||
drop(runDonationWorkflow());
|
drop(initializeDonationService());
|
||||||
|
|
||||||
if (isFromMessageReceiver) {
|
if (isFromMessageReceiver) {
|
||||||
drop(
|
drop(
|
||||||
|
|
|
@ -151,12 +151,15 @@ export function DebugLogWindow({
|
||||||
<Button onClick={copyLog}>{i18n('icu:debugLogCopy')}</Button>
|
<Button onClick={copyLog}>{i18n('icu:debugLogCopy')}</Button>
|
||||||
</div>
|
</div>
|
||||||
<ToastManager
|
<ToastManager
|
||||||
|
changeLocation={shouldNeverBeCalled}
|
||||||
|
clearDonation={shouldNeverBeCalled}
|
||||||
OS="unused"
|
OS="unused"
|
||||||
hideToast={closeToast}
|
hideToast={closeToast}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onShowDebugLog={shouldNeverBeCalled}
|
onShowDebugLog={shouldNeverBeCalled}
|
||||||
onUndoArchive={shouldNeverBeCalled}
|
onUndoArchive={shouldNeverBeCalled}
|
||||||
openFileInFolder={shouldNeverBeCalled}
|
openFileInFolder={shouldNeverBeCalled}
|
||||||
|
setDidResumeDonation={shouldNeverBeCalled}
|
||||||
showAttachmentNotAvailableModal={shouldNeverBeCalled}
|
showAttachmentNotAvailableModal={shouldNeverBeCalled}
|
||||||
toast={toast}
|
toast={toast}
|
||||||
containerWidthBreakpoint={null}
|
containerWidthBreakpoint={null}
|
||||||
|
@ -209,12 +212,15 @@ export function DebugLogWindow({
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<ToastManager
|
<ToastManager
|
||||||
|
changeLocation={shouldNeverBeCalled}
|
||||||
|
clearDonation={shouldNeverBeCalled}
|
||||||
OS="unused"
|
OS="unused"
|
||||||
hideToast={closeToast}
|
hideToast={closeToast}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onShowDebugLog={shouldNeverBeCalled}
|
onShowDebugLog={shouldNeverBeCalled}
|
||||||
onUndoArchive={shouldNeverBeCalled}
|
onUndoArchive={shouldNeverBeCalled}
|
||||||
openFileInFolder={shouldNeverBeCalled}
|
openFileInFolder={shouldNeverBeCalled}
|
||||||
|
setDidResumeDonation={shouldNeverBeCalled}
|
||||||
showAttachmentNotAvailableModal={shouldNeverBeCalled}
|
showAttachmentNotAvailableModal={shouldNeverBeCalled}
|
||||||
toast={toast}
|
toast={toast}
|
||||||
containerWidthBreakpoint={null}
|
containerWidthBreakpoint={null}
|
||||||
|
|
|
@ -284,12 +284,15 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => {
|
||||||
),
|
),
|
||||||
renderToastManager: ({ containerWidthBreakpoint }) => (
|
renderToastManager: ({ containerWidthBreakpoint }) => (
|
||||||
<ToastManager
|
<ToastManager
|
||||||
|
changeLocation={action('changeLocation')}
|
||||||
|
clearDonation={action('clearDonation')}
|
||||||
OS="unused"
|
OS="unused"
|
||||||
hideToast={action('hideToast')}
|
hideToast={action('hideToast')}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onShowDebugLog={action('onShowDebugLog')}
|
onShowDebugLog={action('onShowDebugLog')}
|
||||||
onUndoArchive={action('onUndoArchive')}
|
onUndoArchive={action('onUndoArchive')}
|
||||||
openFileInFolder={action('openFileInFolder')}
|
openFileInFolder={action('openFileInFolder')}
|
||||||
|
setDidResumeDonation={action('setDidResumeDonation')}
|
||||||
showAttachmentNotAvailableModal={action(
|
showAttachmentNotAvailableModal={action(
|
||||||
'showAttachmentNotAvailableModal'
|
'showAttachmentNotAvailableModal'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -58,10 +58,8 @@ import type { UnreadStats } from '../util/countUnreadStats';
|
||||||
import { BackupMediaDownloadProgress } from './BackupMediaDownloadProgress';
|
import { BackupMediaDownloadProgress } from './BackupMediaDownloadProgress';
|
||||||
import type { ServerAlertsType } from '../util/handleServerAlerts';
|
import type { ServerAlertsType } from '../util/handleServerAlerts';
|
||||||
import { getServerAlertDialog } from './ServerAlerts';
|
import { getServerAlertDialog } from './ServerAlerts';
|
||||||
import { NavTab } from '../state/ducks/nav';
|
import { NavTab, SettingsPage, ProfileEditorPage } from '../types/Nav';
|
||||||
import type { Location } from '../state/ducks/nav';
|
import type { Location } from '../types/Nav';
|
||||||
import { Page } from './Preferences';
|
|
||||||
import { EditState } from './ProfileEditor';
|
|
||||||
|
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
backupMediaDownloadProgress: {
|
backupMediaDownloadProgress: {
|
||||||
|
@ -674,8 +672,8 @@ export function LeftPane({
|
||||||
changeLocation({
|
changeLocation({
|
||||||
tab: NavTab.Settings,
|
tab: NavTab.Settings,
|
||||||
details: {
|
details: {
|
||||||
page: Page.Profile,
|
page: SettingsPage.Profile,
|
||||||
state: EditState.Username,
|
state: ProfileEditorPage.Username,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
@ -691,8 +689,8 @@ export function LeftPane({
|
||||||
changeLocation({
|
changeLocation({
|
||||||
tab: NavTab.Settings,
|
tab: NavTab.Settings,
|
||||||
details: {
|
details: {
|
||||||
page: Page.Profile,
|
page: SettingsPage.Profile,
|
||||||
state: EditState.UsernameLink,
|
state: ProfileEditorPage.UsernameLink,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { action } from '@storybook/addon-actions';
|
||||||
import type { Meta } from '@storybook/react';
|
import type { Meta } from '@storybook/react';
|
||||||
import type { NavTabsProps } from './NavTabs';
|
import type { NavTabsProps } from './NavTabs';
|
||||||
import { NavTabs } from './NavTabs';
|
import { NavTabs } from './NavTabs';
|
||||||
import { NavTab } from '../state/ducks/nav';
|
import { NavTab } from '../types/Nav';
|
||||||
import { getDefaultConversation } from '../test-helpers/getDefaultConversation';
|
import { getDefaultConversation } from '../test-helpers/getDefaultConversation';
|
||||||
import { ThemeType } from '../types/Util';
|
import { ThemeType } from '../types/Util';
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,11 @@ import { Avatar, AvatarSize } from './Avatar';
|
||||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||||
import type { ConversationType } from '../state/ducks/conversations';
|
import type { ConversationType } from '../state/ducks/conversations';
|
||||||
import type { BadgeType } from '../badges/types';
|
import type { BadgeType } from '../badges/types';
|
||||||
import { NavTab } from '../state/ducks/nav';
|
import { NavTab, ProfileEditorPage, SettingsPage } from '../types/Nav';
|
||||||
import type { Location } from '../state/ducks/nav';
|
import type { Location } from '../types/Nav';
|
||||||
import { Tooltip, TooltipPlacement } from './Tooltip';
|
import { Tooltip, TooltipPlacement } from './Tooltip';
|
||||||
import { Theme } from '../util/theme';
|
import { Theme } from '../util/theme';
|
||||||
import type { UnreadStats } from '../util/countUnreadStats';
|
import type { UnreadStats } from '../util/countUnreadStats';
|
||||||
import { Page } from './Preferences';
|
|
||||||
import { EditState } from './ProfileEditor';
|
|
||||||
import { ProfileMovedModal } from './ProfileMovedModal';
|
import { ProfileMovedModal } from './ProfileMovedModal';
|
||||||
|
|
||||||
type NavTabsItemBadgesProps = Readonly<{
|
type NavTabsItemBadgesProps = Readonly<{
|
||||||
|
@ -248,8 +246,8 @@ export function NavTabs({
|
||||||
onChangeLocation({
|
onChangeLocation({
|
||||||
tab: NavTab.Settings,
|
tab: NavTab.Settings,
|
||||||
details: {
|
details: {
|
||||||
page: Page.Profile,
|
page: SettingsPage.Profile,
|
||||||
state: EditState.None,
|
state: ProfileEditorPage.None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import React, { useRef, useState } from 'react';
|
||||||
|
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { shuffle } from 'lodash';
|
import { shuffle } from 'lodash';
|
||||||
import { Page, Preferences } from './Preferences';
|
import { Preferences } from './Preferences';
|
||||||
import { DEFAULT_CONVERSATION_COLOR } from '../types/Colors';
|
import { DEFAULT_CONVERSATION_COLOR } from '../types/Colors';
|
||||||
import { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode';
|
import { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode';
|
||||||
import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability';
|
import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability';
|
||||||
|
@ -15,22 +15,23 @@ import { DAY, DurationInSeconds, WEEK } from '../util/durations';
|
||||||
import { DialogUpdate } from './DialogUpdate';
|
import { DialogUpdate } from './DialogUpdate';
|
||||||
import { DialogType } from '../types/Dialogs';
|
import { DialogType } from '../types/Dialogs';
|
||||||
import { ThemeType } from '../types/Util';
|
import { ThemeType } from '../types/Util';
|
||||||
import type { LocalizerType } from '../types/Util';
|
|
||||||
import {
|
import {
|
||||||
getDefaultConversation,
|
getDefaultConversation,
|
||||||
getDefaultGroup,
|
getDefaultGroup,
|
||||||
} from '../test-helpers/getDefaultConversation';
|
} from '../test-helpers/getDefaultConversation';
|
||||||
import { EditState, ProfileEditor } from './ProfileEditor';
|
import { ProfileEditor } from './ProfileEditor';
|
||||||
import {
|
import {
|
||||||
UsernameEditState,
|
UsernameEditState,
|
||||||
UsernameLinkState,
|
UsernameLinkState,
|
||||||
} from '../state/ducks/usernameEnums';
|
} from '../state/ducks/usernameEnums';
|
||||||
|
import { ProfileEditorPage, SettingsPage } from '../types/Nav';
|
||||||
|
import { PreferencesDonations } from './PreferencesDonations';
|
||||||
|
import { strictAssert } from '../util/assert';
|
||||||
|
|
||||||
|
import type { LocalizerType } from '../types/Util';
|
||||||
import type { PropsType } from './Preferences';
|
import type { PropsType } from './Preferences';
|
||||||
import type { WidthBreakpoint } from './_util';
|
import type { WidthBreakpoint } from './_util';
|
||||||
import type { MessageAttributesType } from '../model-types';
|
import type { MessageAttributesType } from '../model-types';
|
||||||
import { PreferencesDonations } from './PreferencesDonations';
|
|
||||||
import { strictAssert } from '../util/assert';
|
|
||||||
import type {
|
import type {
|
||||||
DonationReceipt,
|
DonationReceipt,
|
||||||
OneTimeDonationHumanAmounts,
|
OneTimeDonationHumanAmounts,
|
||||||
|
@ -160,7 +161,7 @@ function RenderProfileEditor(): JSX.Element {
|
||||||
firstName={me.firstName ?? ''}
|
firstName={me.firstName ?? ''}
|
||||||
hasCompletedUsernameLinkOnboarding={false}
|
hasCompletedUsernameLinkOnboarding={false}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
editState={EditState.None}
|
editState={ProfileEditorPage.None}
|
||||||
markCompletedUsernameLinkOnboarding={action(
|
markCompletedUsernameLinkOnboarding={action(
|
||||||
'markCompletedUsernameLinkOnboarding'
|
'markCompletedUsernameLinkOnboarding'
|
||||||
)}
|
)}
|
||||||
|
@ -212,7 +213,7 @@ function RenderDonationsPane(props: {
|
||||||
contentsRef={contentsRef}
|
contentsRef={contentsRef}
|
||||||
clearWorkflow={action('clearWorkflow')}
|
clearWorkflow={action('clearWorkflow')}
|
||||||
isStaging={false}
|
isStaging={false}
|
||||||
page={Page.Donations}
|
page={SettingsPage.Donations}
|
||||||
setPage={action('setPage')}
|
setPage={action('setPage')}
|
||||||
submitDonation={action('submitDonation')}
|
submitDonation={action('submitDonation')}
|
||||||
workflow={undefined}
|
workflow={undefined}
|
||||||
|
@ -331,7 +332,7 @@ export default {
|
||||||
unreadMentionsCount: 0,
|
unreadMentionsCount: 0,
|
||||||
markedUnread: false,
|
markedUnread: false,
|
||||||
},
|
},
|
||||||
page: Page.Profile,
|
page: SettingsPage.Profile,
|
||||||
preferredSystemLocales: ['en'],
|
preferredSystemLocales: ['en'],
|
||||||
preferredWidthFromStorage: 300,
|
preferredWidthFromStorage: 300,
|
||||||
resolvedLocale: 'en',
|
resolvedLocale: 'en',
|
||||||
|
@ -476,86 +477,86 @@ export const _Preferences = Template.bind({});
|
||||||
|
|
||||||
export const General = Template.bind({});
|
export const General = Template.bind({});
|
||||||
General.args = {
|
General.args = {
|
||||||
page: Page.General,
|
page: SettingsPage.General,
|
||||||
};
|
};
|
||||||
export const Appearance = Template.bind({});
|
export const Appearance = Template.bind({});
|
||||||
Appearance.args = {
|
Appearance.args = {
|
||||||
page: Page.Appearance,
|
page: SettingsPage.Appearance,
|
||||||
};
|
};
|
||||||
export const Chats = Template.bind({});
|
export const Chats = Template.bind({});
|
||||||
Chats.args = {
|
Chats.args = {
|
||||||
page: Page.Chats,
|
page: SettingsPage.Chats,
|
||||||
};
|
};
|
||||||
export const ChatFolders = Template.bind({});
|
export const ChatFolders = Template.bind({});
|
||||||
ChatFolders.args = {
|
ChatFolders.args = {
|
||||||
page: Page.ChatFolders,
|
page: SettingsPage.ChatFolders,
|
||||||
};
|
};
|
||||||
export const EditChatFolder = Template.bind({});
|
export const EditChatFolder = Template.bind({});
|
||||||
EditChatFolder.args = {
|
EditChatFolder.args = {
|
||||||
page: Page.EditChatFolder,
|
page: SettingsPage.EditChatFolder,
|
||||||
};
|
};
|
||||||
export const Calls = Template.bind({});
|
export const Calls = Template.bind({});
|
||||||
Calls.args = {
|
Calls.args = {
|
||||||
page: Page.Calls,
|
page: SettingsPage.Calls,
|
||||||
};
|
};
|
||||||
export const Notifications = Template.bind({});
|
export const Notifications = Template.bind({});
|
||||||
Notifications.args = {
|
Notifications.args = {
|
||||||
page: Page.Notifications,
|
page: SettingsPage.Notifications,
|
||||||
};
|
};
|
||||||
export const Privacy = Template.bind({});
|
export const Privacy = Template.bind({});
|
||||||
Privacy.args = {
|
Privacy.args = {
|
||||||
page: Page.Privacy,
|
page: SettingsPage.Privacy,
|
||||||
};
|
};
|
||||||
export const DataUsage = Template.bind({});
|
export const DataUsage = Template.bind({});
|
||||||
DataUsage.args = {
|
DataUsage.args = {
|
||||||
page: Page.DataUsage,
|
page: SettingsPage.DataUsage,
|
||||||
};
|
};
|
||||||
export const Donations = Template.bind({});
|
export const Donations = Template.bind({});
|
||||||
Donations.args = {
|
Donations.args = {
|
||||||
donationsFeatureEnabled: true,
|
donationsFeatureEnabled: true,
|
||||||
page: Page.Donations,
|
page: SettingsPage.Donations,
|
||||||
};
|
};
|
||||||
export const Internal = Template.bind({});
|
export const Internal = Template.bind({});
|
||||||
Internal.args = {
|
Internal.args = {
|
||||||
page: Page.Internal,
|
page: SettingsPage.Internal,
|
||||||
isInternalUser: true,
|
isInternalUser: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Blocked1 = Template.bind({});
|
export const Blocked1 = Template.bind({});
|
||||||
Blocked1.args = {
|
Blocked1.args = {
|
||||||
blockedCount: 1,
|
blockedCount: 1,
|
||||||
page: Page.Privacy,
|
page: SettingsPage.Privacy,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BlockedMany = Template.bind({});
|
export const BlockedMany = Template.bind({});
|
||||||
BlockedMany.args = {
|
BlockedMany.args = {
|
||||||
blockedCount: 55,
|
blockedCount: 55,
|
||||||
page: Page.Privacy,
|
page: SettingsPage.Privacy,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CustomUniversalExpireTimer = Template.bind({});
|
export const CustomUniversalExpireTimer = Template.bind({});
|
||||||
CustomUniversalExpireTimer.args = {
|
CustomUniversalExpireTimer.args = {
|
||||||
universalExpireTimer: DurationInSeconds.fromSeconds(9000),
|
universalExpireTimer: DurationInSeconds.fromSeconds(9000),
|
||||||
page: Page.Privacy,
|
page: SettingsPage.Privacy,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PNPSharingDisabled = Template.bind({});
|
export const PNPSharingDisabled = Template.bind({});
|
||||||
PNPSharingDisabled.args = {
|
PNPSharingDisabled.args = {
|
||||||
whoCanSeeMe: PhoneNumberSharingMode.Nobody,
|
whoCanSeeMe: PhoneNumberSharingMode.Nobody,
|
||||||
whoCanFindMe: PhoneNumberDiscoverability.Discoverable,
|
whoCanFindMe: PhoneNumberDiscoverability.Discoverable,
|
||||||
page: Page.PNP,
|
page: SettingsPage.PNP,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PNPDiscoverabilityDisabled = Template.bind({});
|
export const PNPDiscoverabilityDisabled = Template.bind({});
|
||||||
PNPDiscoverabilityDisabled.args = {
|
PNPDiscoverabilityDisabled.args = {
|
||||||
whoCanSeeMe: PhoneNumberSharingMode.Nobody,
|
whoCanSeeMe: PhoneNumberSharingMode.Nobody,
|
||||||
whoCanFindMe: PhoneNumberDiscoverability.NotDiscoverable,
|
whoCanFindMe: PhoneNumberDiscoverability.NotDiscoverable,
|
||||||
page: Page.PNP,
|
page: SettingsPage.PNP,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BackupsMediaDownloadActive = Template.bind({});
|
export const BackupsMediaDownloadActive = Template.bind({});
|
||||||
BackupsMediaDownloadActive.args = {
|
BackupsMediaDownloadActive.args = {
|
||||||
page: Page.BackupsDetails,
|
page: SettingsPage.BackupsDetails,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
cloudBackupStatus: {
|
cloudBackupStatus: {
|
||||||
|
@ -579,7 +580,7 @@ BackupsMediaDownloadActive.args = {
|
||||||
};
|
};
|
||||||
export const BackupsMediaDownloadPaused = Template.bind({});
|
export const BackupsMediaDownloadPaused = Template.bind({});
|
||||||
BackupsMediaDownloadPaused.args = {
|
BackupsMediaDownloadPaused.args = {
|
||||||
page: Page.BackupsDetails,
|
page: SettingsPage.BackupsDetails,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
cloudBackupStatus: {
|
cloudBackupStatus: {
|
||||||
|
@ -604,7 +605,7 @@ BackupsMediaDownloadPaused.args = {
|
||||||
|
|
||||||
export const BackupsPaidActive = Template.bind({});
|
export const BackupsPaidActive = Template.bind({});
|
||||||
BackupsPaidActive.args = {
|
BackupsPaidActive.args = {
|
||||||
page: Page.Backups,
|
page: SettingsPage.Backups,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
cloudBackupStatus: {
|
cloudBackupStatus: {
|
||||||
|
@ -623,7 +624,7 @@ BackupsPaidActive.args = {
|
||||||
|
|
||||||
export const BackupsPaidCancelled = Template.bind({});
|
export const BackupsPaidCancelled = Template.bind({});
|
||||||
BackupsPaidCancelled.args = {
|
BackupsPaidCancelled.args = {
|
||||||
page: Page.Backups,
|
page: SettingsPage.Backups,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
cloudBackupStatus: {
|
cloudBackupStatus: {
|
||||||
|
@ -642,7 +643,7 @@ BackupsPaidCancelled.args = {
|
||||||
|
|
||||||
export const BackupsFree = Template.bind({});
|
export const BackupsFree = Template.bind({});
|
||||||
BackupsFree.args = {
|
BackupsFree.args = {
|
||||||
page: Page.Backups,
|
page: SettingsPage.Backups,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
backupSubscriptionStatus: {
|
backupSubscriptionStatus: {
|
||||||
|
@ -653,21 +654,21 @@ BackupsFree.args = {
|
||||||
|
|
||||||
export const BackupsOff = Template.bind({});
|
export const BackupsOff = Template.bind({});
|
||||||
BackupsOff.args = {
|
BackupsOff.args = {
|
||||||
page: Page.Backups,
|
page: SettingsPage.Backups,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BackupsLocalBackups = Template.bind({});
|
export const BackupsLocalBackups = Template.bind({});
|
||||||
BackupsLocalBackups.args = {
|
BackupsLocalBackups.args = {
|
||||||
page: Page.Backups,
|
page: SettingsPage.Backups,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BackupsSubscriptionNotFound = Template.bind({});
|
export const BackupsSubscriptionNotFound = Template.bind({});
|
||||||
BackupsSubscriptionNotFound.args = {
|
BackupsSubscriptionNotFound.args = {
|
||||||
page: Page.Backups,
|
page: SettingsPage.Backups,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
backupSubscriptionStatus: {
|
backupSubscriptionStatus: {
|
||||||
|
@ -681,7 +682,7 @@ BackupsSubscriptionNotFound.args = {
|
||||||
|
|
||||||
export const BackupsSubscriptionExpired = Template.bind({});
|
export const BackupsSubscriptionExpired = Template.bind({});
|
||||||
BackupsSubscriptionExpired.args = {
|
BackupsSubscriptionExpired.args = {
|
||||||
page: Page.Backups,
|
page: SettingsPage.Backups,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
backupSubscriptionStatus: {
|
backupSubscriptionStatus: {
|
||||||
|
@ -691,7 +692,7 @@ BackupsSubscriptionExpired.args = {
|
||||||
|
|
||||||
export const LocalBackups = Template.bind({});
|
export const LocalBackups = Template.bind({});
|
||||||
LocalBackups.args = {
|
LocalBackups.args = {
|
||||||
page: Page.LocalBackups,
|
page: SettingsPage.LocalBackups,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
backupKeyViewed: true,
|
backupKeyViewed: true,
|
||||||
|
@ -700,14 +701,14 @@ LocalBackups.args = {
|
||||||
|
|
||||||
export const LocalBackupsSetupChooseFolder = Template.bind({});
|
export const LocalBackupsSetupChooseFolder = Template.bind({});
|
||||||
LocalBackupsSetupChooseFolder.args = {
|
LocalBackupsSetupChooseFolder.args = {
|
||||||
page: Page.LocalBackupsSetupFolder,
|
page: SettingsPage.LocalBackupsSetupFolder,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LocalBackupsSetupViewBackupKey = Template.bind({});
|
export const LocalBackupsSetupViewBackupKey = Template.bind({});
|
||||||
LocalBackupsSetupViewBackupKey.args = {
|
LocalBackupsSetupViewBackupKey.args = {
|
||||||
page: Page.LocalBackupsSetupKey,
|
page: SettingsPage.LocalBackupsSetupKey,
|
||||||
backupFeatureEnabled: true,
|
backupFeatureEnabled: true,
|
||||||
backupLocalBackupsEnabled: true,
|
backupLocalBackupsEnabled: true,
|
||||||
localBackupFolder: '/home/signaluser/Signal Backups/',
|
localBackupFolder: '/home/signaluser/Signal Backups/',
|
||||||
|
|
|
@ -50,6 +50,7 @@ import { PreferencesInternal } from './PreferencesInternal';
|
||||||
import { FunEmojiLocalizationProvider } from './fun/FunEmojiLocalizationProvider';
|
import { FunEmojiLocalizationProvider } from './fun/FunEmojiLocalizationProvider';
|
||||||
import { Avatar, AvatarSize } from './Avatar';
|
import { Avatar, AvatarSize } from './Avatar';
|
||||||
import { NavSidebar } from './NavSidebar';
|
import { NavSidebar } from './NavSidebar';
|
||||||
|
import { SettingsPage } from '../types/Nav';
|
||||||
|
|
||||||
import type { MediaDeviceSettings } from '../types/Calling';
|
import type { MediaDeviceSettings } from '../types/Calling';
|
||||||
import type { ValidationResultType as BackupValidationResultType } from '../services/backups';
|
import type { ValidationResultType as BackupValidationResultType } from '../services/backups';
|
||||||
|
@ -153,7 +154,7 @@ export type PropsDataType = {
|
||||||
hasStoriesDisabled: boolean;
|
hasStoriesDisabled: boolean;
|
||||||
hasTextFormatting: boolean;
|
hasTextFormatting: boolean;
|
||||||
hasTypingIndicators: boolean;
|
hasTypingIndicators: boolean;
|
||||||
page: Page;
|
page: SettingsPage;
|
||||||
lastSyncTime?: number;
|
lastSyncTime?: number;
|
||||||
notificationContent: NotificationSettingType;
|
notificationContent: NotificationSettingType;
|
||||||
phoneNumber: string | undefined;
|
phoneNumber: string | undefined;
|
||||||
|
@ -208,8 +209,8 @@ type PropsFunctionType = {
|
||||||
// Render props
|
// Render props
|
||||||
renderDonationsPane: (options: {
|
renderDonationsPane: (options: {
|
||||||
contentsRef: MutableRefObject<HTMLDivElement | null>;
|
contentsRef: MutableRefObject<HTMLDivElement | null>;
|
||||||
page: Page;
|
page: SettingsPage;
|
||||||
setPage: (page: Page) => void;
|
setPage: (page: SettingsPage) => void;
|
||||||
}) => JSX.Element;
|
}) => JSX.Element;
|
||||||
renderProfileEditor: (options: {
|
renderProfileEditor: (options: {
|
||||||
contentsRef: MutableRefObject<HTMLDivElement | null>;
|
contentsRef: MutableRefObject<HTMLDivElement | null>;
|
||||||
|
@ -254,7 +255,7 @@ type PropsFunctionType = {
|
||||||
value: CustomColorType;
|
value: CustomColorType;
|
||||||
}
|
}
|
||||||
) => unknown;
|
) => unknown;
|
||||||
setPage: (page: Page) => unknown;
|
setPage: (page: SettingsPage) => unknown;
|
||||||
showToast: (toast: AnyToast) => unknown;
|
showToast: (toast: AnyToast) => unknown;
|
||||||
validateBackup: () => Promise<BackupValidationResultType>;
|
validateBackup: () => Promise<BackupValidationResultType>;
|
||||||
|
|
||||||
|
@ -318,39 +319,11 @@ export type PropsType = PropsDataType & PropsFunctionType;
|
||||||
|
|
||||||
export type PropsPreloadType = Omit<PropsType, 'i18n'>;
|
export type PropsPreloadType = Omit<PropsType, 'i18n'>;
|
||||||
|
|
||||||
export enum Page {
|
function isDonationsPage(page: SettingsPage): boolean {
|
||||||
// Accessible through left nav
|
|
||||||
Profile = 'Profile',
|
|
||||||
General = 'General',
|
|
||||||
Donations = 'Donations',
|
|
||||||
Appearance = 'Appearance',
|
|
||||||
Chats = 'Chats',
|
|
||||||
Calls = 'Calls',
|
|
||||||
Notifications = 'Notifications',
|
|
||||||
Privacy = 'Privacy',
|
|
||||||
DataUsage = 'DataUsage',
|
|
||||||
Backups = 'Backups',
|
|
||||||
Internal = 'Internal',
|
|
||||||
|
|
||||||
// Sub pages
|
|
||||||
ChatColor = 'ChatColor',
|
|
||||||
ChatFolders = 'ChatFolders',
|
|
||||||
DonationsDonateFlow = 'DonationsDonateFlow',
|
|
||||||
DonationsReceiptList = 'DonationsReceiptList',
|
|
||||||
EditChatFolder = 'EditChatFolder',
|
|
||||||
PNP = 'PNP',
|
|
||||||
BackupsDetails = 'BackupsDetails',
|
|
||||||
LocalBackups = 'LocalBackups',
|
|
||||||
LocalBackupsSetupFolder = 'LocalBackupsSetupFolder',
|
|
||||||
LocalBackupsSetupKey = 'LocalBackupsSetupKey',
|
|
||||||
LocalBackupsKeyReference = 'LocalBackupsKeyReference',
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDonationsPage(page: Page): boolean {
|
|
||||||
return (
|
return (
|
||||||
page === Page.Donations ||
|
page === SettingsPage.Donations ||
|
||||||
page === Page.DonationsDonateFlow ||
|
page === SettingsPage.DonationsDonateFlow ||
|
||||||
page === Page.DonationsReceiptList
|
page === SettingsPage.DonationsReceiptList
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,14 +540,14 @@ export function Preferences({
|
||||||
|
|
||||||
const handleOpenEditChatFoldersPage = useCallback(
|
const handleOpenEditChatFoldersPage = useCallback(
|
||||||
(chatFolderId: ChatFolderId | null) => {
|
(chatFolderId: ChatFolderId | null) => {
|
||||||
setPage(Page.EditChatFolder);
|
setPage(SettingsPage.EditChatFolder);
|
||||||
setEditChatFolderPageId(chatFolderId);
|
setEditChatFolderPageId(chatFolderId);
|
||||||
},
|
},
|
||||||
[setPage]
|
[setPage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCloseEditChatFoldersPage = useCallback(() => {
|
const handleCloseEditChatFoldersPage = useCallback(() => {
|
||||||
setPage(Page.ChatFolders);
|
setPage(SettingsPage.ChatFolders);
|
||||||
setEditChatFolderPageId(null);
|
setEditChatFolderPageId(null);
|
||||||
}, [setPage]);
|
}, [setPage]);
|
||||||
|
|
||||||
|
@ -613,14 +586,14 @@ export function Preferences({
|
||||||
const shouldShowBackupsPage =
|
const shouldShowBackupsPage =
|
||||||
backupFeatureEnabled || backupLocalBackupsEnabled;
|
backupFeatureEnabled || backupLocalBackupsEnabled;
|
||||||
|
|
||||||
if (page === Page.Backups && !shouldShowBackupsPage) {
|
if (page === SettingsPage.Backups && !shouldShowBackupsPage) {
|
||||||
setPage(Page.General);
|
setPage(SettingsPage.General);
|
||||||
}
|
}
|
||||||
if (isDonationsPage(page) && !donationsFeatureEnabled) {
|
if (isDonationsPage(page) && !donationsFeatureEnabled) {
|
||||||
setPage(Page.General);
|
setPage(SettingsPage.General);
|
||||||
}
|
}
|
||||||
if (page === Page.Internal && !isInternalUser) {
|
if (page === SettingsPage.Internal && !isInternalUser) {
|
||||||
setPage(Page.General);
|
setPage(SettingsPage.General);
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybeUpdateDialog: JSX.Element | undefined;
|
let maybeUpdateDialog: JSX.Element | undefined;
|
||||||
|
@ -782,11 +755,11 @@ export function Preferences({
|
||||||
|
|
||||||
let content: JSX.Element | undefined;
|
let content: JSX.Element | undefined;
|
||||||
|
|
||||||
if (page === Page.Profile) {
|
if (page === SettingsPage.Profile) {
|
||||||
content = renderProfileEditor({
|
content = renderProfileEditor({
|
||||||
contentsRef: settingsPaneRef,
|
contentsRef: settingsPaneRef,
|
||||||
});
|
});
|
||||||
} else if (page === Page.General) {
|
} else if (page === SettingsPage.General) {
|
||||||
const pageContents = (
|
const pageContents = (
|
||||||
<>
|
<>
|
||||||
<SettingsRow>
|
<SettingsRow>
|
||||||
|
@ -920,7 +893,7 @@ export function Preferences({
|
||||||
page,
|
page,
|
||||||
setPage,
|
setPage,
|
||||||
});
|
});
|
||||||
} else if (page === Page.Appearance) {
|
} else if (page === SettingsPage.Appearance) {
|
||||||
let zoomFactors = DEFAULT_ZOOM_FACTORS;
|
let zoomFactors = DEFAULT_ZOOM_FACTORS;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -1101,7 +1074,7 @@ export function Preferences({
|
||||||
icon
|
icon
|
||||||
left={i18n('icu:showChatColorEditor')}
|
left={i18n('icu:showChatColorEditor')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPage(Page.ChatColor);
|
setPage(SettingsPage.ChatColor);
|
||||||
}}
|
}}
|
||||||
right={
|
right={
|
||||||
<div
|
<div
|
||||||
|
@ -1140,7 +1113,7 @@ export function Preferences({
|
||||||
title={i18n('icu:Preferences__button--appearance')}
|
title={i18n('icu:Preferences__button--appearance')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.Chats) {
|
} else if (page === SettingsPage.Chats) {
|
||||||
let spellCheckDirtyText: string | undefined;
|
let spellCheckDirtyText: string | undefined;
|
||||||
if (
|
if (
|
||||||
hasSpellCheck !== undefined &&
|
hasSpellCheck !== undefined &&
|
||||||
|
@ -1231,7 +1204,7 @@ export function Preferences({
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
right={null}
|
right={null}
|
||||||
onClick={() => setPage(Page.ChatFolders)}
|
onClick={() => setPage(SettingsPage.ChatFolders)}
|
||||||
/>
|
/>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
)}
|
)}
|
||||||
|
@ -1298,7 +1271,7 @@ export function Preferences({
|
||||||
title={i18n('icu:Preferences__button--chats')}
|
title={i18n('icu:Preferences__button--chats')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.Calls) {
|
} else if (page === SettingsPage.Calls) {
|
||||||
const pageContents = (
|
const pageContents = (
|
||||||
<>
|
<>
|
||||||
<SettingsRow title={i18n('icu:calling')}>
|
<SettingsRow title={i18n('icu:calling')}>
|
||||||
|
@ -1447,7 +1420,7 @@ export function Preferences({
|
||||||
title={i18n('icu:Preferences__button--calls')}
|
title={i18n('icu:Preferences__button--calls')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.Notifications) {
|
} else if (page === SettingsPage.Notifications) {
|
||||||
const pageContents = (
|
const pageContents = (
|
||||||
<>
|
<>
|
||||||
<SettingsRow>
|
<SettingsRow>
|
||||||
|
@ -1535,7 +1508,7 @@ export function Preferences({
|
||||||
title={i18n('icu:Preferences__button--notifications')}
|
title={i18n('icu:Preferences__button--notifications')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.Privacy) {
|
} else if (page === SettingsPage.Privacy) {
|
||||||
const isCustomDisappearingMessageValue =
|
const isCustomDisappearingMessageValue =
|
||||||
!DEFAULT_DURATIONS_SET.has(universalExpireTimer);
|
!DEFAULT_DURATIONS_SET.has(universalExpireTimer);
|
||||||
const pageContents = (
|
const pageContents = (
|
||||||
|
@ -1562,7 +1535,7 @@ export function Preferences({
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setPage(Page.PNP)}
|
onClick={() => setPage(SettingsPage.PNP)}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__pnp__row--button')}
|
{i18n('icu:Preferences__pnp__row--button')}
|
||||||
|
@ -1813,7 +1786,7 @@ export function Preferences({
|
||||||
title={i18n('icu:Preferences__button--privacy')}
|
title={i18n('icu:Preferences__button--privacy')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.DataUsage) {
|
} else if (page === SettingsPage.DataUsage) {
|
||||||
const pageContents = (
|
const pageContents = (
|
||||||
<>
|
<>
|
||||||
<SettingsRow title={i18n('icu:Preferences__media-auto-download')}>
|
<SettingsRow title={i18n('icu:Preferences__media-auto-download')}>
|
||||||
|
@ -1925,12 +1898,12 @@ export function Preferences({
|
||||||
title={i18n('icu:Preferences__button--data-usage')}
|
title={i18n('icu:Preferences__button--data-usage')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.ChatColor) {
|
} else if (page === SettingsPage.ChatColor) {
|
||||||
const backButton = (
|
const backButton = (
|
||||||
<button
|
<button
|
||||||
aria-label={i18n('icu:goBack')}
|
aria-label={i18n('icu:goBack')}
|
||||||
className="Preferences__back-icon"
|
className="Preferences__back-icon"
|
||||||
onClick={() => setPage(Page.Appearance)}
|
onClick={() => setPage(SettingsPage.Appearance)}
|
||||||
type="button"
|
type="button"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1961,18 +1934,18 @@ export function Preferences({
|
||||||
title={i18n('icu:ChatColorPicker__menu-title')}
|
title={i18n('icu:ChatColorPicker__menu-title')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.ChatFolders) {
|
} else if (page === SettingsPage.ChatFolders) {
|
||||||
content = (
|
content = (
|
||||||
<ChatFoldersPage
|
<ChatFoldersPage
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
settingsPaneRef={settingsPaneRef}
|
settingsPaneRef={settingsPaneRef}
|
||||||
onBack={() => setPage(Page.Chats)}
|
onBack={() => setPage(SettingsPage.Chats)}
|
||||||
onOpenEditChatFoldersPage={handleOpenEditChatFoldersPage}
|
onOpenEditChatFoldersPage={handleOpenEditChatFoldersPage}
|
||||||
chatFolders={chatFolders}
|
chatFolders={chatFolders}
|
||||||
onCreateChatFolder={handleCreateChatFolder}
|
onCreateChatFolder={handleCreateChatFolder}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.EditChatFolder) {
|
} else if (page === SettingsPage.EditChatFolder) {
|
||||||
let initChatFolderParam: ChatFolderParams;
|
let initChatFolderParam: ChatFolderParams;
|
||||||
if (editChatFolderPageId != null) {
|
if (editChatFolderPageId != null) {
|
||||||
const found = chatFolders.find(chatFolder => {
|
const found = chatFolders.find(chatFolder => {
|
||||||
|
@ -1999,7 +1972,7 @@ export function Preferences({
|
||||||
onDeleteChatFolder={handleDeleteChatFolder}
|
onDeleteChatFolder={handleDeleteChatFolder}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.PNP) {
|
} else if (page === SettingsPage.PNP) {
|
||||||
let sharingDescription: string;
|
let sharingDescription: string;
|
||||||
|
|
||||||
if (whoCanSeeMe === PhoneNumberSharingMode.Everybody) {
|
if (whoCanSeeMe === PhoneNumberSharingMode.Everybody) {
|
||||||
|
@ -2020,7 +1993,7 @@ export function Preferences({
|
||||||
<button
|
<button
|
||||||
aria-label={i18n('icu:goBack')}
|
aria-label={i18n('icu:goBack')}
|
||||||
className="Preferences__back-icon"
|
className="Preferences__back-icon"
|
||||||
onClick={() => setPage(Page.Privacy)}
|
onClick={() => setPage(SettingsPage.Privacy)}
|
||||||
type="button"
|
type="button"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -2142,18 +2115,18 @@ export function Preferences({
|
||||||
);
|
);
|
||||||
} else if (isBackupPage(page)) {
|
} else if (isBackupPage(page)) {
|
||||||
let pageTitle: string | undefined;
|
let pageTitle: string | undefined;
|
||||||
if (page === Page.Backups || page === Page.BackupsDetails) {
|
if (page === SettingsPage.Backups || page === SettingsPage.BackupsDetails) {
|
||||||
pageTitle = i18n('icu:Preferences__button--backups');
|
pageTitle = i18n('icu:Preferences__button--backups');
|
||||||
} else if (page === Page.LocalBackups) {
|
} else if (page === SettingsPage.LocalBackups) {
|
||||||
pageTitle = i18n('icu:Preferences__local-backups');
|
pageTitle = i18n('icu:Preferences__local-backups');
|
||||||
}
|
}
|
||||||
// Local backups setup page titles intentionally left blank
|
// Local backups setup page titles intentionally left blank
|
||||||
|
|
||||||
let backPage: PreferencesBackupPage | undefined;
|
let backPage: PreferencesBackupPage | undefined;
|
||||||
if (page === Page.LocalBackupsKeyReference) {
|
if (page === SettingsPage.LocalBackupsKeyReference) {
|
||||||
backPage = Page.LocalBackups;
|
backPage = SettingsPage.LocalBackups;
|
||||||
} else if (page !== Page.Backups) {
|
} else if (page !== SettingsPage.Backups) {
|
||||||
backPage = Page.Backups;
|
backPage = SettingsPage.Backups;
|
||||||
}
|
}
|
||||||
let backButton: JSX.Element | undefined;
|
let backButton: JSX.Element | undefined;
|
||||||
if (backPage) {
|
if (backPage) {
|
||||||
|
@ -2197,7 +2170,7 @@ export function Preferences({
|
||||||
title={pageTitle}
|
title={pageTitle}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.Internal) {
|
} else if (page === SettingsPage.Internal) {
|
||||||
content = (
|
content = (
|
||||||
<PreferencesContent
|
<PreferencesContent
|
||||||
contents={
|
contents={
|
||||||
|
@ -2249,9 +2222,10 @@ export function Preferences({
|
||||||
type="button"
|
type="button"
|
||||||
className={classNames({
|
className={classNames({
|
||||||
'Preferences__profile-chip': true,
|
'Preferences__profile-chip': true,
|
||||||
'Preferences__profile-chip--selected': page === Page.Profile,
|
'Preferences__profile-chip--selected':
|
||||||
|
page === SettingsPage.Profile,
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.Profile)}
|
onClick={() => setPage(SettingsPage.Profile)}
|
||||||
>
|
>
|
||||||
<div className="Preferences__profile-chip__avatar">
|
<div className="Preferences__profile-chip__avatar">
|
||||||
<Avatar
|
<Avatar
|
||||||
|
@ -2293,9 +2267,10 @@ export function Preferences({
|
||||||
className={classNames({
|
className={classNames({
|
||||||
Preferences__button: true,
|
Preferences__button: true,
|
||||||
'Preferences__button--general': true,
|
'Preferences__button--general': true,
|
||||||
'Preferences__button--selected': page === Page.General,
|
'Preferences__button--selected':
|
||||||
|
page === SettingsPage.General,
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.General)}
|
onClick={() => setPage(SettingsPage.General)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--general')}
|
{i18n('icu:Preferences__button--general')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2305,9 +2280,10 @@ export function Preferences({
|
||||||
Preferences__button: true,
|
Preferences__button: true,
|
||||||
'Preferences__button--appearance': true,
|
'Preferences__button--appearance': true,
|
||||||
'Preferences__button--selected':
|
'Preferences__button--selected':
|
||||||
page === Page.Appearance || page === Page.ChatColor,
|
page === SettingsPage.Appearance ||
|
||||||
|
page === SettingsPage.ChatColor,
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.Appearance)}
|
onClick={() => setPage(SettingsPage.Appearance)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--appearance')}
|
{i18n('icu:Preferences__button--appearance')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2316,9 +2292,9 @@ export function Preferences({
|
||||||
className={classNames({
|
className={classNames({
|
||||||
Preferences__button: true,
|
Preferences__button: true,
|
||||||
'Preferences__button--chats': true,
|
'Preferences__button--chats': true,
|
||||||
'Preferences__button--selected': page === Page.Chats,
|
'Preferences__button--selected': page === SettingsPage.Chats,
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.Chats)}
|
onClick={() => setPage(SettingsPage.Chats)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--chats')}
|
{i18n('icu:Preferences__button--chats')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2327,9 +2303,9 @@ export function Preferences({
|
||||||
className={classNames({
|
className={classNames({
|
||||||
Preferences__button: true,
|
Preferences__button: true,
|
||||||
'Preferences__button--calls': true,
|
'Preferences__button--calls': true,
|
||||||
'Preferences__button--selected': page === Page.Calls,
|
'Preferences__button--selected': page === SettingsPage.Calls,
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.Calls)}
|
onClick={() => setPage(SettingsPage.Calls)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--calls')}
|
{i18n('icu:Preferences__button--calls')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2338,9 +2314,10 @@ export function Preferences({
|
||||||
className={classNames({
|
className={classNames({
|
||||||
Preferences__button: true,
|
Preferences__button: true,
|
||||||
'Preferences__button--notifications': true,
|
'Preferences__button--notifications': true,
|
||||||
'Preferences__button--selected': page === Page.Notifications,
|
'Preferences__button--selected':
|
||||||
|
page === SettingsPage.Notifications,
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.Notifications)}
|
onClick={() => setPage(SettingsPage.Notifications)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--notifications')}
|
{i18n('icu:Preferences__button--notifications')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2350,9 +2327,9 @@ export function Preferences({
|
||||||
Preferences__button: true,
|
Preferences__button: true,
|
||||||
'Preferences__button--privacy': true,
|
'Preferences__button--privacy': true,
|
||||||
'Preferences__button--selected':
|
'Preferences__button--selected':
|
||||||
page === Page.Privacy || page === Page.PNP,
|
page === SettingsPage.Privacy || page === SettingsPage.PNP,
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.Privacy)}
|
onClick={() => setPage(SettingsPage.Privacy)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--privacy')}
|
{i18n('icu:Preferences__button--privacy')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2361,9 +2338,10 @@ export function Preferences({
|
||||||
className={classNames({
|
className={classNames({
|
||||||
Preferences__button: true,
|
Preferences__button: true,
|
||||||
'Preferences__button--data-usage': true,
|
'Preferences__button--data-usage': true,
|
||||||
'Preferences__button--selected': page === Page.DataUsage,
|
'Preferences__button--selected':
|
||||||
|
page === SettingsPage.DataUsage,
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.DataUsage)}
|
onClick={() => setPage(SettingsPage.DataUsage)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--data-usage')}
|
{i18n('icu:Preferences__button--data-usage')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2375,7 +2353,7 @@ export function Preferences({
|
||||||
'Preferences__button--backups': true,
|
'Preferences__button--backups': true,
|
||||||
'Preferences__button--selected': isBackupPage(page),
|
'Preferences__button--selected': isBackupPage(page),
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.Backups)}
|
onClick={() => setPage(SettingsPage.Backups)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--backups')}
|
{i18n('icu:Preferences__button--backups')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2388,7 +2366,7 @@ export function Preferences({
|
||||||
'Preferences__button--appearance': true,
|
'Preferences__button--appearance': true,
|
||||||
'Preferences__button--selected': isDonationsPage(page),
|
'Preferences__button--selected': isDonationsPage(page),
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.Donations)}
|
onClick={() => setPage(SettingsPage.Donations)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--donate')}
|
{i18n('icu:Preferences__button--donate')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2399,9 +2377,10 @@ export function Preferences({
|
||||||
className={classNames({
|
className={classNames({
|
||||||
Preferences__button: true,
|
Preferences__button: true,
|
||||||
'Preferences__button--internal': true,
|
'Preferences__button--internal': true,
|
||||||
'Preferences__button--selected': page === Page.Internal,
|
'Preferences__button--selected':
|
||||||
|
page === SettingsPage.Internal,
|
||||||
})}
|
})}
|
||||||
onClick={() => setPage(Page.Internal)}
|
onClick={() => setPage(SettingsPage.Internal)}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--internal')}
|
{i18n('icu:Preferences__button--internal')}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
import { Button, ButtonVariant } from './Button';
|
import { Button, ButtonVariant } from './Button';
|
||||||
import type { PreferencesBackupPage } from '../types/PreferencesBackupPage';
|
import type { PreferencesBackupPage } from '../types/PreferencesBackupPage';
|
||||||
import { Page } from './Preferences';
|
import { SettingsPage } from '../types/Nav';
|
||||||
import { I18n } from './I18n';
|
import { I18n } from './I18n';
|
||||||
import { PreferencesLocalBackups } from './PreferencesLocalBackups';
|
import { PreferencesLocalBackups } from './PreferencesLocalBackups';
|
||||||
import type { ShowToastAction } from '../state/ducks/toast';
|
import type { ShowToastAction } from '../state/ducks/toast';
|
||||||
|
@ -82,17 +82,17 @@ export function PreferencesBackups({
|
||||||
const [isAuthPending, setIsAuthPending] = useState<boolean>(false);
|
const [isAuthPending, setIsAuthPending] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (page === Page.Backups) {
|
if (page === SettingsPage.Backups) {
|
||||||
refreshBackupSubscriptionStatus();
|
refreshBackupSubscriptionStatus();
|
||||||
} else if (page === Page.BackupsDetails) {
|
} else if (page === SettingsPage.BackupsDetails) {
|
||||||
refreshBackupSubscriptionStatus();
|
refreshBackupSubscriptionStatus();
|
||||||
refreshCloudBackupStatus();
|
refreshCloudBackupStatus();
|
||||||
}
|
}
|
||||||
}, [page, refreshBackupSubscriptionStatus, refreshCloudBackupStatus]);
|
}, [page, refreshBackupSubscriptionStatus, refreshCloudBackupStatus]);
|
||||||
|
|
||||||
if (page === Page.BackupsDetails) {
|
if (page === SettingsPage.BackupsDetails) {
|
||||||
if (backupSubscriptionStatus.status === 'off') {
|
if (backupSubscriptionStatus.status === 'off') {
|
||||||
setPage(Page.Backups);
|
setPage(SettingsPage.Backups);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -110,10 +110,10 @@ export function PreferencesBackups({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
page === Page.LocalBackups ||
|
page === SettingsPage.LocalBackups ||
|
||||||
page === Page.LocalBackupsKeyReference ||
|
page === SettingsPage.LocalBackupsKeyReference ||
|
||||||
page === Page.LocalBackupsSetupFolder ||
|
page === SettingsPage.LocalBackupsSetupFolder ||
|
||||||
page === Page.LocalBackupsSetupKey
|
page === SettingsPage.LocalBackupsSetupKey
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<PreferencesLocalBackups
|
<PreferencesLocalBackups
|
||||||
|
@ -193,7 +193,7 @@ export function PreferencesBackups({
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setPage(Page.BackupsDetails)}
|
onClick={() => setPage(SettingsPage.BackupsDetails)}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
>
|
>
|
||||||
{i18n('icu:Preferences__button--manage')}
|
{i18n('icu:Preferences__button--manage')}
|
||||||
|
@ -246,7 +246,7 @@ export function PreferencesBackups({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPage(Page.LocalBackups);
|
setPage(SettingsPage.LocalBackups);
|
||||||
}}
|
}}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
>
|
>
|
||||||
|
|
|
@ -9,7 +9,8 @@ import { ListBox, ListBoxItem } from 'react-aria-components';
|
||||||
import { getDateTimeFormatter } from '../util/formatTimestamp';
|
import { getDateTimeFormatter } from '../util/formatTimestamp';
|
||||||
|
|
||||||
import type { LocalizerType } from '../types/Util';
|
import type { LocalizerType } from '../types/Util';
|
||||||
import { Page, PreferencesContent } from './Preferences';
|
import { PreferencesContent } from './Preferences';
|
||||||
|
import { SettingsPage } from '../types/Nav';
|
||||||
import { PreferencesDonateFlow } from './PreferencesDonateFlow';
|
import { PreferencesDonateFlow } from './PreferencesDonateFlow';
|
||||||
import type {
|
import type {
|
||||||
DonationWorkflow,
|
DonationWorkflow,
|
||||||
|
@ -39,7 +40,7 @@ type PropsExternalType = {
|
||||||
export type PropsDataType = {
|
export type PropsDataType = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
isStaging: boolean;
|
isStaging: boolean;
|
||||||
page: Page;
|
page: SettingsPage;
|
||||||
workflow: DonationWorkflow | undefined;
|
workflow: DonationWorkflow | undefined;
|
||||||
userAvatarData: ReadonlyArray<AvatarDataType>;
|
userAvatarData: ReadonlyArray<AvatarDataType>;
|
||||||
color?: AvatarColorType;
|
color?: AvatarColorType;
|
||||||
|
@ -62,26 +63,26 @@ export type PropsDataType = {
|
||||||
|
|
||||||
type PropsActionType = {
|
type PropsActionType = {
|
||||||
clearWorkflow: () => void;
|
clearWorkflow: () => void;
|
||||||
setPage: (page: Page) => void;
|
setPage: (page: SettingsPage) => void;
|
||||||
submitDonation: (payload: SubmitDonationType) => void;
|
submitDonation: (payload: SubmitDonationType) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PropsType = PropsDataType & PropsActionType & PropsExternalType;
|
export type PropsType = PropsDataType & PropsActionType & PropsExternalType;
|
||||||
|
|
||||||
type DonationPage =
|
type DonationPage =
|
||||||
| Page.Donations
|
| SettingsPage.Donations
|
||||||
| Page.DonationsDonateFlow
|
| SettingsPage.DonationsDonateFlow
|
||||||
| Page.DonationsReceiptList;
|
| SettingsPage.DonationsReceiptList;
|
||||||
|
|
||||||
type PreferencesHomeProps = PropsType & {
|
type PreferencesHomeProps = PropsType & {
|
||||||
navigateToPage: (newPage: Page) => void;
|
navigateToPage: (newPage: SettingsPage) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function isDonationPage(page: Page): page is DonationPage {
|
function isDonationPage(page: SettingsPage): page is DonationPage {
|
||||||
return (
|
return (
|
||||||
page === Page.Donations ||
|
page === SettingsPage.Donations ||
|
||||||
page === Page.DonationsDonateFlow ||
|
page === SettingsPage.DonationsDonateFlow ||
|
||||||
page === Page.DonationsReceiptList
|
page === SettingsPage.DonationsReceiptList
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ function DonationsHome({
|
||||||
variant={ButtonVariant.Primary}
|
variant={ButtonVariant.Primary}
|
||||||
size={ButtonSize.Medium}
|
size={ButtonSize.Medium}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPage(Page.DonationsDonateFlow);
|
setPage(SettingsPage.DonationsDonateFlow);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18n('icu:PreferencesDonations__donate-button')}
|
{i18n('icu:PreferencesDonations__donate-button')}
|
||||||
|
@ -161,7 +162,7 @@ function DonationsHome({
|
||||||
<ListBoxItem
|
<ListBoxItem
|
||||||
className="PreferencesDonations__list-item"
|
className="PreferencesDonations__list-item"
|
||||||
onAction={() => {
|
onAction={() => {
|
||||||
navigateToPage(Page.DonationsReceiptList);
|
navigateToPage(SettingsPage.DonationsReceiptList);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="PreferencesDonations__list-item__icon PreferencesDonations__list-item__icon--receipts" />
|
<span className="PreferencesDonations__list-item__icon PreferencesDonations__list-item__icon--receipts" />
|
||||||
|
@ -418,7 +419,7 @@ export function PreferencesDonations({
|
||||||
showToast,
|
showToast,
|
||||||
}: PropsType): JSX.Element | null {
|
}: PropsType): JSX.Element | null {
|
||||||
const navigateToPage = useCallback(
|
const navigateToPage = useCallback(
|
||||||
(newPage: Page) => {
|
(newPage: SettingsPage) => {
|
||||||
setPage(newPage);
|
setPage(newPage);
|
||||||
},
|
},
|
||||||
[setPage]
|
[setPage]
|
||||||
|
@ -429,7 +430,7 @@ export function PreferencesDonations({
|
||||||
}
|
}
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
if (page === Page.DonationsDonateFlow) {
|
if (page === SettingsPage.DonationsDonateFlow) {
|
||||||
// DonateFlow has to control Back button to switch between CC form and Amount picker
|
// DonateFlow has to control Back button to switch between CC form and Amount picker
|
||||||
return (
|
return (
|
||||||
<PreferencesDonateFlow
|
<PreferencesDonateFlow
|
||||||
|
@ -440,11 +441,11 @@ export function PreferencesDonations({
|
||||||
workflow={workflow}
|
workflow={workflow}
|
||||||
clearWorkflow={clearWorkflow}
|
clearWorkflow={clearWorkflow}
|
||||||
submitDonation={submitDonation}
|
submitDonation={submitDonation}
|
||||||
onBack={() => setPage(Page.Donations)}
|
onBack={() => setPage(SettingsPage.Donations)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (page === Page.Donations) {
|
if (page === SettingsPage.Donations) {
|
||||||
content = (
|
content = (
|
||||||
<DonationsHome
|
<DonationsHome
|
||||||
contentsRef={contentsRef}
|
contentsRef={contentsRef}
|
||||||
|
@ -468,7 +469,7 @@ export function PreferencesDonations({
|
||||||
submitDonation={submitDonation}
|
submitDonation={submitDonation}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === Page.DonationsReceiptList) {
|
} else if (page === SettingsPage.DonationsReceiptList) {
|
||||||
content = (
|
content = (
|
||||||
<PreferencesReceiptList
|
<PreferencesReceiptList
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
@ -482,15 +483,15 @@ export function PreferencesDonations({
|
||||||
|
|
||||||
let title: string | undefined;
|
let title: string | undefined;
|
||||||
let backButton: JSX.Element | undefined;
|
let backButton: JSX.Element | undefined;
|
||||||
if (page === Page.Donations) {
|
if (page === SettingsPage.Donations) {
|
||||||
title = i18n('icu:Preferences__DonateTitle');
|
title = i18n('icu:Preferences__DonateTitle');
|
||||||
} else if (page === Page.DonationsReceiptList) {
|
} else if (page === SettingsPage.DonationsReceiptList) {
|
||||||
title = i18n('icu:PreferencesDonations__receipts');
|
title = i18n('icu:PreferencesDonations__receipts');
|
||||||
backButton = (
|
backButton = (
|
||||||
<button
|
<button
|
||||||
aria-label={i18n('icu:goBack')}
|
aria-label={i18n('icu:goBack')}
|
||||||
className="Preferences__back-icon"
|
className="Preferences__back-icon"
|
||||||
onClick={() => setPage(Page.Donations)}
|
onClick={() => setPage(SettingsPage.Donations)}
|
||||||
type="button"
|
type="button"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {
|
||||||
} from './PreferencesBackups';
|
} from './PreferencesBackups';
|
||||||
import { I18n } from './I18n';
|
import { I18n } from './I18n';
|
||||||
import type { PreferencesBackupPage } from '../types/PreferencesBackupPage';
|
import type { PreferencesBackupPage } from '../types/PreferencesBackupPage';
|
||||||
import { Page } from './Preferences';
|
import { SettingsPage } from '../types/Nav';
|
||||||
import { ToastType } from '../types/Toast';
|
import { ToastType } from '../types/Toast';
|
||||||
import type { ShowToastAction } from '../state/ducks/toast';
|
import type { ShowToastAction } from '../state/ducks/toast';
|
||||||
import { Modal } from './Modal';
|
import { Modal } from './Modal';
|
||||||
|
@ -73,7 +73,7 @@ export function PreferencesLocalBackups({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isReferencingBackupKey = page === Page.LocalBackupsKeyReference;
|
const isReferencingBackupKey = page === SettingsPage.LocalBackupsKeyReference;
|
||||||
if (!backupKeyViewed || isReferencingBackupKey) {
|
if (!backupKeyViewed || isReferencingBackupKey) {
|
||||||
strictAssert(accountEntropyPool, 'AEP is required for backup key viewer');
|
strictAssert(accountEntropyPool, 'AEP is required for backup key viewer');
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ export function PreferencesLocalBackups({
|
||||||
isReferencing={isReferencingBackupKey}
|
isReferencing={isReferencingBackupKey}
|
||||||
onBackupKeyViewed={() => {
|
onBackupKeyViewed={() => {
|
||||||
if (backupKeyViewed) {
|
if (backupKeyViewed) {
|
||||||
setPage(Page.LocalBackups);
|
setPage(SettingsPage.LocalBackups);
|
||||||
} else {
|
} else {
|
||||||
onBackupKeyViewedChange(true);
|
onBackupKeyViewedChange(true);
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ export function PreferencesLocalBackups({
|
||||||
setIsAuthPending(true);
|
setIsAuthPending(true);
|
||||||
const result = await promptOSAuth('view-aep');
|
const result = await promptOSAuth('view-aep');
|
||||||
if (result === 'success' || result === 'unsupported') {
|
if (result === 'success' || result === 'unsupported') {
|
||||||
setPage(Page.LocalBackupsKeyReference);
|
setPage(SettingsPage.LocalBackupsKeyReference);
|
||||||
} else {
|
} else {
|
||||||
setAuthError(result);
|
setAuthError(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@ import casual from 'casual';
|
||||||
import { v4 as generateUuid } from 'uuid';
|
import { v4 as generateUuid } from 'uuid';
|
||||||
|
|
||||||
import type { PropsType } from './ProfileEditor';
|
import type { PropsType } from './ProfileEditor';
|
||||||
import { EditState, ProfileEditor } from './ProfileEditor';
|
|
||||||
|
import { ProfileEditorPage } from '../types/Nav';
|
||||||
|
import { ProfileEditor } from './ProfileEditor';
|
||||||
import { UsernameEditor } from './UsernameEditor';
|
import { UsernameEditor } from './UsernameEditor';
|
||||||
import {
|
import {
|
||||||
UsernameEditState,
|
UsernameEditState,
|
||||||
|
@ -51,7 +53,7 @@ export default {
|
||||||
conversationId: generateUuid(),
|
conversationId: generateUuid(),
|
||||||
color: getRandomColor(),
|
color: getRandomColor(),
|
||||||
deleteAvatarFromDisk: action('deleteAvatarFromDisk'),
|
deleteAvatarFromDisk: action('deleteAvatarFromDisk'),
|
||||||
editState: EditState.None,
|
editState: ProfileEditorPage.None,
|
||||||
familyName: casual.last_name,
|
familyName: casual.last_name,
|
||||||
firstName: casual.first_name,
|
firstName: casual.first_name,
|
||||||
i18n,
|
i18n,
|
||||||
|
|
|
@ -51,6 +51,7 @@ import { FunEmojiPickerButton } from './fun/FunButton';
|
||||||
import { isFunPickerEnabled } from './fun/isFunPickerEnabled';
|
import { isFunPickerEnabled } from './fun/isFunPickerEnabled';
|
||||||
import { useFunEmojiLocalizer } from './fun/useFunEmojiLocalizer';
|
import { useFunEmojiLocalizer } from './fun/useFunEmojiLocalizer';
|
||||||
import { PreferencesContent } from './Preferences';
|
import { PreferencesContent } from './Preferences';
|
||||||
|
import { ProfileEditorPage } from '../types/Nav';
|
||||||
|
|
||||||
import type { AvatarColorType } from '../types/Colors';
|
import type { AvatarColorType } from '../types/Colors';
|
||||||
import type {
|
import type {
|
||||||
|
@ -73,15 +74,6 @@ import type { EmojiVariantKey } from './fun/data/emojis';
|
||||||
import type { FunEmojiSelection } from './fun/panels/FunPanelEmojis';
|
import type { FunEmojiSelection } from './fun/panels/FunPanelEmojis';
|
||||||
import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
|
import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
|
||||||
|
|
||||||
export enum EditState {
|
|
||||||
None = 'None',
|
|
||||||
BetterAvatar = 'BetterAvatar',
|
|
||||||
ProfileName = 'ProfileName',
|
|
||||||
Bio = 'Bio',
|
|
||||||
Username = 'Username',
|
|
||||||
UsernameLink = 'UsernameLink',
|
|
||||||
}
|
|
||||||
|
|
||||||
type PropsExternalType = {
|
type PropsExternalType = {
|
||||||
onProfileChanged: (
|
onProfileChanged: (
|
||||||
profileData: ProfileDataType,
|
profileData: ProfileDataType,
|
||||||
|
@ -100,7 +92,7 @@ export type PropsDataType = {
|
||||||
firstName: string;
|
firstName: string;
|
||||||
hasCompletedUsernameLinkOnboarding: boolean;
|
hasCompletedUsernameLinkOnboarding: boolean;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
editState: EditState;
|
editState: ProfileEditorPage;
|
||||||
profileAvatarUrl?: string;
|
profileAvatarUrl?: string;
|
||||||
userAvatarData: ReadonlyArray<AvatarDataType>;
|
userAvatarData: ReadonlyArray<AvatarDataType>;
|
||||||
username?: string;
|
username?: string;
|
||||||
|
@ -123,7 +115,7 @@ type PropsActionType = {
|
||||||
setUsernameLinkColor: (color: number) => void;
|
setUsernameLinkColor: (color: number) => void;
|
||||||
resetUsernameLink: () => void;
|
resetUsernameLink: () => void;
|
||||||
deleteUsername: () => void;
|
deleteUsername: () => void;
|
||||||
setEditState: (editState: EditState) => void;
|
setEditState: (editState: ProfileEditorPage) => void;
|
||||||
showToast: ShowToastAction;
|
showToast: ShowToastAction;
|
||||||
openUsernameReservationModal: () => void;
|
openUsernameReservationModal: () => void;
|
||||||
};
|
};
|
||||||
|
@ -219,13 +211,13 @@ export function ProfileEditor({
|
||||||
tryClose,
|
tryClose,
|
||||||
});
|
});
|
||||||
|
|
||||||
const TITLES_BY_EDIT_STATE: Record<EditState, string | undefined> = {
|
const TITLES_BY_EDIT_STATE: Record<ProfileEditorPage, string | undefined> = {
|
||||||
[EditState.BetterAvatar]: i18n('icu:ProfileEditorModal--avatar'),
|
[ProfileEditorPage.BetterAvatar]: i18n('icu:ProfileEditorModal--avatar'),
|
||||||
[EditState.Bio]: i18n('icu:ProfileEditorModal--about'),
|
[ProfileEditorPage.Bio]: i18n('icu:ProfileEditorModal--about'),
|
||||||
[EditState.None]: i18n('icu:ProfileEditorModal--profile'),
|
[ProfileEditorPage.None]: i18n('icu:ProfileEditorModal--profile'),
|
||||||
[EditState.ProfileName]: i18n('icu:ProfileEditorModal--name'),
|
[ProfileEditorPage.ProfileName]: i18n('icu:ProfileEditorModal--name'),
|
||||||
[EditState.Username]: i18n('icu:ProfileEditorModal--username'),
|
[ProfileEditorPage.Username]: i18n('icu:ProfileEditorModal--username'),
|
||||||
[EditState.UsernameLink]: i18n('icu:ProfileEditorModal--sharing'),
|
[ProfileEditorPage.UsernameLink]: i18n('icu:ProfileEditorModal--sharing'),
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is here to avoid component re-render jitters in the time it takes
|
// This is here to avoid component re-render jitters in the time it takes
|
||||||
|
@ -275,7 +267,7 @@ export function ProfileEditor({
|
||||||
|
|
||||||
// To make AvatarEditor re-render less often
|
// To make AvatarEditor re-render less often
|
||||||
const handleBack = useCallback(() => {
|
const handleBack = useCallback(() => {
|
||||||
setEditState(EditState.None);
|
setEditState(ProfileEditorPage.None);
|
||||||
}, [setEditState]);
|
}, [setEditState]);
|
||||||
|
|
||||||
const handleEmojiPickerOpenChange = useCallback((open: boolean) => {
|
const handleEmojiPickerOpenChange = useCallback((open: boolean) => {
|
||||||
|
@ -379,7 +371,7 @@ export function ProfileEditor({
|
||||||
|
|
||||||
let content: JSX.Element;
|
let content: JSX.Element;
|
||||||
|
|
||||||
if (editState === EditState.BetterAvatar) {
|
if (editState === ProfileEditorPage.BetterAvatar) {
|
||||||
content = (
|
content = (
|
||||||
<AvatarEditor
|
<AvatarEditor
|
||||||
avatarColor={color || AvatarColors[0]}
|
avatarColor={color || AvatarColors[0]}
|
||||||
|
@ -396,7 +388,7 @@ export function ProfileEditor({
|
||||||
saveAvatarToDisk={saveAvatarToDisk}
|
saveAvatarToDisk={saveAvatarToDisk}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (editState === EditState.ProfileName) {
|
} else if (editState === ProfileEditorPage.ProfileName) {
|
||||||
const shouldDisableSave =
|
const shouldDisableSave =
|
||||||
!stagedProfile.firstName ||
|
!stagedProfile.firstName ||
|
||||||
(stagedProfile.firstName === fullName.firstName &&
|
(stagedProfile.firstName === fullName.firstName &&
|
||||||
|
@ -458,7 +450,7 @@ export function ProfileEditor({
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else if (editState === EditState.Bio) {
|
} else if (editState === ProfileEditorPage.Bio) {
|
||||||
const shouldDisableSave =
|
const shouldDisableSave =
|
||||||
stagedProfile.aboutText === fullBio.aboutText &&
|
stagedProfile.aboutText === fullBio.aboutText &&
|
||||||
stagedProfile.aboutEmoji === fullBio.aboutEmoji;
|
stagedProfile.aboutEmoji === fullBio.aboutEmoji;
|
||||||
|
@ -587,11 +579,11 @@ export function ProfileEditor({
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else if (editState === EditState.Username) {
|
} else if (editState === ProfileEditorPage.Username) {
|
||||||
content = renderUsernameEditor({
|
content = renderUsernameEditor({
|
||||||
onClose: handleBack,
|
onClose: handleBack,
|
||||||
});
|
});
|
||||||
} else if (editState === EditState.UsernameLink) {
|
} else if (editState === ProfileEditorPage.UsernameLink) {
|
||||||
content = (
|
content = (
|
||||||
<UsernameLinkEditor
|
<UsernameLinkEditor
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
@ -604,10 +596,10 @@ export function ProfileEditor({
|
||||||
resetUsernameLink={resetUsernameLink}
|
resetUsernameLink={resetUsernameLink}
|
||||||
saveAttachment={saveAttachment}
|
saveAttachment={saveAttachment}
|
||||||
showToast={showToast}
|
showToast={showToast}
|
||||||
onBack={() => setEditState(EditState.None)}
|
onBack={() => setEditState(ProfileEditorPage.None)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (editState === EditState.None) {
|
} else if (editState === ProfileEditorPage.None) {
|
||||||
let actions: JSX.Element | undefined;
|
let actions: JSX.Element | undefined;
|
||||||
let alwaysShowActions = false;
|
let alwaysShowActions = false;
|
||||||
|
|
||||||
|
@ -696,7 +688,7 @@ export function ProfileEditor({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setEditState(EditState.UsernameLink);
|
setEditState(ProfileEditorPage.UsernameLink);
|
||||||
}}
|
}}
|
||||||
alwaysShowActions
|
alwaysShowActions
|
||||||
actions={linkActions}
|
actions={linkActions}
|
||||||
|
@ -734,7 +726,7 @@ export function ProfileEditor({
|
||||||
}
|
}
|
||||||
|
|
||||||
openUsernameReservationModal();
|
openUsernameReservationModal();
|
||||||
setEditState(EditState.Username);
|
setEditState(ProfileEditorPage.Username);
|
||||||
}}
|
}}
|
||||||
alwaysShowActions={alwaysShowActions}
|
alwaysShowActions={alwaysShowActions}
|
||||||
actions={actions}
|
actions={actions}
|
||||||
|
@ -758,7 +750,7 @@ export function ProfileEditor({
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onAvatarLoaded={handleAvatarLoaded}
|
onAvatarLoaded={handleAvatarLoaded}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditState(EditState.BetterAvatar);
|
setEditState(ProfileEditorPage.BetterAvatar);
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
height: 80,
|
height: 80,
|
||||||
|
@ -768,7 +760,7 @@ export function ProfileEditor({
|
||||||
<div className="ProfileEditor__EditPhotoContainer">
|
<div className="ProfileEditor__EditPhotoContainer">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditState(EditState.BetterAvatar);
|
setEditState(ProfileEditorPage.BetterAvatar);
|
||||||
}}
|
}}
|
||||||
variant={ButtonVariant.Secondary}
|
variant={ButtonVariant.Secondary}
|
||||||
className="ProfileEditor__EditPhoto"
|
className="ProfileEditor__EditPhoto"
|
||||||
|
@ -783,7 +775,7 @@ export function ProfileEditor({
|
||||||
}
|
}
|
||||||
label={<UserText text={getFullNameText()} />}
|
label={<UserText text={getFullNameText()} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditState(EditState.ProfileName);
|
setEditState(ProfileEditorPage.ProfileName);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<PanelRow
|
<PanelRow
|
||||||
|
@ -805,7 +797,7 @@ export function ProfileEditor({
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditState(EditState.Bio);
|
setEditState(ProfileEditorPage.Bio);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="ProfileEditor__info">
|
<div className="ProfileEditor__info">
|
||||||
|
@ -819,7 +811,7 @@ export function ProfileEditor({
|
||||||
}
|
}
|
||||||
|
|
||||||
const backButton =
|
const backButton =
|
||||||
editState !== EditState.None ? (
|
editState !== ProfileEditorPage.None ? (
|
||||||
<button
|
<button
|
||||||
aria-label={i18n('icu:goBack')}
|
aria-label={i18n('icu:goBack')}
|
||||||
className="Preferences__back-icon"
|
className="Preferences__back-icon"
|
||||||
|
@ -862,7 +854,7 @@ export function ProfileEditor({
|
||||||
{
|
{
|
||||||
action: () => {
|
action: () => {
|
||||||
setIsResettingUsernameLink(false);
|
setIsResettingUsernameLink(false);
|
||||||
setEditState(EditState.UsernameLink);
|
setEditState(ProfileEditorPage.UsernameLink);
|
||||||
},
|
},
|
||||||
style: 'affirmative',
|
style: 'affirmative',
|
||||||
text: i18n('icu:UsernameLinkModalBody__error__fix-now'),
|
text: i18n('icu:UsernameLinkModalBody__error__fix-now'),
|
||||||
|
@ -885,7 +877,7 @@ export function ProfileEditor({
|
||||||
style: 'affirmative',
|
style: 'affirmative',
|
||||||
action: () => {
|
action: () => {
|
||||||
openUsernameReservationModal();
|
openUsernameReservationModal();
|
||||||
setEditState(EditState.Username);
|
setEditState(ProfileEditorPage.Username);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -100,6 +100,10 @@ function getToast(toastType: ToastType): AnyToast {
|
||||||
};
|
};
|
||||||
case ToastType.DeleteForEveryoneFailed:
|
case ToastType.DeleteForEveryoneFailed:
|
||||||
return { toastType: ToastType.DeleteForEveryoneFailed };
|
return { toastType: ToastType.DeleteForEveryoneFailed };
|
||||||
|
case ToastType.DonationCompleted:
|
||||||
|
return { toastType: ToastType.DonationCompleted };
|
||||||
|
case ToastType.DonationProcessing:
|
||||||
|
return { toastType: ToastType.DonationProcessing };
|
||||||
case ToastType.Error:
|
case ToastType.Error:
|
||||||
return { toastType: ToastType.Error };
|
return { toastType: ToastType.Error };
|
||||||
case ToastType.Expired:
|
case ToastType.Expired:
|
||||||
|
@ -252,6 +256,8 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
|
changeLocation: action('changeLocation'),
|
||||||
|
clearDonation: action('clearDonation'),
|
||||||
hideToast: action('hideToast'),
|
hideToast: action('hideToast'),
|
||||||
openFileInFolder: action('openFileInFolder'),
|
openFileInFolder: action('openFileInFolder'),
|
||||||
onShowDebugLog: action('onShowDebugLog'),
|
onShowDebugLog: action('onShowDebugLog'),
|
||||||
|
|
|
@ -5,20 +5,25 @@ import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
import type { LocalizerType } from '../types/Util';
|
|
||||||
import { SECOND } from '../util/durations';
|
import { SECOND } from '../util/durations';
|
||||||
import { Toast } from './Toast';
|
import { Toast } from './Toast';
|
||||||
import { WidthBreakpoint } from './_util';
|
import { WidthBreakpoint } from './_util';
|
||||||
import { UsernameMegaphone } from './UsernameMegaphone';
|
import { UsernameMegaphone } from './UsernameMegaphone';
|
||||||
import { assertDev } from '../util/assert';
|
import { assertDev } from '../util/assert';
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
import type { AnyToast } from '../types/Toast';
|
|
||||||
import { ToastType } from '../types/Toast';
|
import { ToastType } from '../types/Toast';
|
||||||
import type { AnyActionableMegaphone } from '../types/Megaphone';
|
|
||||||
import { MegaphoneType } from '../types/Megaphone';
|
import { MegaphoneType } from '../types/Megaphone';
|
||||||
import { AttachmentNotAvailableModalType } from './AttachmentNotAvailableModal';
|
import { AttachmentNotAvailableModalType } from './AttachmentNotAvailableModal';
|
||||||
|
import { NavTab, SettingsPage } from '../types/Nav';
|
||||||
|
|
||||||
|
import type { LocalizerType } from '../types/Util';
|
||||||
|
import type { AnyToast } from '../types/Toast';
|
||||||
|
import type { AnyActionableMegaphone } from '../types/Megaphone';
|
||||||
|
import type { Location } from '../types/Nav';
|
||||||
|
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
|
changeLocation: (newLocation: Location) => unknown;
|
||||||
|
clearDonation: () => unknown;
|
||||||
hideToast: () => unknown;
|
hideToast: () => unknown;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
openFileInFolder: (target: string) => unknown;
|
openFileInFolder: (target: string) => unknown;
|
||||||
|
@ -28,6 +33,7 @@ export type PropsType = {
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
options?: { wasPinned?: boolean }
|
options?: { wasPinned?: boolean }
|
||||||
) => unknown;
|
) => unknown;
|
||||||
|
setDidResumeDonation: (didResume: boolean) => unknown;
|
||||||
showAttachmentNotAvailableModal: (
|
showAttachmentNotAvailableModal: (
|
||||||
type: AttachmentNotAvailableModalType
|
type: AttachmentNotAvailableModalType
|
||||||
) => void;
|
) => void;
|
||||||
|
@ -42,11 +48,14 @@ export type PropsType = {
|
||||||
const SHORT_TIMEOUT = 3 * SECOND;
|
const SHORT_TIMEOUT = 3 * SECOND;
|
||||||
|
|
||||||
export function renderToast({
|
export function renderToast({
|
||||||
|
changeLocation,
|
||||||
|
clearDonation,
|
||||||
hideToast,
|
hideToast,
|
||||||
i18n,
|
i18n,
|
||||||
openFileInFolder,
|
openFileInFolder,
|
||||||
onShowDebugLog,
|
onShowDebugLog,
|
||||||
onUndoArchive,
|
onUndoArchive,
|
||||||
|
setDidResumeDonation,
|
||||||
showAttachmentNotAvailableModal,
|
showAttachmentNotAvailableModal,
|
||||||
OS,
|
OS,
|
||||||
toast,
|
toast,
|
||||||
|
@ -271,6 +280,54 @@ export function renderToast({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toastType === ToastType.DonationCompleted) {
|
||||||
|
return (
|
||||||
|
<Toast
|
||||||
|
autoDismissDisabled
|
||||||
|
onClose={() => {
|
||||||
|
clearDonation();
|
||||||
|
hideToast();
|
||||||
|
}}
|
||||||
|
toastAction={{
|
||||||
|
label: i18n('icu:view'),
|
||||||
|
onClick: () =>
|
||||||
|
changeLocation({
|
||||||
|
tab: NavTab.Settings,
|
||||||
|
details: {
|
||||||
|
page: SettingsPage.Donations,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n('icu:Donations__Toast__Completed')}
|
||||||
|
</Toast>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toastType === ToastType.DonationProcessing) {
|
||||||
|
return (
|
||||||
|
<Toast
|
||||||
|
onClose={() => {
|
||||||
|
setDidResumeDonation(false);
|
||||||
|
hideToast();
|
||||||
|
}}
|
||||||
|
toastAction={{
|
||||||
|
label: i18n('icu:view'),
|
||||||
|
onClick: () => {
|
||||||
|
changeLocation({
|
||||||
|
tab: NavTab.Settings,
|
||||||
|
details: {
|
||||||
|
page: SettingsPage.DonationsDonateFlow,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n('icu:Donations__Toast__Processing')}
|
||||||
|
</Toast>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (toastType === ToastType.Error) {
|
if (toastType === ToastType.Error) {
|
||||||
return (
|
return (
|
||||||
<Toast
|
<Toast
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { getDefaultConversation } from '../../../test-helpers/getDefaultConversa
|
||||||
import { makeFakeLookupConversationWithoutServiceId } from '../../../test-helpers/fakeLookupConversationWithoutServiceId';
|
import { makeFakeLookupConversationWithoutServiceId } from '../../../test-helpers/fakeLookupConversationWithoutServiceId';
|
||||||
import { ThemeType } from '../../../types/Util';
|
import { ThemeType } from '../../../types/Util';
|
||||||
import { DurationInSeconds } from '../../../util/durations';
|
import { DurationInSeconds } from '../../../util/durations';
|
||||||
import { NavTab } from '../../../state/ducks/nav';
|
import { NavTab } from '../../../types/Nav';
|
||||||
import { getFakeCallHistoryGroup } from '../../../test-helpers/getFakeCallHistoryGroup';
|
import { getFakeCallHistoryGroup } from '../../../test-helpers/getFakeCallHistoryGroup';
|
||||||
|
|
||||||
const { i18n } = window.SignalContext;
|
const { i18n } = window.SignalContext;
|
||||||
|
|
|
@ -53,7 +53,7 @@ import { isConversationMuted } from '../../../util/isConversationMuted';
|
||||||
import { ConversationDetailsGroups } from './ConversationDetailsGroups';
|
import { ConversationDetailsGroups } from './ConversationDetailsGroups';
|
||||||
import { PanelType } from '../../../types/Panels';
|
import { PanelType } from '../../../types/Panels';
|
||||||
import { type CallHistoryGroup } from '../../../types/CallDisposition';
|
import { type CallHistoryGroup } from '../../../types/CallDisposition';
|
||||||
import { NavTab } from '../../../state/ducks/nav';
|
import { NavTab } from '../../../types/Nav';
|
||||||
import { ContextMenu } from '../../ContextMenu';
|
import { ContextMenu } from '../../ContextMenu';
|
||||||
import { canHaveNicknameAndNote } from '../../../util/nicknames';
|
import { canHaveNicknameAndNote } from '../../../util/nicknames';
|
||||||
import { CallHistoryGroupPanelSection } from './CallHistoryGroupPanelSection';
|
import { CallHistoryGroupPanelSection } from './CallHistoryGroupPanelSection';
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
|
|
||||||
import { createLogger } from '../logging/log';
|
import { createLogger } from '../logging/log';
|
||||||
|
|
||||||
import type { Location } from '../state/ducks/nav';
|
|
||||||
import { SECOND } from '../util/durations';
|
import { SECOND } from '../util/durations';
|
||||||
import { sleep } from '../util/sleep';
|
import { sleep } from '../util/sleep';
|
||||||
|
|
||||||
|
import type { Location } from '../types/Nav';
|
||||||
|
|
||||||
const log = createLogger('BeforeNavigate');
|
const log = createLogger('BeforeNavigate');
|
||||||
|
|
||||||
export enum BeforeNavigateResponse {
|
export enum BeforeNavigateResponse {
|
||||||
|
|
|
@ -13,10 +13,7 @@ import {
|
||||||
getDistributionListsForRedux,
|
getDistributionListsForRedux,
|
||||||
loadDistributionLists,
|
loadDistributionLists,
|
||||||
} from './distributionListLoader';
|
} from './distributionListLoader';
|
||||||
import {
|
import { getDonationsForRedux, loadDonationReceipts } from './donationsLoader';
|
||||||
getDonationReceiptsForRedux,
|
|
||||||
loadDonationReceipts,
|
|
||||||
} from './donationReceiptsLoader';
|
|
||||||
import { getStoriesForRedux, loadStories } from './storyLoader';
|
import { getStoriesForRedux, loadStories } from './storyLoader';
|
||||||
import { getUserDataForRedux, loadUserData } from './userLoader';
|
import { getUserDataForRedux, loadUserData } from './userLoader';
|
||||||
import {
|
import {
|
||||||
|
@ -67,7 +64,7 @@ export function getParametersForRedux(): ReduxInitData {
|
||||||
callHistory: getCallsHistoryForRedux(),
|
callHistory: getCallsHistoryForRedux(),
|
||||||
callHistoryUnreadCount: getCallsHistoryUnreadCountForRedux(),
|
callHistoryUnreadCount: getCallsHistoryUnreadCountForRedux(),
|
||||||
callLinks: getCallLinksForRedux(),
|
callLinks: getCallLinksForRedux(),
|
||||||
donations: getDonationReceiptsForRedux(),
|
donations: getDonationsForRedux(),
|
||||||
gifs: getGifsStateForRedux(),
|
gifs: getGifsStateForRedux(),
|
||||||
mainWindowStats,
|
mainWindowStats,
|
||||||
menuOptions,
|
menuOptions,
|
||||||
|
|
|
@ -40,6 +40,8 @@ import type {
|
||||||
ReceiptContext,
|
ReceiptContext,
|
||||||
StripeDonationAmount,
|
StripeDonationAmount,
|
||||||
} from '../types/Donations';
|
} from '../types/Donations';
|
||||||
|
import { ToastType } from '../types/Toast';
|
||||||
|
import { NavTab, SettingsPage } from '../types/Nav';
|
||||||
|
|
||||||
const { createDonationReceipt } = DataWriter;
|
const { createDonationReceipt } = DataWriter;
|
||||||
|
|
||||||
|
@ -63,9 +65,36 @@ const MAX_CREDENTIAL_EXPIRATION_IN_DAYS = 90;
|
||||||
let runDonationAbortController: AbortController | undefined;
|
let runDonationAbortController: AbortController | undefined;
|
||||||
let isInternalDonationInProgress = false;
|
let isInternalDonationInProgress = false;
|
||||||
let isDonationInProgress = false;
|
let isDonationInProgress = false;
|
||||||
|
let isInitialized = false;
|
||||||
|
|
||||||
// Public API
|
// Public API
|
||||||
|
|
||||||
|
// Starting everything up
|
||||||
|
|
||||||
|
export async function initialize(): Promise<void> {
|
||||||
|
if (isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isInitialized = true;
|
||||||
|
|
||||||
|
const workflow = _getWorkflowFromRedux();
|
||||||
|
if (!workflow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (didResumeWorkflowAtStartup() && !isDonationPageVisible()) {
|
||||||
|
log.info(
|
||||||
|
'initialize: We resumed at startup and donation page not visible. Showing processing toast.'
|
||||||
|
);
|
||||||
|
window.reduxActions.toast.showToast({
|
||||||
|
toastType: ToastType.DonationProcessing,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await _runDonationWorkflow();
|
||||||
|
}
|
||||||
|
|
||||||
// These are the four moments the user provides input to the donation workflow. So,
|
// These are the four moments the user provides input to the donation workflow. So,
|
||||||
// UI calls these methods directly; everything else happens automatically.
|
// UI calls these methods directly; everything else happens automatically.
|
||||||
|
|
||||||
|
@ -190,13 +219,11 @@ export async function _saveAndRunWorkflow(
|
||||||
log.info(`${logId}: No need to start workflow; it's been cleared`);
|
log.info(`${logId}: No need to start workflow; it's been cleared`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await runDonationWorkflow();
|
await _runDonationWorkflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// There's one place where this is called outside this file - when starting up, in the
|
export async function _runDonationWorkflow(): Promise<void> {
|
||||||
// onEmpty handler in background.ts.
|
let logId = '_runDonationWorkflow';
|
||||||
export async function runDonationWorkflow(): Promise<void> {
|
|
||||||
let logId = 'runDonationWorkflow';
|
|
||||||
|
|
||||||
let totalCount = 0;
|
let totalCount = 0;
|
||||||
let backoffCount = 0;
|
let backoffCount = 0;
|
||||||
|
@ -283,6 +310,15 @@ export async function runDonationWorkflow(): Promise<void> {
|
||||||
updated = await _redeemReceipt(existing);
|
updated = await _redeemReceipt(existing);
|
||||||
// continuing
|
// continuing
|
||||||
} else if (type === donationStateSchema.Enum.DONE) {
|
} else if (type === donationStateSchema.Enum.DONE) {
|
||||||
|
if (!isDonationPageVisible()) {
|
||||||
|
log.info(
|
||||||
|
`${logId}: Donation page not visible. Showing complete toast.`
|
||||||
|
);
|
||||||
|
window.reduxActions.toast.showToast({
|
||||||
|
toastType: ToastType.DonationCompleted,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
log.info(`${logId}: Workflow is complete. Returning.`);
|
log.info(`${logId}: Workflow is complete. Returning.`);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -789,6 +825,19 @@ async function saveReceipt(workflow: DonationWorkflow, logId: string) {
|
||||||
log.info(`${logId}: Successfully saved receipt`);
|
log.info(`${logId}: Successfully saved receipt`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function didResumeWorkflowAtStartup() {
|
||||||
|
return window.reduxStore.getState().donations.didResumeWorkflowAtStartup;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDonationPageVisible() {
|
||||||
|
const { selectedLocation } = window.reduxStore.getState().nav;
|
||||||
|
return (
|
||||||
|
selectedLocation.tab === NavTab.Settings &&
|
||||||
|
(selectedLocation.details.page === SettingsPage.DonationsDonateFlow ||
|
||||||
|
selectedLocation.details.page === SettingsPage.DonationsReceiptList)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Working with zkgroup receipts
|
// Working with zkgroup receipts
|
||||||
|
|
||||||
function getServerPublicParams(): ServerPublicParams {
|
function getServerPublicParams(): ServerPublicParams {
|
||||||
|
|
|
@ -15,13 +15,16 @@ export async function loadDonationReceipts(): Promise<void> {
|
||||||
donationReceipts = await DataReader.getAllDonationReceipts();
|
donationReceipts = await DataReader.getAllDonationReceipts();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDonationReceiptsForRedux(): DonationsStateType {
|
export function getDonationsForRedux(): DonationsStateType {
|
||||||
strictAssert(
|
strictAssert(
|
||||||
donationReceipts != null,
|
donationReceipts != null,
|
||||||
'donation receipts have not been loaded'
|
'donation receipts have not been loaded'
|
||||||
);
|
);
|
||||||
|
const currentWorkflow = _getWorkflowFromStorage();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentWorkflow: _getWorkflowFromStorage(),
|
currentWorkflow,
|
||||||
|
didResumeWorkflowAtStartup: Boolean(currentWorkflow),
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
receipts: donationReceipts,
|
receipts: donationReceipts,
|
||||||
};
|
};
|
|
@ -180,12 +180,8 @@ import {
|
||||||
MESSAGE_MAX_EDIT_COUNT,
|
MESSAGE_MAX_EDIT_COUNT,
|
||||||
} from '../../util/canEditMessage';
|
} from '../../util/canEditMessage';
|
||||||
import type { ChangeLocationAction } from './nav';
|
import type { ChangeLocationAction } from './nav';
|
||||||
import {
|
import { CHANGE_LOCATION, changeLocation, actions as navActions } from './nav';
|
||||||
CHANGE_LOCATION,
|
import { NavTab, ProfileEditorPage, SettingsPage } from '../../types/Nav';
|
||||||
NavTab,
|
|
||||||
changeLocation,
|
|
||||||
actions as navActions,
|
|
||||||
} from './nav';
|
|
||||||
import { sortByMessageOrder } from '../../types/ForwardDraft';
|
import { sortByMessageOrder } from '../../types/ForwardDraft';
|
||||||
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation';
|
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation';
|
||||||
import {
|
import {
|
||||||
|
@ -221,8 +217,6 @@ import { markFailed } from '../../test-node/util/messageFailures';
|
||||||
import { cleanupMessages } from '../../util/cleanup';
|
import { cleanupMessages } from '../../util/cleanup';
|
||||||
import { MessageModel } from '../../models/messages';
|
import { MessageModel } from '../../models/messages';
|
||||||
import type { ConversationModel } from '../../models/conversations';
|
import type { ConversationModel } from '../../models/conversations';
|
||||||
import { EditState } from '../../components/ProfileEditor';
|
|
||||||
import { Page } from '../../components/Preferences';
|
|
||||||
import { MessageRequestResponseSource } from '../../types/MessageRequestResponseEvent';
|
import { MessageRequestResponseSource } from '../../types/MessageRequestResponseEvent';
|
||||||
|
|
||||||
const log = createLogger('conversations');
|
const log = createLogger('conversations');
|
||||||
|
@ -2268,8 +2262,8 @@ function myProfileChanged(
|
||||||
changeLocation({
|
changeLocation({
|
||||||
tab: NavTab.Settings,
|
tab: NavTab.Settings,
|
||||||
details: {
|
details: {
|
||||||
page: Page.Profile,
|
page: SettingsPage.Profile,
|
||||||
state: EditState.None,
|
state: ProfileEditorPage.None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import { useBoundActions } from '../../hooks/useBoundActions';
|
||||||
import { createLogger } from '../../logging/log';
|
import { createLogger } from '../../logging/log';
|
||||||
import * as Errors from '../../types/errors';
|
import * as Errors from '../../types/errors';
|
||||||
import { isStagingServer } from '../../util/isStagingServer';
|
import { isStagingServer } from '../../util/isStagingServer';
|
||||||
|
import { DataWriter } from '../../sql/Client';
|
||||||
|
import * as donations from '../../services/donations';
|
||||||
|
|
||||||
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
|
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
|
||||||
import type {
|
import type {
|
||||||
|
@ -18,8 +20,7 @@ import type {
|
||||||
StripeDonationAmount,
|
StripeDonationAmount,
|
||||||
} from '../../types/Donations';
|
} from '../../types/Donations';
|
||||||
import type { StateType as RootStateType } from '../reducer';
|
import type { StateType as RootStateType } from '../reducer';
|
||||||
import { DataWriter } from '../../sql/Client';
|
import { drop } from '../../util/drop';
|
||||||
import * as donations from '../../services/donations';
|
|
||||||
|
|
||||||
const log = createLogger('donations');
|
const log = createLogger('donations');
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@ export type DonationsStateType = ReadonlyDeep<{
|
||||||
currentWorkflow: DonationWorkflow | undefined;
|
currentWorkflow: DonationWorkflow | undefined;
|
||||||
lastError: DonationErrorType | undefined;
|
lastError: DonationErrorType | undefined;
|
||||||
receipts: Array<DonationReceipt>;
|
receipts: Array<DonationReceipt>;
|
||||||
|
didResumeWorkflowAtStartup: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
@ -37,12 +39,18 @@ export const ADD_RECEIPT = 'donations/ADD_RECEIPT';
|
||||||
export const SUBMIT_DONATION = 'donations/SUBMIT_DONATION';
|
export const SUBMIT_DONATION = 'donations/SUBMIT_DONATION';
|
||||||
export const UPDATE_WORKFLOW = 'donations/UPDATE_WORKFLOW';
|
export const UPDATE_WORKFLOW = 'donations/UPDATE_WORKFLOW';
|
||||||
export const UPDATE_LAST_ERROR = 'donations/UPDATE_LAST_ERROR';
|
export const UPDATE_LAST_ERROR = 'donations/UPDATE_LAST_ERROR';
|
||||||
|
export const SET_DID_RESUME = 'donations/SET_DID_RESUME';
|
||||||
|
|
||||||
export type AddReceiptAction = ReadonlyDeep<{
|
export type AddReceiptAction = ReadonlyDeep<{
|
||||||
type: typeof ADD_RECEIPT;
|
type: typeof ADD_RECEIPT;
|
||||||
payload: { receipt: DonationReceipt };
|
payload: { receipt: DonationReceipt };
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
export type SetDidResumeAction = ReadonlyDeep<{
|
||||||
|
type: typeof SET_DID_RESUME;
|
||||||
|
payload: boolean;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type SubmitDonationAction = ReadonlyDeep<{
|
export type SubmitDonationAction = ReadonlyDeep<{
|
||||||
type: typeof SUBMIT_DONATION;
|
type: typeof SUBMIT_DONATION;
|
||||||
payload: SubmitDonationType;
|
payload: SubmitDonationType;
|
||||||
|
@ -60,6 +68,7 @@ export type UpdateWorkflowAction = ReadonlyDeep<{
|
||||||
|
|
||||||
export type DonationsActionType = ReadonlyDeep<
|
export type DonationsActionType = ReadonlyDeep<
|
||||||
| AddReceiptAction
|
| AddReceiptAction
|
||||||
|
| SetDidResumeAction
|
||||||
| SubmitDonationAction
|
| SubmitDonationAction
|
||||||
| UpdateLastErrorAction
|
| UpdateLastErrorAction
|
||||||
| UpdateWorkflowAction
|
| UpdateWorkflowAction
|
||||||
|
@ -97,6 +106,13 @@ function internalAddDonationReceipt(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setDidResume(didResume: boolean): SetDidResumeAction {
|
||||||
|
return {
|
||||||
|
type: SET_DID_RESUME,
|
||||||
|
payload: didResume,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type SubmitDonationType = ReadonlyDeep<{
|
export type SubmitDonationType = ReadonlyDeep<{
|
||||||
currencyType: string;
|
currencyType: string;
|
||||||
paymentAmount: StripeDonationAmount;
|
paymentAmount: StripeDonationAmount;
|
||||||
|
@ -132,6 +148,8 @@ function submitDonation({
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearWorkflow(): UpdateWorkflowAction {
|
function clearWorkflow(): UpdateWorkflowAction {
|
||||||
|
drop(donations.clearDonation());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: UPDATE_WORKFLOW,
|
type: UPDATE_WORKFLOW,
|
||||||
payload: { nextWorkflow: undefined },
|
payload: { nextWorkflow: undefined },
|
||||||
|
@ -160,6 +178,7 @@ export const actions = {
|
||||||
addReceipt,
|
addReceipt,
|
||||||
clearWorkflow,
|
clearWorkflow,
|
||||||
internalAddDonationReceipt,
|
internalAddDonationReceipt,
|
||||||
|
setDidResume,
|
||||||
submitDonation,
|
submitDonation,
|
||||||
updateLastError,
|
updateLastError,
|
||||||
updateWorkflow,
|
updateWorkflow,
|
||||||
|
@ -174,6 +193,7 @@ export const useDonationsActions = (): BoundActionCreatorsMapObject<
|
||||||
export function getEmptyState(): DonationsStateType {
|
export function getEmptyState(): DonationsStateType {
|
||||||
return {
|
return {
|
||||||
currentWorkflow: undefined,
|
currentWorkflow: undefined,
|
||||||
|
didResumeWorkflowAtStartup: false,
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
receipts: [],
|
receipts: [],
|
||||||
};
|
};
|
||||||
|
@ -190,6 +210,13 @@ export function reducer(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.type === SET_DID_RESUME) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
didResumeWorkflowAtStartup: action.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (action.type === UPDATE_LAST_ERROR) {
|
if (action.type === UPDATE_LAST_ERROR) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -198,9 +225,11 @@ export function reducer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === UPDATE_WORKFLOW) {
|
if (action.type === UPDATE_WORKFLOW) {
|
||||||
|
const { nextWorkflow } = action.payload;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentWorkflow: action.payload.nextWorkflow,
|
currentWorkflow: nextWorkflow,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,38 +6,19 @@ import type { ThunkAction } from 'redux-thunk';
|
||||||
|
|
||||||
import { createLogger } from '../../logging/log';
|
import { createLogger } from '../../logging/log';
|
||||||
import { useBoundActions } from '../../hooks/useBoundActions';
|
import { useBoundActions } from '../../hooks/useBoundActions';
|
||||||
import { Page } from '../../components/Preferences';
|
import { NavTab, SettingsPage } from '../../types/Nav';
|
||||||
|
|
||||||
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
|
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
|
||||||
import type { StateType as RootStateType } from '../reducer';
|
import type { StateType as RootStateType } from '../reducer';
|
||||||
import type { EditState } from '../../components/ProfileEditor';
|
import type { Location } from '../../types/Nav';
|
||||||
|
|
||||||
const log = createLogger('nav');
|
const log = createLogger('nav');
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
|
|
||||||
export enum NavTab {
|
|
||||||
Chats = 'Chats',
|
|
||||||
Calls = 'Calls',
|
|
||||||
Stories = 'Stories',
|
|
||||||
Settings = 'Settings',
|
|
||||||
}
|
|
||||||
export type Location = ReadonlyDeep<
|
|
||||||
| {
|
|
||||||
tab: NavTab.Settings;
|
|
||||||
details:
|
|
||||||
| {
|
|
||||||
page: Page.Profile;
|
|
||||||
state: EditState;
|
|
||||||
}
|
|
||||||
| { page: Exclude<Page, Page.Profile> };
|
|
||||||
}
|
|
||||||
| { tab: Exclude<NavTab, NavTab.Settings> }
|
|
||||||
>;
|
|
||||||
|
|
||||||
function printLocation(location: Location): string {
|
function printLocation(location: Location): string {
|
||||||
if (location.tab === NavTab.Settings) {
|
if (location.tab === NavTab.Settings) {
|
||||||
if (location.details.page === Page.Profile) {
|
if (location.details.page === SettingsPage.Profile) {
|
||||||
return `${location.tab}/${location.details.page}/${location.details.state}`;
|
return `${location.tab}/${location.details.page}/${location.details.state}`;
|
||||||
}
|
}
|
||||||
return `${location.tab}/${location.details.page}`;
|
return `${location.tab}/${location.details.page}`;
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import type { StateType } from '../reducer';
|
|
||||||
import { NavTab, type NavStateType } from '../ducks/nav';
|
|
||||||
import { getAllConversationsUnreadStats } from './conversations';
|
import { getAllConversationsUnreadStats } from './conversations';
|
||||||
import { getStoriesNotificationCount } from './stories';
|
import { getStoriesNotificationCount } from './stories';
|
||||||
import type { UnreadStats } from '../../util/countUnreadStats';
|
|
||||||
import { getCallHistoryUnreadCount } from './callHistory';
|
import { getCallHistoryUnreadCount } from './callHistory';
|
||||||
|
import { NavTab } from '../../types/Nav';
|
||||||
|
|
||||||
|
import type { StateType } from '../reducer';
|
||||||
|
import type { NavStateType } from '../ducks/nav';
|
||||||
|
import type { UnreadStats } from '../../util/countUnreadStats';
|
||||||
|
|
||||||
function getNav(state: StateType): NavStateType {
|
function getNav(state: StateType): NavStateType {
|
||||||
return state.nav;
|
return state.nav;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
// Copyright 2023 Signal Messenger, LLC
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import type { ReactNode } from 'react';
|
|
||||||
import React, { memo, useCallback } from 'react';
|
import React, { memo, useCallback } from 'react';
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { NavTabs } from '../../components/NavTabs';
|
import { NavTabs } from '../../components/NavTabs';
|
||||||
import { getIntl, getTheme, getIsNightly } from '../selectors/user';
|
import { getIntl, getTheme, getIsNightly } from '../selectors/user';
|
||||||
|
@ -20,13 +22,14 @@ import {
|
||||||
getStoriesEnabled,
|
getStoriesEnabled,
|
||||||
} from '../selectors/items';
|
} from '../selectors/items';
|
||||||
import { getSelectedNavTab } from '../selectors/nav';
|
import { getSelectedNavTab } from '../selectors/nav';
|
||||||
import type { Location } from '../ducks/nav';
|
|
||||||
import { useNavActions } from '../ducks/nav';
|
import { useNavActions } from '../ducks/nav';
|
||||||
import { getHasPendingUpdate } from '../selectors/updates';
|
import { getHasPendingUpdate } from '../selectors/updates';
|
||||||
import { getCallHistoryUnreadCount } from '../selectors/callHistory';
|
import { getCallHistoryUnreadCount } from '../selectors/callHistory';
|
||||||
import { Environment } from '../../environment';
|
import { Environment } from '../../environment';
|
||||||
import { useItemsActions } from '../ducks/items';
|
import { useItemsActions } from '../ducks/items';
|
||||||
|
|
||||||
|
import type { Location } from '../../types/Nav';
|
||||||
|
|
||||||
export type SmartNavTabsProps = Readonly<{
|
export type SmartNavTabsProps = Readonly<{
|
||||||
navTabsCollapsed: boolean;
|
navTabsCollapsed: boolean;
|
||||||
onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => void;
|
onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => void;
|
||||||
|
|
|
@ -55,15 +55,15 @@ import { waitForEvent } from '../../shims/events';
|
||||||
import { MINUTE } from '../../util/durations';
|
import { MINUTE } from '../../util/durations';
|
||||||
import { sendSyncRequests } from '../../textsecure/syncRequests';
|
import { sendSyncRequests } from '../../textsecure/syncRequests';
|
||||||
import { SmartUpdateDialog } from './UpdateDialog';
|
import { SmartUpdateDialog } from './UpdateDialog';
|
||||||
import { Page, Preferences } from '../../components/Preferences';
|
import { Preferences } from '../../components/Preferences';
|
||||||
import { useUpdatesActions } from '../ducks/updates';
|
import { useUpdatesActions } from '../ducks/updates';
|
||||||
import { getUpdateDialogType } from '../selectors/updates';
|
import { getUpdateDialogType } from '../selectors/updates';
|
||||||
import { getHasAnyFailedStorySends } from '../selectors/stories';
|
import { getHasAnyFailedStorySends } from '../selectors/stories';
|
||||||
import { getOtherTabsUnreadStats, getSelectedLocation } from '../selectors/nav';
|
import { getOtherTabsUnreadStats, getSelectedLocation } from '../selectors/nav';
|
||||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||||
import { SmartProfileEditor } from './ProfileEditor';
|
import { SmartProfileEditor } from './ProfileEditor';
|
||||||
import { NavTab, useNavActions } from '../ducks/nav';
|
import { useNavActions } from '../ducks/nav';
|
||||||
import { EditState } from '../../components/ProfileEditor';
|
import { NavTab, ProfileEditorPage, SettingsPage } from '../../types/Nav';
|
||||||
import { SmartToastManager } from './ToastManager';
|
import { SmartToastManager } from './ToastManager';
|
||||||
import { useToastActions } from '../ducks/toast';
|
import { useToastActions } from '../ducks/toast';
|
||||||
import { DataReader } from '../../sql/Client';
|
import { DataReader } from '../../sql/Client';
|
||||||
|
@ -111,8 +111,8 @@ function renderDonationsPane({
|
||||||
setPage,
|
setPage,
|
||||||
}: {
|
}: {
|
||||||
contentsRef: MutableRefObject<HTMLDivElement | null>;
|
contentsRef: MutableRefObject<HTMLDivElement | null>;
|
||||||
page: Page;
|
page: SettingsPage;
|
||||||
setPage: (page: Page) => void;
|
setPage: (page: SettingsPage) => void;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<SmartPreferencesDonations
|
<SmartPreferencesDonations
|
||||||
|
@ -687,13 +687,13 @@ export function SmartPreferences(): JSX.Element | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { page } = currentLocation.details;
|
const { page } = currentLocation.details;
|
||||||
const setPage = (newPage: Page, editState?: EditState) => {
|
const setPage = (newPage: SettingsPage, editState?: ProfileEditorPage) => {
|
||||||
if (newPage === Page.Profile) {
|
if (newPage === SettingsPage.Profile) {
|
||||||
changeLocation({
|
changeLocation({
|
||||||
tab: NavTab.Settings,
|
tab: NavTab.Settings,
|
||||||
details: {
|
details: {
|
||||||
page: newPage,
|
page: newPage,
|
||||||
state: editState || EditState.None,
|
state: editState || ProfileEditorPage.None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import type { MutableRefObject } from 'react';
|
||||||
import { getIntl } from '../selectors/user';
|
import { getIntl } from '../selectors/user';
|
||||||
import { getMe } from '../selectors/conversations';
|
import { getMe } from '../selectors/conversations';
|
||||||
import { PreferencesDonations } from '../../components/PreferencesDonations';
|
import { PreferencesDonations } from '../../components/PreferencesDonations';
|
||||||
import type { Page } from '../../components/Preferences';
|
import type { SettingsPage } from '../../types/Nav';
|
||||||
import { useDonationsActions } from '../ducks/donations';
|
import { useDonationsActions } from '../ducks/donations';
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
import { isStagingServer } from '../../util/isStagingServer';
|
import { isStagingServer } from '../../util/isStagingServer';
|
||||||
|
@ -26,8 +26,8 @@ export const SmartPreferencesDonations = memo(
|
||||||
setPage,
|
setPage,
|
||||||
}: {
|
}: {
|
||||||
contentsRef: MutableRefObject<HTMLDivElement | null>;
|
contentsRef: MutableRefObject<HTMLDivElement | null>;
|
||||||
page: Page;
|
page: SettingsPage;
|
||||||
setPage: (page: Page) => void;
|
setPage: (page: SettingsPage) => void;
|
||||||
}) {
|
}) {
|
||||||
const [validCurrencies, setValidCurrencies] = useState<
|
const [validCurrencies, setValidCurrencies] = useState<
|
||||||
ReadonlyArray<string>
|
ReadonlyArray<string>
|
||||||
|
|
|
@ -27,10 +27,10 @@ import {
|
||||||
} from '../selectors/username';
|
} from '../selectors/username';
|
||||||
import { SmartUsernameEditor } from './UsernameEditor';
|
import { SmartUsernameEditor } from './UsernameEditor';
|
||||||
import { getSelectedLocation } from '../selectors/nav';
|
import { getSelectedLocation } from '../selectors/nav';
|
||||||
import { NavTab, useNavActions } from '../ducks/nav';
|
import { useNavActions } from '../ducks/nav';
|
||||||
import { Page } from '../../components/Preferences';
|
import { NavTab, SettingsPage } from '../../types/Nav';
|
||||||
|
|
||||||
import type { EditState } from '../../components/ProfileEditor';
|
import type { ProfileEditorPage } from '../../types/Nav';
|
||||||
import type { SmartUsernameEditorProps } from './UsernameEditor';
|
import type { SmartUsernameEditorProps } from './UsernameEditor';
|
||||||
import { ConfirmationDialog } from '../../components/ConfirmationDialog';
|
import { ConfirmationDialog } from '../../components/ConfirmationDialog';
|
||||||
|
|
||||||
|
@ -103,17 +103,17 @@ export const SmartProfileEditor = memo(function SmartProfileEditor(props: {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
selectedLocation.tab !== NavTab.Settings ||
|
selectedLocation.tab !== NavTab.Settings ||
|
||||||
selectedLocation.details.page !== Page.Profile
|
selectedLocation.details.page !== SettingsPage.Profile
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const editState = selectedLocation.details.state;
|
const editState = selectedLocation.details.state;
|
||||||
const setEditState = (newState: EditState) => {
|
const setEditState = (newState: ProfileEditorPage) => {
|
||||||
changeLocation({
|
changeLocation({
|
||||||
tab: NavTab.Settings,
|
tab: NavTab.Settings,
|
||||||
details: {
|
details: {
|
||||||
page: Page.Profile,
|
page: SettingsPage.Profile,
|
||||||
state: newState,
|
state: newState,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,11 +21,13 @@ import { getMe, getSelectedConversationId } from '../selectors/conversations';
|
||||||
import { useConversationsActions } from '../ducks/conversations';
|
import { useConversationsActions } from '../ducks/conversations';
|
||||||
import { useToastActions } from '../ducks/toast';
|
import { useToastActions } from '../ducks/toast';
|
||||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||||
import { NavTab } from '../ducks/nav';
|
import { useNavActions } from '../ducks/nav';
|
||||||
|
import { NavTab } from '../../types/Nav';
|
||||||
import { getHasCompletedUsernameOnboarding } from '../selectors/items';
|
import { getHasCompletedUsernameOnboarding } from '../selectors/items';
|
||||||
import { ToastManager } from '../../components/ToastManager';
|
import { ToastManager } from '../../components/ToastManager';
|
||||||
import type { WidthBreakpoint } from '../../components/_util';
|
import type { WidthBreakpoint } from '../../components/_util';
|
||||||
import { getToast } from '../selectors/toast';
|
import { getToast } from '../selectors/toast';
|
||||||
|
import { useDonationsActions } from '../ducks/donations';
|
||||||
|
|
||||||
export type SmartPropsType = Readonly<{
|
export type SmartPropsType = Readonly<{
|
||||||
disableMegaphone?: boolean;
|
disableMegaphone?: boolean;
|
||||||
|
@ -53,6 +55,8 @@ export const SmartToastManager = memo(function SmartToastManager({
|
||||||
const { username } = useSelector(getMe);
|
const { username } = useSelector(getMe);
|
||||||
const selectedNavTab = useSelector(getSelectedNavTab);
|
const selectedNavTab = useSelector(getSelectedNavTab);
|
||||||
const selectedConversationId = useSelector(getSelectedConversationId);
|
const selectedConversationId = useSelector(getSelectedConversationId);
|
||||||
|
const { changeLocation } = useNavActions();
|
||||||
|
const { clearWorkflow, setDidResume } = useDonationsActions();
|
||||||
|
|
||||||
const { onUndoArchive } = useConversationsActions();
|
const { onUndoArchive } = useConversationsActions();
|
||||||
const { openFileInFolder, hideToast } = useToastActions();
|
const { openFileInFolder, hideToast } = useToastActions();
|
||||||
|
@ -86,6 +90,8 @@ export const SmartToastManager = memo(function SmartToastManager({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToastManager
|
<ToastManager
|
||||||
|
changeLocation={changeLocation}
|
||||||
|
clearDonation={clearWorkflow}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
OS={OS.getName()}
|
OS={OS.getName()}
|
||||||
toast={toast}
|
toast={toast}
|
||||||
|
@ -94,6 +100,7 @@ export const SmartToastManager = memo(function SmartToastManager({
|
||||||
onUndoArchive={onUndoArchive}
|
onUndoArchive={onUndoArchive}
|
||||||
openFileInFolder={openFileInFolder}
|
openFileInFolder={openFileInFolder}
|
||||||
hideToast={hideToast}
|
hideToast={hideToast}
|
||||||
|
setDidResumeDonation={setDidResume}
|
||||||
showAttachmentNotAvailableModal={showAttachmentNotAvailableModal}
|
showAttachmentNotAvailableModal={showAttachmentNotAvailableModal}
|
||||||
centerToast={centerToast}
|
centerToast={centerToast}
|
||||||
containerWidthBreakpoint={containerWidthBreakpoint}
|
containerWidthBreakpoint={containerWidthBreakpoint}
|
||||||
|
|
|
@ -8,9 +8,8 @@ import { UsernameOnboardingModal } from '../../components/UsernameOnboardingModa
|
||||||
import { getIntl } from '../selectors/user';
|
import { getIntl } from '../selectors/user';
|
||||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||||
import { useUsernameActions } from '../ducks/username';
|
import { useUsernameActions } from '../ducks/username';
|
||||||
import { NavTab, useNavActions } from '../ducks/nav';
|
import { useNavActions } from '../ducks/nav';
|
||||||
import { Page } from '../../components/Preferences';
|
import { NavTab, SettingsPage, ProfileEditorPage } from '../../types/Nav';
|
||||||
import { EditState } from '../../components/ProfileEditor';
|
|
||||||
|
|
||||||
export const SmartUsernameOnboardingModal = memo(
|
export const SmartUsernameOnboardingModal = memo(
|
||||||
function SmartUsernameOnboardingModal(): JSX.Element {
|
function SmartUsernameOnboardingModal(): JSX.Element {
|
||||||
|
@ -25,8 +24,8 @@ export const SmartUsernameOnboardingModal = memo(
|
||||||
changeLocation({
|
changeLocation({
|
||||||
tab: NavTab.Settings,
|
tab: NavTab.Settings,
|
||||||
details: {
|
details: {
|
||||||
page: Page.Profile,
|
page: SettingsPage.Profile,
|
||||||
state: EditState.Username,
|
state: ProfileEditorPage.Username,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
toggleUsernameOnboarding();
|
toggleUsernameOnboarding();
|
||||||
|
|
61
ts/types/Nav.ts
Normal file
61
ts/types/Nav.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright 2020 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { ReadonlyDeep } from 'type-fest';
|
||||||
|
|
||||||
|
export type Location = ReadonlyDeep<
|
||||||
|
| {
|
||||||
|
tab: NavTab.Settings;
|
||||||
|
details:
|
||||||
|
| {
|
||||||
|
page: SettingsPage.Profile;
|
||||||
|
state: ProfileEditorPage;
|
||||||
|
}
|
||||||
|
| { page: Exclude<SettingsPage, SettingsPage.Profile> };
|
||||||
|
}
|
||||||
|
| { tab: Exclude<NavTab, NavTab.Settings> }
|
||||||
|
>;
|
||||||
|
|
||||||
|
export enum NavTab {
|
||||||
|
Chats = 'Chats',
|
||||||
|
Calls = 'Calls',
|
||||||
|
Stories = 'Stories',
|
||||||
|
Settings = 'Settings',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SettingsPage {
|
||||||
|
// Accessible through left nav
|
||||||
|
Profile = 'Profile',
|
||||||
|
General = 'General',
|
||||||
|
Donations = 'Donations',
|
||||||
|
Appearance = 'Appearance',
|
||||||
|
Chats = 'Chats',
|
||||||
|
Calls = 'Calls',
|
||||||
|
Notifications = 'Notifications',
|
||||||
|
Privacy = 'Privacy',
|
||||||
|
DataUsage = 'DataUsage',
|
||||||
|
Backups = 'Backups',
|
||||||
|
Internal = 'Internal',
|
||||||
|
|
||||||
|
// Sub pages
|
||||||
|
ChatColor = 'ChatColor',
|
||||||
|
ChatFolders = 'ChatFolders',
|
||||||
|
DonationsDonateFlow = 'DonationsDonateFlow',
|
||||||
|
DonationsReceiptList = 'DonationsReceiptList',
|
||||||
|
EditChatFolder = 'EditChatFolder',
|
||||||
|
PNP = 'PNP',
|
||||||
|
BackupsDetails = 'BackupsDetails',
|
||||||
|
LocalBackups = 'LocalBackups',
|
||||||
|
LocalBackupsSetupFolder = 'LocalBackupsSetupFolder',
|
||||||
|
LocalBackupsSetupKey = 'LocalBackupsSetupKey',
|
||||||
|
LocalBackupsKeyReference = 'LocalBackupsKeyReference',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProfileEditorPage {
|
||||||
|
None = 'None',
|
||||||
|
BetterAvatar = 'BetterAvatar',
|
||||||
|
ProfileName = 'ProfileName',
|
||||||
|
Bio = 'Bio',
|
||||||
|
Username = 'Username',
|
||||||
|
UsernameLink = 'UsernameLink',
|
||||||
|
}
|
|
@ -1,25 +1,27 @@
|
||||||
// Copyright 2025 Signal Messenger, LLC
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { Page } from '../components/Preferences';
|
import { SettingsPage } from './Nav';
|
||||||
|
|
||||||
// Should be in sync with isBackupPage()
|
// Should be in sync with isBackupPage()
|
||||||
export type PreferencesBackupPage =
|
export type PreferencesBackupPage =
|
||||||
| Page.Backups
|
| SettingsPage.Backups
|
||||||
| Page.BackupsDetails
|
| SettingsPage.BackupsDetails
|
||||||
| Page.LocalBackups
|
| SettingsPage.LocalBackups
|
||||||
| Page.LocalBackupsKeyReference
|
| SettingsPage.LocalBackupsKeyReference
|
||||||
| Page.LocalBackupsSetupFolder
|
| SettingsPage.LocalBackupsSetupFolder
|
||||||
| Page.LocalBackupsSetupKey;
|
| SettingsPage.LocalBackupsSetupKey;
|
||||||
|
|
||||||
// Should be in sync with PreferencesBackupPage
|
// Should be in sync with PreferencesBackupPage
|
||||||
export function isBackupPage(page: Page): page is PreferencesBackupPage {
|
export function isBackupPage(
|
||||||
|
page: SettingsPage
|
||||||
|
): page is PreferencesBackupPage {
|
||||||
return (
|
return (
|
||||||
page === Page.Backups ||
|
page === SettingsPage.Backups ||
|
||||||
page === Page.BackupsDetails ||
|
page === SettingsPage.BackupsDetails ||
|
||||||
page === Page.LocalBackups ||
|
page === SettingsPage.LocalBackups ||
|
||||||
page === Page.LocalBackupsSetupFolder ||
|
page === SettingsPage.LocalBackupsSetupFolder ||
|
||||||
page === Page.LocalBackupsSetupKey ||
|
page === SettingsPage.LocalBackupsSetupKey ||
|
||||||
page === Page.LocalBackupsKeyReference
|
page === SettingsPage.LocalBackupsKeyReference
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ export enum ToastType {
|
||||||
DecryptionError = 'DecryptionError',
|
DecryptionError = 'DecryptionError',
|
||||||
DebugLogError = 'DebugLogError',
|
DebugLogError = 'DebugLogError',
|
||||||
DeleteForEveryoneFailed = 'DeleteForEveryoneFailed',
|
DeleteForEveryoneFailed = 'DeleteForEveryoneFailed',
|
||||||
|
DonationCompleted = 'DonationCompleted',
|
||||||
|
DonationProcessing = 'DonationProcessing',
|
||||||
Error = 'Error',
|
Error = 'Error',
|
||||||
Expired = 'Expired',
|
Expired = 'Expired',
|
||||||
FailedToDeleteUsername = 'FailedToDeleteUsername',
|
FailedToDeleteUsername = 'FailedToDeleteUsername',
|
||||||
|
@ -120,6 +122,8 @@ export type AnyToast =
|
||||||
| { toastType: ToastType.DangerousFileType }
|
| { toastType: ToastType.DangerousFileType }
|
||||||
| { toastType: ToastType.DebugLogError }
|
| { toastType: ToastType.DebugLogError }
|
||||||
| { toastType: ToastType.DeleteForEveryoneFailed }
|
| { toastType: ToastType.DeleteForEveryoneFailed }
|
||||||
|
| { toastType: ToastType.DonationCompleted }
|
||||||
|
| { toastType: ToastType.DonationProcessing }
|
||||||
| { toastType: ToastType.Error }
|
| { toastType: ToastType.Error }
|
||||||
| { toastType: ToastType.Expired }
|
| { toastType: ToastType.Expired }
|
||||||
| { toastType: ToastType.FailedToDeleteUsername }
|
| { toastType: ToastType.FailedToDeleteUsername }
|
||||||
|
|
|
@ -126,6 +126,7 @@ window.testUtilities = {
|
||||||
stories: [],
|
stories: [],
|
||||||
storyDistributionLists: [],
|
storyDistributionLists: [],
|
||||||
donations: {
|
donations: {
|
||||||
|
didResumeWorkflowAtStartup: false,
|
||||||
currentWorkflow: undefined,
|
currentWorkflow: undefined,
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
receipts: [],
|
receipts: [],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue