1 import { type FC, type PropsWithChildren, createContext, useCallback, useContext, useMemo } from 'react';
2 import { useDispatch, useStore } from 'react-redux';
4 import { c } from 'ttag';
6 import { useBulkSelect } from '@proton/pass/components/Bulk/BulkSelectProvider';
7 import { ConfirmTrashAlias } from '@proton/pass/components/Item/Actions/ConfirmAliasActions';
9 ConfirmDeleteManyItems,
11 ConfirmTrashManyItems,
12 } from '@proton/pass/components/Item/Actions/ConfirmBulkActions';
13 import { ConfirmDeleteItem, ConfirmMoveItem } from '@proton/pass/components/Item/Actions/ConfirmItemActions';
14 import { VaultSelect, VaultSelectMode, useVaultSelectModalHandles } from '@proton/pass/components/Vault/VaultSelect';
15 import { useConfirm } from '@proton/pass/hooks/useConfirm';
16 import { isAliasDisabled, isAliasItem } from '@proton/pass/lib/items/item.predicates';
20 itemBulkRestoreIntent,
26 } from '@proton/pass/store/actions';
27 import { selectAliasTrashAcknowledged, selectLoginItemByEmail } from '@proton/pass/store/selectors';
28 import type { State } from '@proton/pass/store/types';
29 import { type BulkSelectionDTO, type ItemRevision, type MaybeNull } from '@proton/pass/types';
30 import { uniqueId } from '@proton/pass/utils/string/unique-id';
32 /** Ongoing: move every item action definition to this
33 * context object. This context should be loosely connected */
34 type ItemActionsContextType = {
35 delete: (item: ItemRevision) => void;
36 deleteMany: (items: BulkSelectionDTO) => void;
37 move: (item: ItemRevision, mode: VaultSelectMode) => void;
38 moveMany: (items: BulkSelectionDTO, shareId?: string) => void;
39 restore: (item: ItemRevision) => void;
40 restoreMany: (items: BulkSelectionDTO) => void;
41 trash: (item: ItemRevision) => void;
42 trashMany: (items: BulkSelectionDTO) => void;
45 const ItemActionsContext = createContext<MaybeNull<ItemActionsContextType>>(null);
47 export const ItemActionsProvider: FC<PropsWithChildren> = ({ children }) => {
48 const bulk = useBulkSelect();
49 const dispatch = useDispatch();
50 const store = useStore<State>();
52 const { closeVaultSelect, openVaultSelect, modalState } = useVaultSelectModalHandles();
54 const moveItem = useConfirm(
56 (options: { item: ItemRevision; shareId: string }) =>
60 optimisticId: uniqueId(),
67 const moveManyItems = useConfirm(
69 (options: { selected: BulkSelectionDTO; shareId: string }) => {
70 dispatch(itemBulkMoveIntent(options));
77 const trashItem = useConfirm(
79 (item: ItemRevision) =>
83 shareId: item.shareId,
91 const trashManyItems = useConfirm(
93 (selected: BulkSelectionDTO) => {
94 dispatch(itemBulkTrashIntent({ selected }));
101 const deleteItem = useConfirm(
102 useCallback((item: ItemRevision) => {
106 shareId: item.shareId,
113 const deleteManyItems = useConfirm(
115 (selected: BulkSelectionDTO) => {
116 dispatch(itemBulkDeleteIntent({ selected }));
123 const restoreItem = useCallback(
124 (item: ItemRevision) =>
128 shareId: item.shareId,
135 const restoreManyItems = useCallback(
136 (selected: BulkSelectionDTO) => {
137 dispatch(itemBulkRestoreIntent({ selected }));
143 const context = useMemo<ItemActionsContextType>(() => {
145 move: (item, mode) =>
148 shareId: item.shareId,
149 onSubmit: (shareId) => {
150 moveItem.prompt({ item, shareId });
154 moveMany: (selected, shareId) =>
156 ? moveManyItems.prompt({ selected, shareId })
158 mode: VaultSelectMode.Writable,
159 shareId: '' /* allow all vaults */,
160 onSubmit: (shareId) => {
161 moveManyItems.prompt({ selected, shareId });
166 if (isAliasItem(item.data)) {
167 const state = store.getState();
168 const aliasEmail = item.aliasEmail!;
169 const relatedLogin = selectLoginItemByEmail(aliasEmail)(state);
170 const ack = selectAliasTrashAcknowledged(state);
171 if ((isAliasDisabled(item) || ack) && !relatedLogin) trashItem.call(item);
172 else trashItem.prompt(item);
173 } else trashItem.call(item);
175 trashMany: trashManyItems.prompt,
176 delete: deleteItem.prompt,
177 deleteMany: deleteManyItems.prompt,
178 restore: restoreItem,
179 restoreMany: restoreManyItems,
184 <ItemActionsContext.Provider value={context}>
187 downgradeMessage={c('Info')
188 .t`You have exceeded the number of vaults included in your subscription. Items can only be moved to your first two vaults. To move items between all vaults upgrade your subscription.`}
189 onClose={closeVaultSelect}
193 {moveItem.pending && (
195 item={moveItem.param.item}
196 shareId={moveItem.param.shareId}
197 onCancel={moveItem.cancel}
198 onConfirm={moveItem.confirm}
202 {moveManyItems.pending && (
203 <ConfirmMoveManyItems
204 selected={moveManyItems.param.selected}
205 shareId={moveManyItems.param.shareId}
206 onConfirm={moveManyItems.confirm}
207 onCancel={moveManyItems.cancel}
211 {trashItem.pending && (
212 <ConfirmTrashAlias onCancel={trashItem.cancel} onConfirm={trashItem.confirm} item={trashItem.param} />
215 {trashManyItems.pending && (
216 <ConfirmTrashManyItems
217 onCancel={trashManyItems.cancel}
218 onConfirm={trashManyItems.confirm}
219 selected={trashManyItems.param}
223 {deleteItem.pending && (
225 onCancel={deleteItem.cancel}
226 onConfirm={deleteItem.confirm}
227 item={deleteItem.param}
231 {deleteManyItems.pending && (
232 <ConfirmDeleteManyItems
233 onCancel={deleteManyItems.cancel}
234 onConfirm={deleteManyItems.confirm}
235 selected={deleteManyItems.param}
238 </ItemActionsContext.Provider>
242 export const useItemsActions = (): ItemActionsContextType => useContext(ItemActionsContext)!;