1 import { createAction } from '@reduxjs/toolkit';
2 import { c } from 'ttag';
4 import { getItemActionId } from '@proton/pass/lib/items/item.utils';
5 import { withCache, withThrottledCache } from '@proton/pass/store/actions/enhancers/cache';
6 import { type ActionCallback, withCallback } from '@proton/pass/store/actions/enhancers/callback';
7 import { withSynchronousAction } from '@proton/pass/store/actions/enhancers/client';
8 import { withNotification } from '@proton/pass/store/actions/enhancers/notification';
13 itemsBulkDeleteRequest,
15 itemsBulkRestoreRequest,
16 itemsBulkTrashRequest,
17 secureLinkCreateRequest,
18 secureLinkOpenRequest,
19 secureLinkRemoveRequest,
20 secureLinksGetRequest,
21 secureLinksRemoveInactiveRequest,
22 } from '@proton/pass/store/actions/requests';
23 import { createOptimisticAction } from '@proton/pass/store/optimistic/action/create-optimistic-action';
24 import type { Draft, DraftBase } from '@proton/pass/store/reducers';
30 } from '@proton/pass/store/request/enhancers';
31 import { requestActionsFactory } from '@proton/pass/store/request/flow';
42 SecureLinkCreationDTO,
48 } from '@proton/pass/types';
49 import { getErrorMessage } from '@proton/pass/utils/errors/get-error-message';
50 import { pipe } from '@proton/pass/utils/fp/pipe';
51 import { UNIX_MINUTE } from '@proton/pass/utils/time/constants';
53 export const draftSave = createAction('draft::save', (payload: Draft) => withThrottledCache({ payload }));
54 export const draftDiscard = createAction('draft::discard', (payload: DraftBase) => withThrottledCache({ payload }));
55 export const draftsGarbageCollect = createAction('drafts::gc');
57 export const itemCreationIntent = createOptimisticAction(
58 'item::creation::intent',
60 payload: ItemCreateIntent,
61 callback?: ActionCallback<ReturnType<typeof itemCreationSuccess> | ReturnType<typeof itemCreationFailure>>
62 ) => pipe(withSynchronousAction, withCallback(callback))({ payload }),
63 ({ payload }) => getItemActionId(payload)
66 export const itemCreationFailure = createOptimisticAction(
67 'item::creation::failure',
68 (payload: { optimisticId: string; shareId: string }, error: unknown) =>
71 text: c('Error').t`Item creation failed`,
73 })({ payload, error }),
74 ({ payload }) => getItemActionId(payload)
77 export const itemCreationDismiss = createOptimisticAction(
78 'item::creation::dismiss',
79 (payload: { optimisticId: string; shareId: string; item: ItemRevision }) =>
82 text: c('Info').t`"${payload.item.data.metadata.name}" item was dismissed`,
84 ({ payload }) => getItemActionId(payload)
87 export const itemCreationSuccess = createOptimisticAction(
88 'item::creation::success',
89 (payload: { optimisticId: string; shareId: string; item: ItemRevision; alias?: ItemRevision }) =>
94 text: c('Info').t`Item "${payload.item.data.metadata.name}" created`,
97 ({ payload }) => getItemActionId(payload)
100 export const itemEditIntent = createOptimisticAction(
101 'item::edit::intent',
103 payload: ItemEditIntent,
104 callback?: ActionCallback<ReturnType<typeof itemEditSuccess> | ReturnType<typeof itemEditFailure>>
105 ) => pipe(withSynchronousAction, withCallback(callback))({ payload }),
106 ({ payload }) => getItemActionId(payload)
109 export const itemEditFailure = createOptimisticAction(
110 'item::edit::failure',
111 (payload: SelectedItem, error: unknown) =>
114 text: c('Error').t`Editing item failed`,
116 })({ payload, error }),
117 ({ payload }) => getItemActionId(payload)
120 export const itemEditDismiss = createOptimisticAction(
121 'item::edit::dismiss',
122 (payload: { itemId: string; shareId: string; item: ItemRevision }) =>
125 text: c('Info').t`"${payload.item.data.metadata.name}" update was dismissed`,
127 ({ payload }) => getItemActionId(payload)
130 export const itemEditSuccess = createOptimisticAction(
131 'item::edit::success',
132 (payload: { item: ItemRevision } & SelectedItem) =>
137 text: c('Info').t`Item "${payload.item.data.metadata.name}" updated`,
140 ({ payload }) => getItemActionId(payload)
143 export const itemsEditSync = createAction('items::edit::sync', (items: ItemRevision[]) =>
144 withCache({ payload: { items } })
147 export const itemMoveIntent = createOptimisticAction(
148 'item::move::intent',
149 (payload: { item: ItemRevision; shareId: string; optimisticId: string }) => withSynchronousAction({ payload }),
150 ({ payload }) => getItemActionId(payload)
153 export const itemMoveFailure = createOptimisticAction(
154 'item::move::failure',
155 (payload: { optimisticId: string; shareId: string; item: ItemRevision }, error: unknown) =>
158 text: c('Error').t`Moving item failed`,
160 })({ payload, error }),
161 ({ payload }) => getItemActionId(payload)
164 export const itemMoveSuccess = createOptimisticAction(
165 'item::move::success',
166 (payload: { item: ItemRevision; optimisticId: string; shareId: string }) =>
171 text: c('Info').t`Item successfully moved`,
174 ({ payload }) => getItemActionId(payload)
177 export const itemBulkMoveIntent = createAction(
178 'item::bulk::move::intent',
179 (payload: { selected: BulkSelectionDTO; shareId: string }) =>
181 withRequest({ status: 'start', id: itemsBulkMoveRequest() }),
186 text: c('Info').t`Moving items`,
191 export const itemBulkMoveFailure = createAction(
192 'item::bulk::move::failure',
193 withRequestFailure((payload: {}, error: unknown) =>
196 text: c('Error').t`Failed to move items`,
198 })({ payload, error })
202 export const itemBulkMoveProgress = createAction(
203 'item::bulk::move::progress',
204 withRequestProgress((payload: BatchItemRevisions & { movedItems: ItemRevision[]; destinationShareId: string }) =>
205 withCache({ payload })
209 export const itemBulkMoveSuccess = createAction(
210 'item::bulk::move::success',
211 withRequestSuccess((payload: {}) =>
214 text: c('Info').t`All items successfully moved`,
219 export const itemTrashIntent = createOptimisticAction(
220 'item::trash::intent',
222 payload: { item: ItemRevision } & SelectedItem,
223 callback?: ActionCallback<ReturnType<typeof itemTrashSuccess> | ReturnType<typeof itemTrashFailure>>
224 ) => withCallback(callback)({ payload }),
225 ({ payload }) => getItemActionId(payload)
228 export const itemTrashFailure = createOptimisticAction(
229 'item::trash::failure',
230 (payload: SelectedItem, error: unknown) =>
233 text: c('Error').t`Trashing item failed`,
235 })({ payload, error }),
236 ({ payload }) => getItemActionId(payload)
239 export const itemTrashSuccess = createOptimisticAction(
240 'item::trash::success',
241 (payload: SelectedItem) =>
246 key: getItemActionId(payload),
247 text: c('Info').t`Item moved to trash`,
250 ({ payload }) => getItemActionId(payload)
253 export const itemBulkTrashIntent = createAction(
254 'item::bulk::trash::intent',
255 (payload: { selected: BulkSelectionDTO }) =>
257 withRequest({ status: 'start', id: itemsBulkTrashRequest() }),
262 text: c('Info').t`Moving items to trash`,
267 export const itemBulkTrashFailure = createAction(
268 'item::bulk::trash::failure',
269 withRequestFailure((payload: {}, error: unknown) =>
272 text: c('Error').t`Failed to move items to trash`,
274 })({ payload, error })
278 export const itemBulkTrashProgress = createAction(
279 'item::bulk::trash::progress',
280 withRequestProgress((payload: BatchItemRevisionIDs) => withCache({ payload }))
283 export const itemBulkTrashSuccess = createAction(
284 'item::bulk::trash::success',
285 withRequestSuccess((payload: {}) =>
288 text: c('Info').t`Selected items successfully moved to trash`,
293 export const itemDeleteIntent = createOptimisticAction(
294 'item::delete::intent',
296 payload: { item: ItemRevision } & SelectedItem,
297 callback?: ActionCallback<ReturnType<typeof itemDeleteSuccess> | ReturnType<typeof itemDeleteFailure>>
298 ) => withCallback(callback)({ payload }),
299 ({ payload }) => getItemActionId(payload)
302 export const itemDeleteFailure = createOptimisticAction(
303 'item::delete::failure',
304 (payload: SelectedItem, error: unknown) =>
307 text: c('Error').t`Deleting item failed`,
309 })({ payload, error }),
310 ({ payload }) => getItemActionId(payload)
313 export const itemDeleteSuccess = createOptimisticAction(
314 'item::delete::success',
315 (payload: SelectedItem) =>
320 text: c('Info').t`Item permanently deleted`,
323 ({ payload }) => getItemActionId(payload)
326 export const itemBulkDeleteIntent = createAction(
327 'item::bulk::delete::intent',
328 (payload: { selected: BulkSelectionDTO }) =>
330 withRequest({ status: 'start', id: itemsBulkDeleteRequest() }),
335 text: c('Info').t`Permanently deleting selected items`,
340 export const itemBulkDeleteFailure = createAction(
341 'item::bulk::delete::failure',
342 withRequestFailure((payload: {}, error: unknown) =>
345 text: c('Error').t`Failed to delete selected items`,
347 })({ payload, error })
351 export const itemBulkDeleteProgress = createAction(
352 'item::bulk::delete::progress',
353 withRequestProgress((payload: BatchItemRevisionIDs) => withCache({ payload }))
356 export const itemBulkDeleteSuccess = createAction(
357 'item::bulk::delete::success',
358 withRequestSuccess((payload: {}) =>
361 text: c('Info').t`Selected items permanently deleted`,
366 export const itemsDeleteSync = createAction('items::delete::sync', (shareId: string, itemIds: string[]) =>
367 withCache({ payload: { shareId, itemIds } })
370 export const itemRestoreIntent = createOptimisticAction(
371 'item::restore::intent',
373 payload: { item: ItemRevision } & SelectedItem,
374 callback?: ActionCallback<ReturnType<typeof itemRestoreSuccess> | ReturnType<typeof itemRestoreFailure>>
375 ) => withCallback(callback)({ payload }),
376 ({ payload }) => getItemActionId(payload)
379 export const itemRestoreFailure = createOptimisticAction(
380 'item::restore::failure',
381 (payload: SelectedItem, error: unknown) =>
384 text: c('Error').t`Restoring item failed`,
386 })({ payload, error }),
387 ({ payload }) => getItemActionId(payload)
390 export const itemRestoreSuccess = createOptimisticAction(
391 'item::restore::success',
392 (payload: SelectedItem) =>
397 text: c('Info').t`Item restored`,
400 ({ payload }) => getItemActionId(payload)
403 export const itemBulkRestoreIntent = createAction(
404 'item::bulk:restore::intent',
405 (payload: { selected: BulkSelectionDTO }) =>
407 withRequest({ status: 'start', id: itemsBulkRestoreRequest() }),
412 text: c('Info').t`Restoring items from trash`,
417 export const itemBulkRestoreFailure = createAction(
418 'item::bulk::restore::failure',
419 withRequestFailure((payload: {}, error: unknown) =>
422 text: c('Error').t`Failed to restore items from trash`,
424 })({ payload, error })
428 export const itemBulkRestoreProgress = createAction(
429 'item::bulk::restore::progress',
430 withRequestProgress((payload: BatchItemRevisionIDs) => withCache({ payload }))
433 export const itemBulkRestoreSuccess = createAction(
434 'item::bulk::restore::success',
435 withRequestSuccess((payload: {}) =>
438 text: c('Info').t`Selected items successfully restored from trash`,
443 export const itemAutofilled = createAction('item::autofilled', (payload: SelectedItem) => ({ payload }));
445 export const itemsUsedSync = createAction('items::used::sync', (items: (SelectedItem & { lastUseTime: number })[]) =>
446 withCache({ payload: { items } })
449 export const itemPinIntent = createAction('item::pin::intent', (payload: UniqueItem) =>
451 withRequest({ status: 'start', id: itemPinRequest(payload.shareId, payload.itemId) }),
454 text: c('Info').t`Pinning item...`,
461 export const itemPinSuccess = createAction(
462 'item::pin::success',
463 withRequestSuccess((payload: UniqueItem) =>
468 text: c('Info').t`Item successfully pinned`,
474 export const itemPinFailure = createAction(
475 'item::pin::failure',
476 withRequestFailure((error: unknown) =>
479 text: c('Error').t`Failed to pin item`,
481 })({ payload: {}, error })
485 export const itemUnpinIntent = createAction('item::unpin::intent', (payload: UniqueItem) =>
487 withRequest({ status: 'start', id: itemUnpinRequest(payload.shareId, payload.itemId) }),
490 text: c('Info').t`Unpinning item...`,
497 export const itemUnpinSuccess = createAction(
498 'item::unpin::success',
499 withRequestSuccess((payload: UniqueItem) =>
504 text: c('Info').t`Item successfully unpinned`,
510 export const itemUnpinFailure = createAction(
511 'item::unpin::failure',
512 withRequestFailure((error: unknown) =>
515 text: c('Error').t`Failed to unpin item`,
517 })({ payload: {}, error })
521 export const itemHistoryIntent = createAction('item::history::intent', (payload: ItemRevisionsIntent) =>
522 withRequest({ status: 'start', id: itemRevisionsRequest(payload.shareId, payload.itemId) })({ payload })
525 export const itemHistorySuccess = createAction(
526 'item::history::success',
527 withRequestSuccess((payload: ItemRevisionsSuccess) => ({ payload }), { data: true })
530 export const itemHistoryFailure = createAction(
531 'item::history::failure',
532 withRequestFailure((error: unknown) =>
535 text: c('Error').t`Failed to load item history`,
537 })({ payload: {}, error })
541 export const secureLinksGet = requestActionsFactory<void, SecureLink[]>('secure-link::get')({
542 requestId: secureLinksGetRequest,
543 success: { config: { maxAge: UNIX_MINUTE } },
546 export const secureLinkCreate = requestActionsFactory<SecureLinkCreationDTO, SecureLink>('secure-link::create')({
547 requestId: ({ shareId, itemId }) => secureLinkCreateRequest(shareId, itemId),
548 success: { config: { data: true } },
553 text: c('Error').t`Secure link could not be created.`,
555 })({ payload: { error: getErrorMessage(error) } }),
559 export const secureLinkOpen = requestActionsFactory<SecureLinkQuery, SecureLinkItem>('secure-link::open')({
560 requestId: ({ token }) => secureLinkOpenRequest(token),
561 success: { config: { data: true } },
563 config: { data: true },
567 text: c('Error').t`Secure link could not be opened.`,
569 })({ payload: { error: getErrorMessage(error) } }),
573 export const secureLinkRemove = requestActionsFactory<SecureLinkDeleteDTO, SecureLinkDeleteDTO>('secure-link::remove')({
574 requestId: ({ shareId, itemId }) => secureLinkRemoveRequest(shareId, itemId),
576 prepare: (payload) =>
580 text: c('Info').t`Removing secure link...`,
584 prepare: (payload) =>
587 text: c('Info').t`The secure link has been removed`,
595 .t`There was an error while removing the secure link. Please try again in a few minutes.`,
597 })({ payload: null }),
601 export const secureLinksRemoveInactive = requestActionsFactory<void, SecureLink[]>('secure-links::remove::inactive')({
602 requestId: secureLinksRemoveInactiveRequest,
604 prepare: (payload) =>
608 text: c('Info').t`Removing all inactive secure links...`,
612 prepare: (payload) =>
615 text: c('Info').t`All inactive secure links were removed`,
622 text: c('Error').t`Inactive secure links could not be removed.`,
624 })({ payload: null }),