Update selected item color in Pass menu
[ProtonMail-WebClient.git] / packages / pass / lib / bridge / PassBridgeFactory.ts
blobe32e05c6b9c010dcf6b04475e74c2d6096644fd5
1 import { getAliasOptions } from '@proton/pass/lib/alias/alias.requests';
2 import { exposeApi } from '@proton/pass/lib/api/api';
3 import { exposePassCrypto } from '@proton/pass/lib/crypto';
4 import { createPassCrypto } from '@proton/pass/lib/crypto/pass-crypto';
5 import { parseItemRevision } from '@proton/pass/lib/items/item.parser';
6 import { createAlias, requestAllItemsForShareId } from '@proton/pass/lib/items/item.requests';
7 import { getOrganizationSettings, setOrganizationSettings } from '@proton/pass/lib/organization/organization.requests';
8 import { parseShareResponse } from '@proton/pass/lib/shares/share.parser';
9 import { requestShares } from '@proton/pass/lib/shares/share.requests';
10 import { getUserAccess } from '@proton/pass/lib/user/user.requests';
11 import { isActiveVault, isOwnVault, isWritableVault } from '@proton/pass/lib/vaults/vault.predicates';
12 import { createVault } from '@proton/pass/lib/vaults/vault.requests';
13 import type { ItemRevision, Api as PassApi } from '@proton/pass/types';
14 import { first } from '@proton/pass/utils/array/first';
15 import { prop } from '@proton/pass/utils/fp/lens';
16 import { maxAgeMemoize } from '@proton/pass/utils/fp/memo';
17 import { pipe } from '@proton/pass/utils/fp/pipe';
18 import { and, truthy } from '@proton/pass/utils/fp/predicates';
19 import { sortOn } from '@proton/pass/utils/fp/sort';
20 import { waitUntil } from '@proton/pass/utils/fp/wait-until';
21 import { obfuscate } from '@proton/pass/utils/obfuscate/xor';
22 import { uniqueId } from '@proton/pass/utils/string/unique-id';
23 import { getEpoch } from '@proton/pass/utils/time/epoch';
24 import type { Api } from '@proton/shared/lib/interfaces';
25 import unary from '@proton/utils/unary';
27 import type { PassBridge, PassBridgeAliasItem } from './types';
29 let passBridgeInstance: PassBridge | undefined;
31 export const createPassBridge = (api: Api): PassBridge => {
32     return (
33         passBridgeInstance ||
34         (() => {
35             exposeApi(api as PassApi);
36             const PassCrypto = exposePassCrypto(createPassCrypto());
38             passBridgeInstance = {
39                 async init({ user, addresses, authStore }) {
40                     await PassCrypto.hydrate({ user, addresses, keyPassword: authStore.getPassword(), clear: false });
41                     const isReady = await waitUntil(() => PassCrypto.ready, 250).then(() => true);
43                     return isReady;
44                 },
45                 user: {
46                     getUserAccess: maxAgeMemoize(async () => {
47                         const result = await getUserAccess();
48                         return result;
49                     }),
50                 },
51                 vault: {
52                     getDefault: maxAgeMemoize(async () => {
53                         const encryptedShares = await requestShares();
54                         const shares = (await Promise.all(encryptedShares.map(unary(parseShareResponse)))).filter(
55                             truthy
56                         );
58                         const candidates = shares
59                             .filter(and(isActiveVault, isWritableVault, isOwnVault))
60                             .sort(sortOn('createTime', 'ASC'));
62                         return first(candidates);
63                     }),
64                     async createDefaultVault() {
65                         // In case a default vault has been created in the meantime
66                         const defaultVault = await this.getDefault({ maxAge: 0 });
67                         if (defaultVault) {
68                             return defaultVault;
69                         }
71                         const newVault = await createVault({
72                             content: {
73                                 name: 'Personal',
74                                 description: 'Personal vault (created from Mail)',
75                                 display: {},
76                             },
77                         });
78                         return newVault;
79                     },
80                 },
81                 alias: {
82                     async create({ shareId, name, note, alias: { aliasEmail, mailbox, prefix, signedSuffix } }) {
83                         const itemUuid = uniqueId();
85                         const encryptedItem = await createAlias({
86                             content: {},
87                             createTime: getEpoch(),
88                             extraData: { aliasEmail, mailboxes: [mailbox], prefix, signedSuffix },
89                             extraFields: [],
90                             metadata: { itemUuid, name, note: obfuscate(note ?? '') },
91                             optimisticId: itemUuid,
92                             shareId,
93                             type: 'alias',
94                         });
96                         const item = (await parseItemRevision(shareId, encryptedItem)) as ItemRevision<'alias'>;
98                         return {
99                             item: { ...item, aliasEmail },
100                         };
101                     },
102                     getAliasOptions,
103                     getAllByShareId: maxAgeMemoize(async (shareId) => {
104                         const aliases = (await Promise.all(
105                             (await requestAllItemsForShareId({ shareId, OnlyAlias: true }))
106                                 .filter(pipe(prop('AliasEmail'), truthy))
107                                 .map((item) => parseItemRevision(shareId, item))
108                         )) as ItemRevision<'alias'>[];
110                         return aliases.map((item): PassBridgeAliasItem => ({ item }));
111                     }),
112                 },
113                 organization: {
114                     settings: {
115                         get: getOrganizationSettings,
116                         set: (key, value) => setOrganizationSettings({ [key]: value }),
117                     },
118                 },
119             };
121             return passBridgeInstance;
122         })()
123     );