From 7c860f27b37156f38a0d5302bb447e91c47612c1 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sat, 3 Jul 2021 15:34:03 +0300 Subject: [PATCH] Show confirmation modal before joining with invite links (#1225) --- src/api/gramjs/methods/chats.ts | 26 ++++- src/api/gramjs/methods/index.ts | 2 +- src/api/types/updates.ts | 14 ++- src/bundles/extra.ts | 2 +- .../settings/folders/SettingsFoldersMain.tsx | 18 +-- .../{Errors.async.tsx => Dialogs.async.tsx} | 8 +- .../main/{Errors.scss => Dialogs.scss} | 10 +- src/components/main/Dialogs.tsx | 103 ++++++++++++++++++ src/components/main/Errors.tsx | 57 ---------- src/components/main/Main.tsx | 10 +- src/components/middle/composer/Composer.tsx | 16 +-- src/components/payment/PaymentModal.tsx | 20 ++-- src/global/initial.ts | 2 +- src/global/types.ts | 6 +- src/modules/actions/api/bots.ts | 2 +- src/modules/actions/api/chats.ts | 12 ++ src/modules/actions/apiUpdaters/chats.ts | 8 ++ src/modules/actions/apiUpdaters/initial.ts | 2 +- src/modules/actions/ui/misc.ts | 29 ++--- src/modules/helpers/payments.ts | 7 +- src/util/setupServiceWorker.ts | 2 +- 21 files changed, 232 insertions(+), 124 deletions(-) rename src/components/main/{Errors.async.tsx => Dialogs.async.tsx} (54%) rename src/components/main/{Errors.scss => Dialogs.scss} (58%) create mode 100644 src/components/main/Dialogs.tsx delete mode 100644 src/components/main/Errors.tsx diff --git a/src/api/gramjs/methods/chats.ts b/src/api/gramjs/methods/chats.ts index c5d8b46e1..cc69ec2ba 100644 --- a/src/api/gramjs/methods/chats.ts +++ b/src/api/gramjs/methods/chats.ts @@ -907,12 +907,15 @@ export async function openChatByInvite(hash: string) { let chat: ApiChat | undefined; if (result instanceof GramJs.ChatInvite) { - const updates = await invokeRequest(new GramJs.messages.ImportChatInvite({ hash }), true); - if (!(updates instanceof GramJs.Updates) || !updates.chats.length) { - return undefined; - } - - chat = buildApiChatFromPreview(updates.chats[0]); + onUpdate({ + '@type': 'showInvite', + data: { + title: result.title, + hash, + participantsCount: result.participantsCount, + isChannel: result.channel, + }, + }); } else { chat = buildApiChatFromPreview(result.chat); @@ -978,3 +981,14 @@ function updateLocalDb(result: ( }); } } + +export async function importChatInvite({ hash }: {hash: string}) { + const updates = await invokeRequest(new GramJs.messages.ImportChatInvite({ hash }), true); + if (!(updates instanceof GramJs.Updates) || !updates.chats.length) { + return undefined; + } + + const chat = buildApiChatFromPreview(updates.chats[0]); + + return chat; +} diff --git a/src/api/gramjs/methods/index.ts b/src/api/gramjs/methods/index.ts index b99e3d773..070018dae 100644 --- a/src/api/gramjs/methods/index.ts +++ b/src/api/gramjs/methods/index.ts @@ -14,7 +14,7 @@ export { fetchChatFolders, editChatFolder, deleteChatFolder, fetchRecommendedChatFolders, getChatByUsername, togglePreHistoryHidden, updateChatDefaultBannedRights, updateChatMemberBannedRights, updateChatTitle, updateChatAbout, toggleSignatures, updateChatAdmin, fetchGroupsForDiscussion, setDiscussionGroup, - migrateChat, openChatByInvite, fetchMembers, + migrateChat, openChatByInvite, fetchMembers, importChatInvite, } from './chats'; export { diff --git a/src/api/types/updates.ts b/src/api/types/updates.ts index 3af1d1fd0..182faeb08 100644 --- a/src/api/types/updates.ts +++ b/src/api/types/updates.ts @@ -74,6 +74,11 @@ export type ApiUpdateChatJoin = { id: number; }; +export type ApiUpdateShowInvite = { + '@type': 'showInvite'; + data: ApiInviteInfo; +}; + export type ApiUpdateChatLeave = { '@type': 'updateChatLeave'; id: number; @@ -311,6 +316,13 @@ export type ApiError = { textParams?: Record; }; +export type ApiInviteInfo = { + title: string; + hash: string; + isChannel?: boolean; + participantsCount?: number; +}; + export type ApiUpdateError = { '@type': 'error'; error: ApiError; @@ -395,7 +407,7 @@ export type ApiUpdate = ( ApiUpdateDeleteScheduledMessages | ApiUpdateResetMessages | ApiUpdateTwoFaError | updateTwoFaStateWaitCode | ApiUpdateNotifySettings | ApiUpdateNotifyExceptions | ApiUpdatePeerBlocked | ApiUpdatePrivacy | - ApiUpdateServerTimeOffset + ApiUpdateServerTimeOffset | ApiUpdateShowInvite ); export type OnApiUpdate = (update: ApiUpdate) => void; diff --git a/src/bundles/extra.ts b/src/bundles/extra.ts index 241614bd8..14517d712 100644 --- a/src/bundles/extra.ts +++ b/src/bundles/extra.ts @@ -1,7 +1,7 @@ export { default as MediaViewer } from '../components/mediaViewer/MediaViewer'; export { default as ForwardPicker } from '../components/main/ForwardPicker'; -export { default as Errors } from '../components/main/Errors'; +export { default as Dialogs } from '../components/main/Dialogs'; export { default as Notifications } from '../components/main/Notifications'; export { default as SafeLinkModal } from '../components/main/SafeLinkModal'; export { default as HistoryCalendar } from '../components/main/HistoryCalendar'; diff --git a/src/components/left/settings/folders/SettingsFoldersMain.tsx b/src/components/left/settings/folders/SettingsFoldersMain.tsx index fcb13d8aa..9917d39f4 100644 --- a/src/components/left/settings/folders/SettingsFoldersMain.tsx +++ b/src/components/left/settings/folders/SettingsFoldersMain.tsx @@ -35,7 +35,7 @@ type StateProps = { notifyExceptions?: Record; }; -type DispatchProps = Pick; +type DispatchProps = Pick; const runThrottledForLoadRecommended = throttle((cb) => cb(), 60000, true); @@ -53,7 +53,7 @@ const SettingsFoldersMain: FC = ({ notifyExceptions, loadRecommendedChatFolders, addChatFolder, - showError, + showDialog, }) => { const [animationData, setAnimationData] = useState>(); const [isAnimationLoaded, setIsAnimationLoaded] = useState(false); @@ -75,8 +75,8 @@ const SettingsFoldersMain: FC = ({ const handleCreateFolder = useCallback(() => { if (Object.keys(foldersById).length >= MAX_ALLOWED_FOLDERS) { - showError({ - error: { + showDialog({ + data: { message: 'DIALOG_FILTERS_TOO_MUCH', }, }); @@ -85,7 +85,7 @@ const SettingsFoldersMain: FC = ({ } onCreateFolder(); - }, [foldersById, showError, onCreateFolder]); + }, [foldersById, showDialog, onCreateFolder]); const lang = useLang(); @@ -111,8 +111,8 @@ const SettingsFoldersMain: FC = ({ const handleCreateFolderFromRecommended = useCallback((folder: ApiChatFolder) => { if (Object.keys(foldersById).length >= MAX_ALLOWED_FOLDERS) { - showError({ - error: { + showDialog({ + data: { message: 'DIALOG_FILTERS_TOO_MUCH', }, }); @@ -121,7 +121,7 @@ const SettingsFoldersMain: FC = ({ } addChatFolder({ folder }); - }, [foldersById, addChatFolder, showError]); + }, [foldersById, addChatFolder, showDialog]); return (
@@ -238,5 +238,5 @@ export default memo(withGlobal( notifyExceptions: selectNotifyExceptions(global), }; }, - (setGlobal, actions): DispatchProps => pick(actions, ['loadRecommendedChatFolders', 'addChatFolder', 'showError']), + (setGlobal, actions): DispatchProps => pick(actions, ['loadRecommendedChatFolders', 'addChatFolder', 'showDialog']), )(SettingsFoldersMain)); diff --git a/src/components/main/Errors.async.tsx b/src/components/main/Dialogs.async.tsx similarity index 54% rename from src/components/main/Errors.async.tsx rename to src/components/main/Dialogs.async.tsx index 119288978..0c214dda5 100644 --- a/src/components/main/Errors.async.tsx +++ b/src/components/main/Dialogs.async.tsx @@ -3,11 +3,11 @@ import { Bundles } from '../../util/moduleLoader'; import useModuleLoader from '../../hooks/useModuleLoader'; -const ErrorsAsync: FC = ({ isOpen }) => { - const Errors = useModuleLoader(Bundles.Extra, 'Errors', !isOpen); +const DialogsAsync: FC = ({ isOpen }) => { + const Dialogs = useModuleLoader(Bundles.Extra, 'Dialogs', !isOpen); // eslint-disable-next-line react/jsx-props-no-spreading - return Errors ? : undefined; + return Dialogs ? : undefined; }; -export default memo(ErrorsAsync); +export default memo(DialogsAsync); diff --git a/src/components/main/Errors.scss b/src/components/main/Dialogs.scss similarity index 58% rename from src/components/main/Errors.scss rename to src/components/main/Dialogs.scss index e763a8ec1..b88b78894 100644 --- a/src/components/main/Errors.scss +++ b/src/components/main/Dialogs.scss @@ -1,4 +1,4 @@ -#Errors { +#Dialogs { position: fixed; top: 0; left: 0; @@ -6,3 +6,11 @@ height: 100vh; z-index: var(--z-modal); } + +.buttons { + display: flex; + + button { + flex: 1; + } +} diff --git a/src/components/main/Dialogs.tsx b/src/components/main/Dialogs.tsx new file mode 100644 index 000000000..8fc0ac61b --- /dev/null +++ b/src/components/main/Dialogs.tsx @@ -0,0 +1,103 @@ +import React, { FC, memo } from '../../lib/teact/teact'; +import { withGlobal } from '../../lib/teact/teactn'; + +import { GlobalActions } from '../../global/types'; +import { ApiError, ApiInviteInfo } from '../../api/types'; + +import getReadableErrorText from '../../util/getReadableErrorText'; +import { pick } from '../../util/iteratees'; +import useLang from '../../hooks/useLang'; + +import Modal from '../ui/Modal'; +import Button from '../ui/Button'; + +import './Dialogs.scss'; + +type StateProps = { + dialogs: (ApiError | ApiInviteInfo)[]; +}; + +type DispatchProps = Pick; + +const Dialogs: FC = ({ dialogs, dismissDialog, acceptInviteConfirmation }) => { + const lang = useLang(); + + if (!dialogs.length) { + return undefined; + } + + const renderInvite = (invite: ApiInviteInfo) => { + const { + hash, title, participantsCount, isChannel, + } = invite; + + const handleJoinClick = () => { + acceptInviteConfirmation({ + hash, + }); + dismissDialog(); + }; + + const participantsText = isChannel + ? lang('Subscribers', participantsCount, 'i') + : lang('Members', participantsCount, 'i'); + + const joinText = isChannel ? lang('ChannelJoin') : lang('JoinGroup'); + + return ( + + {participantsCount !== undefined &&

{participantsText}

} + + +
+ ); + }; + + const renderError = (error: ApiError) => { + return ( + +

{getReadableErrorText(error)}

+
+ +
+
+ ); + }; + + const renderDialog = (dialog: ApiError | ApiInviteInfo) => { + if ('hash' in dialog) { + return renderInvite(dialog); + } + + return renderError(dialog); + }; + + return ( +
+ {dialogs.map(renderDialog)} +
+ ); +}; + +function getErrorHeader(error: ApiError) { + if (error.isSlowMode) { + return 'Slowmode enabled'; + } + + return 'Something went wrong'; +} + +export default memo(withGlobal( + (global): StateProps => pick(global, ['dialogs']), + (setGlobal, actions): DispatchProps => pick(actions, ['dismissDialog', 'acceptInviteConfirmation']), +)(Dialogs)); diff --git a/src/components/main/Errors.tsx b/src/components/main/Errors.tsx deleted file mode 100644 index 76953beb3..000000000 --- a/src/components/main/Errors.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, { FC, memo } from '../../lib/teact/teact'; -import { withGlobal } from '../../lib/teact/teactn'; - -import { GlobalActions } from '../../global/types'; -import { ApiError } from '../../api/types'; - -import getReadableErrorText from '../../util/getReadableErrorText'; -import { pick } from '../../util/iteratees'; -import useLang from '../../hooks/useLang'; - -import Modal from '../ui/Modal'; -import Button from '../ui/Button'; - -import './Errors.scss'; - -type StateProps = { - errors: ApiError[]; -}; - -type DispatchProps = Pick; - -const Errors: FC = ({ errors, dismissError }) => { - const lang = useLang(); - - if (!errors.length) { - return undefined; - } - - return ( -
- {errors.map((error) => ( - -

{getReadableErrorText(error)}

- -
- ))} -
- ); -}; - -function getErrorHeader(error: ApiError) { - if (error.isSlowMode) { - return 'Slowmode enabled'; - } - - return 'Something went wrong'; -} - -export default memo(withGlobal( - (global): StateProps => pick(global, ['errors']), - (setGlobal, actions): DispatchProps => pick(actions, ['dismissError']), -)(Errors)); diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index ea2d15334..5e8c70041 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -30,7 +30,7 @@ import RightColumn from '../right/RightColumn'; import MediaViewer from '../mediaViewer/MediaViewer.async'; import AudioPlayer from '../middle/AudioPlayer'; import Notifications from './Notifications.async'; -import Errors from './Errors.async'; +import Dialogs from './Dialogs.async'; import ForwardPicker from './ForwardPicker.async'; import SafeLinkModal from './SafeLinkModal.async'; import HistoryCalendar from './HistoryCalendar.async'; @@ -45,7 +45,7 @@ type StateProps = { isMediaViewerOpen: boolean; isForwardModalOpen: boolean; hasNotifications: boolean; - hasErrors: boolean; + hasDialogs: boolean; audioMessage?: ApiMessage; safeLinkModalUrl?: string; isHistoryCalendarOpen: boolean; @@ -71,7 +71,7 @@ const Main: FC = ({ isForwardModalOpen, animationLevel, hasNotifications, - hasErrors, + hasDialogs, audioMessage, safeLinkModalUrl, isHistoryCalendarOpen, @@ -192,7 +192,7 @@ const Main: FC = ({ - + {audioMessage && } @@ -228,7 +228,7 @@ export default memo(withGlobal( isMediaViewerOpen: selectIsMediaViewerOpen(global), isForwardModalOpen: selectIsForwardModalOpen(global), hasNotifications: Boolean(global.notifications.length), - hasErrors: Boolean(global.errors.length), + hasDialogs: Boolean(global.dialogs.length), audioMessage, safeLinkModalUrl: global.safeLinkModalUrl, isHistoryCalendarOpen: Boolean(global.historyCalendarSelectedAt), diff --git a/src/components/middle/composer/Composer.tsx b/src/components/middle/composer/Composer.tsx index 89f055673..10c1c7437 100644 --- a/src/components/middle/composer/Composer.tsx +++ b/src/components/middle/composer/Composer.tsx @@ -128,7 +128,7 @@ type StateProps = { type DispatchProps = Pick; @@ -189,7 +189,7 @@ const Composer: FC = ({ editMessage, saveDraft, clearDraft, - showError, + showDialog, setStickerSearchQuery, setGifSearchQuery, forwardMessages, @@ -428,8 +428,8 @@ const Composer: FC = ({ if (currentAttachments.length && text && text.length > CAPTION_MAX_LENGTH) { const extraLength = text.length - CAPTION_MAX_LENGTH; - showError({ - error: { + showDialog({ + data: { message: 'CAPTION_TOO_LONG_PLEASE_REMOVE_CHARACTERS', textParams: { '{EXTRA_CHARS_COUNT}': extraLength, @@ -454,8 +454,8 @@ const Composer: FC = ({ const secondsRemaining = nextSendDateNotReached ? slowMode.nextSendDate! - nowSeconds : slowMode.seconds - secondsSinceLastMessage!; - showError({ - error: { + showDialog({ + data: { message: `A wait of ${secondsRemaining} seconds is required before sending another message in this chat`, isSlowMode: true, }, @@ -488,7 +488,7 @@ const Composer: FC = ({ requestAnimationFrame(resetComposer); }, [ connectionState, attachments, activeVoiceRecording, isForwarding, serverTimeOffset, clearDraft, chatId, - resetComposer, stopRecordingVoice, showError, slowMode, isAdmin, sendMessage, forwardMessages, + resetComposer, stopRecordingVoice, showDialog, slowMode, isAdmin, sendMessage, forwardMessages, ]); const handleStickerSelect = useCallback((sticker: ApiSticker) => { @@ -972,7 +972,7 @@ export default memo(withGlobal( 'editMessage', 'saveDraft', 'clearDraft', - 'showError', + 'showDialog', 'setStickerSearchQuery', 'setGifSearchQuery', 'forwardMessages', diff --git a/src/components/payment/PaymentModal.tsx b/src/components/payment/PaymentModal.tsx index 6ba00a46a..5f66ca86f 100644 --- a/src/components/payment/PaymentModal.tsx +++ b/src/components/payment/PaymentModal.tsx @@ -5,12 +5,12 @@ import { withGlobal } from '../../lib/teact/teactn'; import { GlobalActions, GlobalState } from '../../global/types'; import { PaymentStep, ShippingOption, Price } from '../../types'; -import { ApiError } from '../../api/types'; +import { ApiError, ApiInviteInfo } from '../../api/types'; import { pick } from '../../util/iteratees'; import { getCurrencySign } from '../middle/helpers/getCurrencySign'; import { detectCardTypeText } from '../common/helpers/detectCardType'; -import { getShippingError } from '../../modules/helpers/payments'; +import { getShippingErrors } from '../../modules/helpers/payments'; import usePaymentReducer, { FormState } from '../../hooks/reducers/usePaymentReducer'; import useLang from '../../hooks/useLang'; @@ -46,7 +46,7 @@ type StateProps = { needCardholderName?: boolean; needCountry?: boolean; needZip?: boolean; - globalErrors?: ApiError[]; + globalDialogs?: (ApiError | ApiInviteInfo)[]; }; type GlobalStateProps = Pick = ({ needCountry, needZip, error, - globalErrors, + globalDialogs, validateRequestedInfo, sendPaymentForm, setPaymentStep, @@ -92,10 +92,10 @@ const Invoice: FC = ({ const lang = useLang(); useEffect(() => { - if (step || error || globalErrors) { + if (step || error || globalDialogs) { setIsLoading(false); } - }, [step, error, globalErrors]); + }, [step, error, globalDialogs]); useEffect(() => { if (error && error.field) { @@ -107,8 +107,8 @@ const Invoice: FC = ({ }); return; } - if (globalErrors && globalErrors.length) { - const errors = getShippingError(globalErrors); + if (globalDialogs && globalDialogs.length) { + const errors = getShippingErrors(globalDialogs); paymentDispatch({ type: 'setFormErrors', payload: { @@ -116,7 +116,7 @@ const Invoice: FC = ({ }, }); } - }, [error, globalErrors, paymentDispatch]); + }, [error, globalDialogs, paymentDispatch]); useEffect(() => { if (savedInfo) { @@ -412,7 +412,7 @@ export default memo(withGlobal( needCountry, needZip, error, - globalErrors: global.errors, + globalDialogs: global.dialogs, }; }, (setGlobal, actions): DispatchProps => { diff --git a/src/global/initial.ts b/src/global/initial.ts index 8fbdc6269..c570c338c 100644 --- a/src/global/initial.ts +++ b/src/global/initial.ts @@ -99,7 +99,7 @@ export const INITIAL_STATE: GlobalState = { notifications: [], - errors: [], + dialogs: [], activeSessions: [], diff --git a/src/global/types.ts b/src/global/types.ts index 6f7368344..30c9d369f 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -18,6 +18,7 @@ import { ApiPaymentSavedInfo, ApiSession, ApiNewPoll, + ApiInviteInfo, } from '../api/types'; import { FocusDirection, @@ -364,7 +365,7 @@ export type GlobalState = { }; notifications: ApiNotification[]; - errors: ApiError[]; + dialogs: (ApiError | ApiInviteInfo)[]; // TODO Move to settings activeSessions: ApiSession[]; @@ -399,7 +400,7 @@ export type GlobalState = { export type ActionTypes = ( // system 'init' | 'reset' | 'disconnect' | 'initApi' | 'apiUpdate' | 'sync' | 'saveSession' | 'afterSync' | - 'showNotification' | 'dismissNotification' | 'showError' | 'dismissError' | + 'showNotification' | 'dismissNotification' | 'showDialog' | 'dismissDialog' | // ui 'toggleChatInfo' | 'setIsUiReady' | 'addRecentEmoji' | 'addRecentSticker' | 'toggleLeftColumn' | 'toggleSafeLinkModal' | 'openHistoryCalendar' | 'closeHistoryCalendar' | 'disableContextMenuHint' | @@ -438,6 +439,7 @@ export type ActionTypes = ( 'toggleManagement' | 'closeManagement' | 'checkPublicLink' | 'updatePublicLink' | 'updatePrivateLink' | // groups 'togglePreHistoryHidden' | 'updateChatDefaultBannedRights' | 'updateChatMemberBannedRights' | 'updateChatAdmin' | + 'acceptInviteConfirmation' | // users 'loadFullUser' | 'openUserInfo' | 'loadNearestCountry' | 'loadTopUsers' | 'loadContactList' | 'loadCurrentUser' | 'updateProfile' | 'checkUsername' | 'updateContact' | 'deleteUser' | 'loadUser' | diff --git a/src/modules/actions/api/bots.ts b/src/modules/actions/api/bots.ts index 436fda331..0c4cc51ba 100644 --- a/src/modules/actions/api/bots.ts +++ b/src/modules/actions/api/bots.ts @@ -84,7 +84,7 @@ async function answerCallbackButton(chat: ApiChat, messageId: number, data: stri const { message, alert: isError } = result; if (isError) { - getDispatch().showError({ error: { message } }); + getDispatch().showDialog({ data: { message } }); } else { getDispatch().showNotification({ message }); } diff --git a/src/modules/actions/api/chats.ts b/src/modules/actions/api/chats.ts index 57b035662..1952b4267 100644 --- a/src/modules/actions/api/chats.ts +++ b/src/modules/actions/api/chats.ts @@ -403,6 +403,18 @@ addReducer('openTelegramLink', (global, actions, payload) => { } }); +addReducer('acceptInviteConfirmation', (global, actions, payload) => { + const { hash } = payload!; + (async () => { + const result = await callApi('importChatInvite', { hash }); + if (!result) { + return; + } + + actions.openChat({ id: result.id }); + })(); +}); + addReducer('openChatByUsername', (global, actions, payload) => { const { username } = payload!; diff --git a/src/modules/actions/apiUpdaters/chats.ts b/src/modules/actions/apiUpdaters/chats.ts index a1d6dde4c..3d099ef9b 100644 --- a/src/modules/actions/apiUpdaters/chats.ts +++ b/src/modules/actions/apiUpdaters/chats.ts @@ -373,6 +373,14 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => { setGlobal(global); } + break; + } + + case 'showInvite': { + const { data } = update; + + actions.showDialog({ data }); + break; } } }); diff --git a/src/modules/actions/apiUpdaters/initial.ts b/src/modules/actions/apiUpdaters/initial.ts index c85fea5fc..2675c6135 100644 --- a/src/modules/actions/apiUpdaters/initial.ts +++ b/src/modules/actions/apiUpdaters/initial.ts @@ -59,7 +59,7 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => { actions.signOut(); } - actions.showError({ error: update.error }); + actions.showDialog({ data: update.error }); break; } diff --git a/src/modules/actions/ui/misc.ts b/src/modules/actions/ui/misc.ts index f0d261393..fa7b960e4 100644 --- a/src/modules/actions/ui/misc.ts +++ b/src/modules/actions/ui/misc.ts @@ -5,6 +5,7 @@ import { GlobalState } from '../../../global/types'; import { IS_SINGLE_COLUMN_LAYOUT, IS_TABLET_COLUMN_LAYOUT } from '../../../util/environment'; import getReadableErrorText from '../../../util/getReadableErrorText'; import { selectCurrentMessageList } from '../../selectors'; +import { ApiError } from '../../../api/types'; const MAX_STORED_EMOJIS = 18; // Represents two rows of recent emojis @@ -158,36 +159,38 @@ addReducer('dismissNotification', (global) => { }; }); -addReducer('showError', (global, actions, payload) => { - const { error } = payload!; +addReducer('showDialog', (global, actions, payload) => { + const { data } = payload!; // Filter out errors that we don't want to show to the user - if (!getReadableErrorText(error)) { + if ('message' in data && !getReadableErrorText(data)) { return global; } - const newErrors = [...global.errors]; - const existingErrorIndex = newErrors.findIndex((err) => err.message === error.message); - if (existingErrorIndex !== -1) { - newErrors.splice(existingErrorIndex, 1); + const newDialogs = [...global.dialogs]; + if ('message' in data) { + const existingErrorIndex = newDialogs.findIndex((err) => (err as ApiError).message === data.message); + if (existingErrorIndex !== -1) { + newDialogs.splice(existingErrorIndex, 1); + } } - newErrors.push(error); + newDialogs.push(data); return { ...global, - errors: newErrors, + dialogs: newDialogs, }; }); -addReducer('dismissError', (global) => { - const newErrors = [...global.errors]; +addReducer('dismissDialog', (global) => { + const newDialogs = [...global.dialogs]; - newErrors.pop(); + newDialogs.pop(); return { ...global, - errors: newErrors, + dialogs: newDialogs, }; }); diff --git a/src/modules/helpers/payments.ts b/src/modules/helpers/payments.ts index a6c60ce6f..16a0652e3 100644 --- a/src/modules/helpers/payments.ts +++ b/src/modules/helpers/payments.ts @@ -1,3 +1,5 @@ +import { ApiError, ApiInviteInfo } from '../../api/types'; + const STRIPE_ERRORS: Record> = { missing_payment_information: { field: 'cardNumber', @@ -91,8 +93,9 @@ const SHIPPING_ERRORS: Record> = { }; -export function getShippingError(errors: Record) { - return Object.values(errors).reduce((acc, cur) => { +export function getShippingErrors(dialogs: (ApiError | ApiInviteInfo)[]) { + return Object.values(dialogs).reduce((acc, cur) => { + if (!('message' in cur)) return acc; const error = SHIPPING_ERRORS[cur.message]; if (error) { acc = { diff --git a/src/util/setupServiceWorker.ts b/src/util/setupServiceWorker.ts index afe1b0073..e79d56659 100644 --- a/src/util/setupServiceWorker.ts +++ b/src/util/setupServiceWorker.ts @@ -52,7 +52,7 @@ if (IS_SERVICE_WORKER_SUPPORTED) { // eslint-disable-next-line no-console console.error('[SW] ServiceWorker not available'); } - getDispatch().showError({ error: { message: 'SERVICE_WORKER_DISABLED' } }); + getDispatch().showDialog({ data: { message: 'SERVICE_WORKER_DISABLED' } }); } } catch (err) { if (DEBUG) {