Update selected item color in Pass menu
[ProtonMail-WebClient.git] / packages / pass / store / sagas / items / item-edit.saga.ts
blobf2ce4b58181c7cc264d1a71e4af3d3ee570992a2
1 import { call, put, select, takeEvery } from 'redux-saga/effects';
3 import { api } from '@proton/pass/lib/api/api';
4 import { parseItemRevision } from '@proton/pass/lib/items/item.parser';
5 import { editItem } from '@proton/pass/lib/items/item.requests';
6 import { createTelemetryEvent } from '@proton/pass/lib/telemetry/event';
7 import { aliasDetailsSync, itemEditFailure, itemEditIntent, itemEditSuccess } from '@proton/pass/store/actions';
8 import type { AliasState } from '@proton/pass/store/reducers';
9 import { selectAliasDetails, selectAliasOptions, selectItem } from '@proton/pass/store/selectors';
10 import type { RootSagaOptions } from '@proton/pass/store/types';
11 import type { ItemEditIntent, ItemRevision, ItemRevisionContentsResponse } from '@proton/pass/types';
12 import { TelemetryEventName, TelemetryItemType } from '@proton/pass/types/data/telemetry';
13 import { deobfuscate } from '@proton/pass/utils/obfuscate/xor';
14 import { isEqual } from '@proton/pass/utils/set/is-equal';
16 function* editMailboxesWorker(aliasEditIntent: ItemEditIntent<'alias'>) {
17     if (!aliasEditIntent.extraData) return;
19     const { itemId, shareId } = aliasEditIntent;
21     const item: ItemRevision<'alias'> = yield select(selectItem(shareId, itemId));
22     const mailboxesForAlias: string[] = yield select(selectAliasDetails(item.aliasEmail!));
23     const aliasOptions: AliasState['aliasOptions'] = yield select(selectAliasOptions);
25     const currentMailboxIds = new Set(
26         mailboxesForAlias
27             .map((mailbox) => aliasOptions?.mailboxes.find(({ email }) => email === mailbox)?.id)
28             .filter(Boolean) as number[]
29     );
31     const nextMailboxIds = new Set(aliasEditIntent.extraData.mailboxes.map(({ id }) => id));
33     /* only update the mailboxes if there is a change */
34     if (!isEqual(currentMailboxIds, nextMailboxIds)) {
35         yield api({
36             url: `pass/v1/share/${shareId}/alias/${itemId}/mailbox`,
37             method: 'post',
38             data: {
39                 MailboxIDs: Array.from(nextMailboxIds.values()),
40             },
41         });
43         yield put(
44             aliasDetailsSync({
45                 aliasEmail: item.aliasEmail!,
46                 mailboxes: aliasEditIntent.extraData.mailboxes,
47             })
48         );
49     }
51 function* itemEditWorker(
52     { onItemsUpdated, getTelemetry }: RootSagaOptions,
53     { payload: editIntent, meta: { callback: onItemEditIntentProcessed } }: ReturnType<typeof itemEditIntent>
54 ) {
55     const { itemId, shareId, lastRevision } = editIntent;
56     const telemetry = getTelemetry();
58     try {
59         if (editIntent.type === 'alias' && editIntent.extraData?.aliasOwner) {
60             yield call(editMailboxesWorker, editIntent);
61         }
63         const encryptedItem: ItemRevisionContentsResponse = yield editItem(editIntent, lastRevision);
64         const item: ItemRevision = yield parseItemRevision(shareId, encryptedItem);
66         const itemEditSuccessAction = itemEditSuccess({ item, itemId, shareId });
67         yield put(itemEditSuccessAction);
69         void telemetry?.push(
70             createTelemetryEvent(TelemetryEventName.ItemUpdate, {}, { type: TelemetryItemType[item.data.type] })
71         );
73         if (item.data.type === 'login' && editIntent.type === 'login') {
74             const prevTotp = deobfuscate(editIntent.content.totpUri);
75             const nextTotp = deobfuscate(item.data.content.totpUri);
77             if (nextTotp && prevTotp !== nextTotp) {
78                 void telemetry?.push(createTelemetryEvent(TelemetryEventName.TwoFAUpdate, {}, {}));
79             }
80         }
82         onItemEditIntentProcessed?.(itemEditSuccessAction);
83         onItemsUpdated?.();
84     } catch (e) {
85         const itemEditFailureAction = itemEditFailure({ itemId, shareId }, e);
86         yield put(itemEditFailureAction);
88         onItemEditIntentProcessed?.(itemEditFailureAction);
89     }
92 export default function* watcher(options: RootSagaOptions) {
93     yield takeEvery(itemEditIntent.match, itemEditWorker, options);