diff --git a/lib/actions/entry-actions.js b/lib/actions/entry-actions.js --- a/lib/actions/entry-actions.js +++ b/lib/actions/entry-actions.js @@ -19,6 +19,7 @@ import { type DMCreateEntryOperation, type DMEditEntryOperation, + type DMDeleteEntryOperation, } from '../types/dm-ops.js'; import type { RawEntryInfo, @@ -407,10 +408,81 @@ }; }; +export type UseDeleteEntryInput = $ReadOnly< + | { + +thick: false, + +deleteEntryInfo: DeleteEntryInfo, + } + | { + +thick: true, + +threadInfo: ThreadInfo, + +deleteEntryInfo: DeleteEntryInfo, + }, +>; function useDeleteEntry(): ( - info: DeleteEntryInfo, + input: UseDeleteEntryInput, ) => Promise { - return useKeyserverCall(deleteEntry); + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + + const processAndSendDMOperation = useProcessAndSendDMOperation(); + const keyserverCall = useKeyserverCall(deleteEntry); + const entryInfos = useSelector(state => state.entryStore.entryInfos); + + return React.useCallback( + async (input: UseDeleteEntryInput) => { + if (!input.thick) { + const result = await keyserverCall(input.deleteEntryInfo); + return result; + } + const { deleteEntryInfo, threadInfo } = input; + const prevEntry = entryInfos[deleteEntryInfo.entryID]; + + invariant(viewerID, 'viewerID must be set'); + + const op: DMDeleteEntryOperation = { + type: 'delete_entry', + threadID: threadInfo.id, + creatorID: viewerID, + creationTime: prevEntry.creationTime, + time: Date.now(), + entryID: deleteEntryInfo.entryID, + entryDate: stringFromDate( + prevEntry.year, + prevEntry.month, + prevEntry.day, + ), + prevText: deleteEntryInfo.prevText, + messageID: uuid.v4(), + }; + + const opSpecification: OutboundDMOperationSpecification = { + type: dmOperationSpecificationTypes.OUTBOUND, + op, + recipients: { + type: 'all_thread_members', + threadID: + threadInfo.type === thickThreadTypes.THICK_SIDEBAR && + threadInfo.parentThreadID + ? threadInfo.parentThreadID + : threadInfo.id, + }, + }; + + await processAndSendDMOperation(opSpecification); + + return { + threadID: threadInfo.id, + newMessageInfos: [], + updatesResult: { + viewerUpdates: [], + userInfos: [], + }, + }; + }, + [keyserverCall, processAndSendDMOperation, viewerID, entryInfos], + ); } export type FetchRevisionsForEntryInput = { diff --git a/lib/shared/dm-ops/delete-entry-spec.js b/lib/shared/dm-ops/delete-entry-spec.js --- a/lib/shared/dm-ops/delete-entry-spec.js +++ b/lib/shared/dm-ops/delete-entry-spec.js @@ -15,7 +15,8 @@ import { rawMessageInfoFromMessageData } from '../message-utils.js'; function createMessageDataFromDMOperation(dmOperation: DMDeleteEntryOperation) { - const { threadID, creatorID, time, entryID, entryDate, text } = dmOperation; + const { threadID, creatorID, time, entryID, entryDate, prevText } = + dmOperation; return { type: messageTypes.DELETE_ENTRY, threadID, @@ -23,7 +24,7 @@ time, entryID, date: entryDate, - text, + text: prevText, }; } @@ -37,9 +38,10 @@ threadID, creatorID, time, + creationTime, entryID, entryDate: dateString, - text, + prevText, messageID, } = dmOperation; @@ -52,11 +54,11 @@ const rawEntryInfo: ThickRawEntryInfo = { id: entryID, threadID, - text, + text: prevText, year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate(), - creationTime: time, + creationTime, creatorID, thick: true, deleted: true, diff --git a/lib/types/dm-ops.js b/lib/types/dm-ops.js --- a/lib/types/dm-ops.js +++ b/lib/types/dm-ops.js @@ -403,9 +403,10 @@ +threadID: string, +creatorID: string, +time: number, + +creationTime: number, +entryID: string, +entryDate: string, - +text: string, + +prevText: string, +messageID: string, }; @@ -415,9 +416,10 @@ threadID: t.String, creatorID: t.String, time: t.Number, + creationTime: t.Number, entryID: t.String, entryDate: t.String, - text: t.String, + prevText: t.String, messageID: t.String, }); diff --git a/native/calendar/entry.react.js b/native/calendar/entry.react.js --- a/native/calendar/entry.react.js +++ b/native/calendar/entry.react.js @@ -27,6 +27,7 @@ useSaveEntry, type UseCreateEntryInput, type UseSaveEntryInput, + type UseDeleteEntryInput, } from 'lib/actions/entry-actions.js'; import { extractKeyserverIDFromIDOptional } from 'lib/keyserver-conn/keyserver-call-utils.js'; import { registerFetchKey } from 'lib/reducers/loading-reducer.js'; @@ -37,7 +38,6 @@ import type { CalendarQuery, CreateEntryPayload, - DeleteEntryInfo, DeleteEntryResult, SaveEntryPayload, SaveEntryResult, @@ -209,7 +209,7 @@ // async functions that hit server APIs +createEntry: (input: UseCreateEntryInput) => Promise, +saveEntry: (input: UseSaveEntryInput) => Promise, - +deleteEntry: (info: DeleteEntryInfo) => Promise, + +deleteEntry: (input: UseDeleteEntryInput) => Promise, +canEditEntry: boolean, }; type State = { @@ -786,15 +786,28 @@ } async deleteAction( - serverID: ?string, + entryID: ?string, prevText: string, ): Promise { - if (serverID) { - return await this.props.deleteEntry({ - entryID: serverID, + if (entryID) { + const deleteEntryInfo = { + entryID, prevText, calendarQuery: this.props.calendarQuery(), - }); + }; + + const useDeleteEntryInput = threadTypeIsThick(this.props.threadInfo.type) + ? { + thick: true, + threadInfo: this.props.threadInfo, + deleteEntryInfo, + } + : { + thick: false, + deleteEntryInfo, + }; + + return await this.props.deleteEntry(useDeleteEntryInput); } else if (this.creating || this.currentlySaving) { this.needsDeleteAfterSave = true; } diff --git a/web/calendar/entry.react.js b/web/calendar/entry.react.js --- a/web/calendar/entry.react.js +++ b/web/calendar/entry.react.js @@ -14,6 +14,7 @@ concurrentModificationResetActionType, type UseCreateEntryInput, type UseSaveEntryInput, + type UseDeleteEntryInput, } from 'lib/actions/entry-actions.js'; import { type PushModal, @@ -30,7 +31,6 @@ type SaveEntryResult, type SaveEntryPayload, type CreateEntryPayload, - type DeleteEntryInfo, type DeleteEntryResult, type CalendarQuery, } from 'lib/types/entry-types.js'; @@ -74,7 +74,7 @@ +dispatchActionPromise: DispatchActionPromise, +createEntry: (input: UseCreateEntryInput) => Promise, +saveEntry: (input: UseSaveEntryInput) => Promise, - +deleteEntry: (info: DeleteEntryInfo) => Promise, + +deleteEntry: (input: UseDeleteEntryInput) => Promise, +pushModal: PushModal, +popModal: () => void, }; @@ -444,7 +444,7 @@ } async deleteAction( - serverID: ?string, + entryID: ?string, focusOnNextEntry: boolean, ): Promise { invariant( @@ -454,12 +454,25 @@ if (focusOnNextEntry) { this.props.focusOnFirstEntryNewerThan(this.props.entryInfo.creationTime); } - if (serverID) { - return await this.props.deleteEntry({ - entryID: serverID, + if (entryID) { + const deleteEntryInfo = { + entryID, prevText: this.props.entryInfo.text, calendarQuery: this.props.calendarQuery(), - }); + }; + + const useDeleteEntryInput = threadTypeIsThick(this.props.threadInfo.type) + ? { + thick: true, + threadInfo: this.props.threadInfo, + deleteEntryInfo, + } + : { + thick: false, + deleteEntryInfo, + }; + + return await this.props.deleteEntry(useDeleteEntryInput); } else if (this.creating) { this.needsDeleteAfterCreation = true; }