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 @@ -16,8 +16,10 @@ CalendarQueryUpdateResult, } from '../types/entry-types.js'; import type { HistoryRevisionInfo } from '../types/history-types.js'; -import type { CallServerEndpoint } from '../utils/call-server-endpoint.js'; +import { extractKeyserverIDFromID } from '../utils/action-utils.js'; import { dateFromString } from '../utils/date-utils.js'; +import { useKeyserverCall } from '../utils/keyserver-call.js'; +import type { CallKeyserverEndpoint } from '../utils/keyserver-call.js'; const fetchEntriesActionTypes = Object.freeze({ started: 'FETCH_ENTRIES_STARTED', @@ -26,15 +28,38 @@ }); const fetchEntries = ( - callServerEndpoint: CallServerEndpoint, + callKeyserverEndpoint: CallKeyserverEndpoint, + allKeyserverIDs: $ReadOnlyArray, ): ((calendarQuery: CalendarQuery) => Promise) => async calendarQuery => { - const response = await callServerEndpoint('fetch_entries', calendarQuery); + const requests = {}; + for (const keyserverID of allKeyserverIDs) { + requests[keyserverID] = calendarQuery; + } + + const responses = await callKeyserverEndpoint('fetch_entries', requests); + let rawEntryInfos = []; + for (const keyserverID in responses) { + rawEntryInfos = rawEntryInfos.concat( + responses[keyserverID].rawEntryInfos, + ); + } return { - rawEntryInfos: response.rawEntryInfos, + rawEntryInfos, }; }; +function useFetchEntries(): ( + calendarQuery: CalendarQuery, +) => Promise { + return useKeyserverCall(fetchEntries); +} + +export type UpdateCalendarQueryInput = { + +calendarQuery: CalendarQuery, + +reduxAlreadyUpdated?: boolean, +}; + const updateCalendarQueryActionTypes = Object.freeze({ started: 'UPDATE_CALENDAR_QUERY_STARTED', success: 'UPDATE_CALENDAR_QUERY_SUCCESS', @@ -42,17 +67,33 @@ }); const updateCalendarQuery = ( - callServerEndpoint: CallServerEndpoint, + callKeyserverEndpoint: CallKeyserverEndpoint, + allKeyserverIDs: $ReadOnlyArray, ): (( - calendarQuery: CalendarQuery, - reduxAlreadyUpdated?: boolean, + input: UpdateCalendarQueryInput, ) => Promise) => - async (calendarQuery, reduxAlreadyUpdated = false) => { - const response = await callServerEndpoint( + async input => { + const { calendarQuery, reduxAlreadyUpdated = false } = input; + + const requests = {}; + for (const keyserverID of allKeyserverIDs) { + requests[keyserverID] = calendarQuery; + } + + const responses = await callKeyserverEndpoint( 'update_calendar_query', - calendarQuery, + requests, ); - const { rawEntryInfos, deletedEntryIDs } = response; + let rawEntryInfos = []; + let deletedEntryIDs = []; + for (const keyserverID in responses) { + rawEntryInfos = rawEntryInfos.concat( + responses[keyserverID].rawEntryInfos, + ); + deletedEntryIDs = deletedEntryIDs.concat( + responses[keyserverID].deletedEntryIDs, + ); + } return { rawEntryInfos, deletedEntryIDs, @@ -61,6 +102,12 @@ }; }; +function useUpdateCalendarQuery(): ( + input: UpdateCalendarQueryInput, +) => Promise { + return useKeyserverCall(updateCalendarQuery); +} + const createLocalEntryActionType = 'CREATE_LOCAL_ENTRY'; function createLocalEntry( threadID: string, @@ -90,19 +137,28 @@ }); const createEntry = ( - callServerEndpoint: CallServerEndpoint, - ): ((request: CreateEntryInfo) => Promise) => - async request => { - const result = await callServerEndpoint('create_entry', request); + callKeyserverEndpoint: CallKeyserverEndpoint, + ): ((input: CreateEntryInfo) => Promise) => + async input => { + const keyserverID = extractKeyserverIDFromID(input.threadID); + const requests = { [keyserverID]: input }; + + const response = await callKeyserverEndpoint('create_entry', requests); return { - entryID: result.entryID, - newMessageInfos: result.newMessageInfos, - threadID: request.threadID, - localID: request.localID, - updatesResult: result.updatesResult, + entryID: response[keyserverID].entryID, + newMessageInfos: response[keyserverID].newMessageInfos, + threadID: response[keyserverID].threadID, + localID: response[keyserverID].localID, + updatesResult: response[keyserverID].updatesResult, }; }; +function useCreateEntry(): ( + request: CreateEntryInfo, +) => Promise { + return useKeyserverCall(createEntry); +} + const saveEntryActionTypes = Object.freeze({ started: 'SAVE_ENTRY_STARTED', success: 'SAVE_ENTRY_SUCCESS', @@ -111,17 +167,26 @@ const concurrentModificationResetActionType = 'CONCURRENT_MODIFICATION_RESET'; const saveEntry = ( - callServerEndpoint: CallServerEndpoint, - ): ((request: SaveEntryInfo) => Promise) => - async request => { - const result = await callServerEndpoint('update_entry', request); + callKeyserverEndpoint: CallKeyserverEndpoint, + ): ((input: SaveEntryInfo) => Promise) => + async input => { + const keyserverID = extractKeyserverIDFromID(input.entryID); + const requests = { + [keyserverID]: input, + }; + + const response = await callKeyserverEndpoint('update_entry', requests); return { - entryID: result.entryID, - newMessageInfos: result.newMessageInfos, - updatesResult: result.updatesResult, + entryID: response[keyserverID].entryID, + newMessageInfos: response[keyserverID].newMessageInfos, + updatesResult: response[keyserverID].updatesResult, }; }; +function useSaveEntry(): (input: SaveEntryInfo) => Promise { + return useKeyserverCall(saveEntry); +} + const deleteEntryActionTypes = Object.freeze({ started: 'DELETE_ENTRY_STARTED', success: 'DELETE_ENTRY_SUCCESS', @@ -129,20 +194,34 @@ }); const deleteEntry = ( - callServerEndpoint: CallServerEndpoint, + callKeyserverEndpoint: CallKeyserverEndpoint, ): ((info: DeleteEntryInfo) => Promise) => - async info => { - const response = await callServerEndpoint('delete_entry', { - ...info, - timestamp: Date.now(), - }); + async input => { + const keyserverID = extractKeyserverIDFromID(input.entryID); + const requests = { + [keyserverID]: { + ...input, + timestamp: Date.now(), + }, + }; + + const response = await callKeyserverEndpoint('delete_entry', requests); return { - newMessageInfos: response.newMessageInfos, - threadID: response.threadID, - updatesResult: response.updatesResult, + newMessageInfos: response[keyserverID].newMessageInfos, + threadID: response[keyserverID].threadID, + updatesResult: response[keyserverID].updatesResult, }; }; +function useDeleteEntry(): ( + info: DeleteEntryInfo, +) => Promise { + return useKeyserverCall(deleteEntry); +} + +export type FetchRevisionsForEntryInput = { + +entryID: string, +}; const fetchRevisionsForEntryActionTypes = Object.freeze({ started: 'FETCH_REVISIONS_FOR_ENTRY_STARTED', success: 'FETCH_REVISIONS_FOR_ENTRY_SUCCESS', @@ -150,15 +229,31 @@ }); const fetchRevisionsForEntry = ( - callServerEndpoint: CallServerEndpoint, - ): ((entryID: string) => Promise<$ReadOnlyArray>) => - async entryID => { - const response = await callServerEndpoint('fetch_entry_revisions', { - id: entryID, - }); - return response.result; + callKeyserverEndpoint: CallKeyserverEndpoint, + ): (( + input: FetchRevisionsForEntryInput, + ) => Promise<$ReadOnlyArray>) => + async input => { + const keyserverID = extractKeyserverIDFromID(input.entryID); + const requests = { + [keyserverID]: { + id: input.entryID, + }, + }; + + const response = await callKeyserverEndpoint( + 'fetch_entry_revisions', + requests, + ); + return response[keyserverID].result; }; +function useFetchRevisionsForEntry(): ( + input: FetchRevisionsForEntryInput, +) => Promise<$ReadOnlyArray> { + return useKeyserverCall(fetchRevisionsForEntry); +} + const restoreEntryActionTypes = Object.freeze({ started: 'RESTORE_ENTRY_STARTED', success: 'RESTORE_ENTRY_SUCCESS', @@ -166,35 +261,41 @@ }); const restoreEntry = ( - callServerEndpoint: CallServerEndpoint, - ): ((info: RestoreEntryInfo) => Promise) => - async info => { - const response = await callServerEndpoint('restore_entry', { - ...info, - timestamp: Date.now(), - }); + callKeyserverEndpoint: CallKeyserverEndpoint, + ): ((input: RestoreEntryInfo) => Promise) => + async input => { + const keyserverID = extractKeyserverIDFromID(input.entryID); + const requests = { [keyserverID]: { ...input, timestamp: Date.now() } }; + + const response = await callKeyserverEndpoint('restore_entry', requests); return { - newMessageInfos: response.newMessageInfos, - updatesResult: response.updatesResult, + newMessageInfos: response[keyserverID].newMessageInfos, + updatesResult: response[keyserverID].updatesResult, }; }; +function useRestoreEntry(): ( + input: RestoreEntryInfo, +) => Promise { + return useKeyserverCall(restoreEntry); +} + export { fetchEntriesActionTypes, - fetchEntries, + useFetchEntries, updateCalendarQueryActionTypes, - updateCalendarQuery, + useUpdateCalendarQuery, createLocalEntryActionType, createLocalEntry, createEntryActionTypes, - createEntry, + useCreateEntry, saveEntryActionTypes, concurrentModificationResetActionType, - saveEntry, + useSaveEntry, deleteEntryActionTypes, - deleteEntry, + useDeleteEntry, fetchRevisionsForEntryActionTypes, - fetchRevisionsForEntry, + useFetchRevisionsForEntry, restoreEntryActionTypes, - restoreEntry, + useRestoreEntry, }; diff --git a/lib/socket/calendar-query-handler.react.js b/lib/socket/calendar-query-handler.react.js --- a/lib/socket/calendar-query-handler.react.js +++ b/lib/socket/calendar-query-handler.react.js @@ -6,8 +6,9 @@ import { updateCalendarQueryActionTypes, - updateCalendarQuery, + useUpdateCalendarQuery, } from '../actions/entry-actions.js'; +import type { UpdateCalendarQueryInput } from '../actions/entry-actions.js'; import { connectionSelector } from '../selectors/keyserver-selectors.js'; import { timeUntilCalendarRangeExpiration } from '../selectors/nav-selectors.js'; import { useIsAppForegrounded } from '../shared/lifecycle-utils.js'; @@ -20,7 +21,6 @@ import { type DispatchActionPromise, useDispatchActionPromise, - useServerCall, } from '../utils/action-utils.js'; import { useSelector } from '../utils/redux-utils.js'; @@ -36,8 +36,7 @@ +foreground: boolean, +dispatchActionPromise: DispatchActionPromise, +updateCalendarQuery: ( - calendarQuery: CalendarQuery, - reduxAlreadyUpdated?: boolean, + input: UpdateCalendarQueryInput, ) => Promise, }; class CalendarQueryHandler extends React.PureComponent { @@ -110,7 +109,10 @@ this.serverCalendarQuery = calendarQuery; this.props.dispatchActionPromise( updateCalendarQueryActionTypes, - this.props.updateCalendarQuery(calendarQuery, true), + this.props.updateCalendarQuery({ + calendarQuery, + reduxAlreadyUpdated: true, + }), undefined, ({ calendarQuery }: CalendarQueryUpdateStartingPayload), ); @@ -132,7 +134,7 @@ ); // We include this so that componentDidUpdate will be called on foreground const foreground = useIsAppForegrounded(); - const callUpdateCalendarQuery = useServerCall(updateCalendarQuery); + const callUpdateCalendarQuery = useUpdateCalendarQuery(); const dispatchActionPromise = useDispatchActionPromise(); const calendarQuery = useSelector(state => state.actualizedCalendarQuery); diff --git a/native/calendar/calendar.react.js b/native/calendar/calendar.react.js --- a/native/calendar/calendar.react.js +++ b/native/calendar/calendar.react.js @@ -22,8 +22,9 @@ import { updateCalendarQueryActionTypes, - updateCalendarQuery, + useUpdateCalendarQuery, } from 'lib/actions/entry-actions.js'; +import type { UpdateCalendarQueryInput } from 'lib/actions/entry-actions.js'; import { connectionSelector } from 'lib/selectors/keyserver-selectors.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import { entryKey } from 'lib/shared/entry-utils.js'; @@ -37,7 +38,6 @@ import type { ConnectionStatus } from 'lib/types/socket-types.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; import { - useServerCall, useDispatchActionPromise, type DispatchActionPromise, } from 'lib/utils/action-utils.js'; @@ -143,8 +143,7 @@ +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs +updateCalendarQuery: ( - calendarQuery: CalendarQuery, - reduxAlreadyUpdated?: boolean, + input: UpdateCalendarQueryInput, ) => Promise, }; type State = { @@ -954,7 +953,7 @@ dispatchCalendarQueryUpdate(calendarQuery: CalendarQuery) { this.props.dispatchActionPromise( updateCalendarQueryActionTypes, - this.props.updateCalendarQuery(calendarQuery), + this.props.updateCalendarQuery({ calendarQuery }), ); } @@ -1077,7 +1076,7 @@ const indicatorStyle = useIndicatorStyle(); const dispatchActionPromise = useDispatchActionPromise(); - const callUpdateCalendarQuery = useServerCall(updateCalendarQuery); + const callUpdateCalendarQuery = useUpdateCalendarQuery(); return ( Promise, }; type State = { @@ -230,7 +229,10 @@ }; this.props.dispatchActionPromise( updateCalendarQueryActionTypes, - this.props.updateCalendarQuery(newCalendarQuery, true), + this.props.updateCalendarQuery({ + calendarQuery: newCalendarQuery, + reduxAlreadyUpdated: true, + }), undefined, ({ calendarQuery: newCalendarQuery }: CalendarQueryUpdateStartingPayload), ); @@ -245,7 +247,10 @@ }; this.props.dispatchActionPromise( updateCalendarQueryActionTypes, - this.props.updateCalendarQuery(newCalendarQuery, true), + this.props.updateCalendarQuery({ + calendarQuery: newCalendarQuery, + reduxAlreadyUpdated: true, + }), undefined, ({ calendarQuery: newCalendarQuery }: CalendarQueryUpdateStartingPayload), ); @@ -260,7 +265,7 @@ const navInfo = useSelector(state => state.navInfo); const currentCalendarQuery = useSelector(webCalendarQuery); const loggedIn = useSelector(isLoggedIn); - const callUpdateCalendarQuery = useServerCall(updateCalendarQuery); + const callUpdateCalendarQuery = useUpdateCalendarQuery(); const dispatchActionPromise = useDispatchActionPromise(); return ( 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 @@ -7,11 +7,11 @@ import { createEntryActionTypes, - createEntry, + useCreateEntry, saveEntryActionTypes, - saveEntry, + useSaveEntry, deleteEntryActionTypes, - deleteEntry, + useDeleteEntry, concurrentModificationResetActionType, } from 'lib/actions/entry-actions.js'; import { @@ -41,7 +41,6 @@ import type { ResolvedThreadInfo } from 'lib/types/thread-types.js'; import { type DispatchActionPromise, - useServerCall, useDispatchActionPromise, } from 'lib/utils/action-utils.js'; import { dateString } from 'lib/utils/date-utils.js'; @@ -476,9 +475,9 @@ const connection = useSelector(connectionSelector); invariant(connection, 'keyserver missing from keyserverStore'); const online = connection.status === 'connected'; - const callCreateEntry = useServerCall(createEntry); - const callSaveEntry = useServerCall(saveEntry); - const callDeleteEntry = useServerCall(deleteEntry); + const callCreateEntry = useCreateEntry(); + const callSaveEntry = useSaveEntry(); + const callDeleteEntry = useDeleteEntry(); const dispatchActionPromise = useDispatchActionPromise(); const dispatch = useDispatch(); diff --git a/web/modals/history/history-entry.react.js b/web/modals/history/history-entry.react.js --- a/web/modals/history/history-entry.react.js +++ b/web/modals/history/history-entry.react.js @@ -6,7 +6,7 @@ import { restoreEntryActionTypes, - restoreEntry, + useRestoreEntry, } from 'lib/actions/entry-actions.js'; import { useENSNames } from 'lib/hooks/ens-cache.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; @@ -24,7 +24,6 @@ import { type DispatchActionPromise, useDispatchActionPromise, - useServerCall, } from 'lib/utils/action-utils.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; @@ -165,7 +164,7 @@ ), ); const calenderQuery = useSelector(nonThreadCalendarQuery); - const callRestoreEntry = useServerCall(restoreEntry); + const callRestoreEntry = useRestoreEntry(); const dispatchActionPromise = useDispatchActionPromise(); const { creator } = props.entryInfo; diff --git a/web/modals/history/history-modal.react.js b/web/modals/history/history-modal.react.js --- a/web/modals/history/history-modal.react.js +++ b/web/modals/history/history-modal.react.js @@ -10,10 +10,11 @@ import { fetchEntriesActionTypes, - fetchEntries, + useFetchEntries, fetchRevisionsForEntryActionTypes, - fetchRevisionsForEntry, + useFetchRevisionsForEntry, } from 'lib/actions/entry-actions.js'; +import type { FetchRevisionsForEntryInput } from 'lib/actions/entry-actions.js'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import { nonExcludeDeletedCalendarFiltersSelector } from 'lib/selectors/calendar-filter-selectors.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; @@ -30,7 +31,6 @@ import type { LoadingStatus } from 'lib/types/loading-types.js'; import { type DispatchActionPromise, - useServerCall, useDispatchActionPromise, } from 'lib/utils/action-utils.js'; import { prettyDateWithoutDay } from 'lib/utils/date-utils.js'; @@ -59,7 +59,7 @@ calendarQuery: CalendarQuery, ) => Promise, +fetchRevisionsForEntry: ( - entryID: string, + input: FetchRevisionsForEntryInput, ) => Promise<$ReadOnlyArray>, +onClose: () => void, }; @@ -212,7 +212,7 @@ } async fetchRevisionsForEntryAction(entryID: string) { - const result = await this.props.fetchRevisionsForEntry(entryID); + const result = await this.props.fetchRevisionsForEntry({ entryID }); this.setState(prevState => { // This merge here will preserve time ordering correctly const revisions = _unionBy('id')(result)(prevState.revisions); @@ -261,8 +261,8 @@ const calendarFilters = useSelector( nonExcludeDeletedCalendarFiltersSelector, ); - const callFetchEntries = useServerCall(fetchEntries); - const callFetchRevisionsForEntry = useServerCall(fetchRevisionsForEntry); + const callFetchEntries = useFetchEntries(); + const callFetchRevisionsForEntry = useFetchRevisionsForEntry(); const dispatchActionPromise = useDispatchActionPromise(); const modalContext = useModalContext();