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 @@ -1,12 +1,22 @@ // @flow +import invariant from 'invariant'; +import * as React from 'react'; +import uuid from 'uuid'; + import { extractKeyserverIDFromID, sortCalendarQueryPerKeyserver, } from '../keyserver-conn/keyserver-call-utils.js'; import { useKeyserverCall } from '../keyserver-conn/keyserver-call.js'; import type { CallKeyserverEndpoint } from '../keyserver-conn/keyserver-conn-types.js'; +import { + dmOperationSpecificationTypes, + type OutboundDMOperationSpecification, +} from '../shared/dm-ops/dm-op-utils.js'; +import { useProcessAndSendDMOperation } from '../shared/dm-ops/process-dm-ops.js'; import { getNextLocalID } from '../shared/message-utils.js'; +import { type DMCreateEntryOperation } from '../types/dm-ops.js'; import type { RawEntryInfo, CalendarQuery, @@ -22,7 +32,10 @@ CalendarQueryUpdateResult, } from '../types/entry-types.js'; import type { HistoryRevisionInfo } from '../types/history-types.js'; +import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types'; +import { thickThreadTypes } from '../types/thread-types-enum.js'; import { dateFromString } from '../utils/date-utils.js'; +import { useSelector } from '../utils/redux-utils.js'; const fetchEntriesActionTypes = Object.freeze({ started: 'FETCH_ENTRIES_STARTED', @@ -178,10 +191,76 @@ }; }; +export type UseCreateEntryInput = $ReadOnly< + | { + +thick: false, + +createEntryInfo: CreateEntryInfo, + } + | { + +thick: true, + +threadInfo: ThreadInfo, + +createEntryInfo: CreateEntryInfo, + }, +>; function useCreateEntry(): ( - request: CreateEntryInfo, + input: UseCreateEntryInput, ) => Promise { - return useKeyserverCall(createEntry); + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + + const processAndSendDMOperation = useProcessAndSendDMOperation(); + const keyserverCall = useKeyserverCall(createEntry); + + return React.useCallback( + async (input: UseCreateEntryInput) => { + if (!input.thick) { + const result = await keyserverCall(input.createEntryInfo); + return result; + } + + invariant(viewerID, 'viewerID must be set'); + const entryID = uuid.v4(); + + const { createEntryInfo, threadInfo } = input; + const op: DMCreateEntryOperation = { + type: 'create_entry', + threadID: threadInfo.id, + creatorID: viewerID, + time: createEntryInfo.timestamp, + entryID: uuid.v4(), + entryDate: createEntryInfo.date, + text: createEntryInfo.text, + 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 { + entryID, + newMessageInfos: [], + threadID: createEntryInfo.threadID, + localID: createEntryInfo.localID, + updatesResult: { + viewerUpdates: [], + userInfos: [], + }, + }; + }, + [keyserverCall, processAndSendDMOperation, viewerID], + ); } const saveEntryActionTypes = Object.freeze({ 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 @@ -25,6 +25,7 @@ useCreateEntry, useDeleteEntry, useSaveEntry, + type UseCreateEntryInput, } from 'lib/actions/entry-actions.js'; import { extractKeyserverIDFromIDOptional } from 'lib/keyserver-conn/keyserver-call-utils.js'; import { registerFetchKey } from 'lib/reducers/loading-reducer.js'; @@ -34,7 +35,6 @@ import { useThreadHasPermission } from 'lib/shared/thread-utils.js'; import type { CalendarQuery, - CreateEntryInfo, CreateEntryPayload, DeleteEntryInfo, DeleteEntryResult, @@ -49,6 +49,7 @@ } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import type { Dispatch } from 'lib/types/redux-types.js'; import { threadPermissions } from 'lib/types/thread-permission-types.js'; +import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; import { dateString } from 'lib/utils/date-utils.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; import { ServerError } from 'lib/utils/errors.js'; @@ -206,7 +207,7 @@ +dispatch: Dispatch, +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs - +createEntry: (info: CreateEntryInfo) => Promise, + +createEntry: (input: UseCreateEntryInput) => Promise, +saveEntry: (info: SaveEntryInfo) => Promise, +deleteEntry: (info: DeleteEntryInfo) => Promise, +canEditEntry: boolean, @@ -637,7 +638,7 @@ invariant(localID, "if there's no serverID, there should be a localID"); const curSaveAttempt = this.nextSaveAttemptIndex++; try { - const response = await this.props.createEntry({ + const createEntryInfo = { text, timestamp: this.props.entryInfo.creationTime, date: dateString( @@ -648,7 +649,21 @@ threadID: this.props.entryInfo.threadID, localID, calendarQuery: this.props.calendarQuery(), - }); + }; + + const useCreateEntryInput = threadTypeIsThick(this.props.threadInfo.type) + ? { + thick: true, + threadInfo: this.props.threadInfo, + createEntryInfo, + } + : { + thick: false, + createEntryInfo, + }; + + const response = await this.props.createEntry(useCreateEntryInput); + if (curSaveAttempt + 1 === this.nextSaveAttemptIndex) { this.guardedSetState({ loadingStatus: 'inactive' }); } 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 @@ -12,6 +12,7 @@ deleteEntryActionTypes, useDeleteEntry, concurrentModificationResetActionType, + type UseCreateEntryInput, } from 'lib/actions/entry-actions.js'; import { type PushModal, @@ -25,7 +26,6 @@ import { useThreadHasPermission } from 'lib/shared/thread-utils.js'; import { type EntryInfo, - type CreateEntryInfo, type SaveEntryInfo, type SaveEntryResult, type SaveEntryPayload, @@ -38,6 +38,7 @@ import type { ResolvedThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import type { Dispatch } from 'lib/types/redux-types.js'; import { threadPermissions } from 'lib/types/thread-permission-types.js'; +import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; import { dateString } from 'lib/utils/date-utils.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; import { ServerError } from 'lib/utils/errors.js'; @@ -71,7 +72,7 @@ +online: boolean, +dispatch: Dispatch, +dispatchActionPromise: DispatchActionPromise, - +createEntry: (info: CreateEntryInfo) => Promise, + +createEntry: (input: UseCreateEntryInput) => Promise, +saveEntry: (info: SaveEntryInfo) => Promise, +deleteEntry: (info: DeleteEntryInfo) => Promise, +pushModal: PushModal, @@ -322,7 +323,7 @@ const curSaveAttempt = this.nextSaveAttemptIndex++; this.guardedSetState({ loadingStatus: 'loading' }); try { - const response = await this.props.createEntry({ + const createEntryInfo = { text, timestamp: this.props.entryInfo.creationTime, date: dateString( @@ -333,7 +334,20 @@ threadID: this.props.entryInfo.threadID, localID, calendarQuery: this.props.calendarQuery(), - }); + }; + + const useCreateEntryInput = threadTypeIsThick(this.props.threadInfo.type) + ? { + thick: true, + threadInfo: this.props.threadInfo, + createEntryInfo, + } + : { + thick: false, + createEntryInfo, + }; + + const response = await this.props.createEntry(useCreateEntryInput); if (curSaveAttempt + 1 === this.nextSaveAttemptIndex) { this.guardedSetState({ loadingStatus: 'inactive' }); }