Skip to content

Commit 2965493

Browse files
Merge pull request #1023 from oneblink/AP-7184b
AP-7184b # `draftService.syncDrafts` to download draft data in the ba…
2 parents 3eab504 + 4613e25 commit 2965493

5 files changed

Lines changed: 123 additions & 35 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- **[BREAKING]** `draftService.syncDrafts` to download draft data in the background
13+
1014
### Fixed
1115

1216
- Lot / DP Numbers display for NSW Point V3 form element

src/apps/draft-service.ts

Lines changed: 102 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import OneBlinkAppsError from './services/errors/oneBlinkAppsError'
55
import { isOffline } from './offline-service'
66
import { getUsername } from './services/cognito'
77
import { getFormsKeyId, getCurrentFormsAppUser } from './auth-service'
8-
import { getFormSubmissionDrafts, uploadDraftData } from './services/api/drafts'
8+
import {
9+
DRAFT_DATA_UNAVAILABLE_ERROR_TITLE,
10+
getFormSubmissionDrafts,
11+
uploadDraftData,
12+
} from './services/api/drafts'
913
import {
1014
getPendingQueueSubmissions,
1115
deletePendingQueueSubmission,
@@ -92,6 +96,7 @@ function generateLocalFormSubmissionDraftsFromDraftSubmissions(
9296
taskActionId: draftSubmission.taskCompletion?.taskAction.taskActionId,
9397
draftSubmission,
9498
versions: undefined,
99+
downloadStatus: 'SUCCESS',
95100
})
96101
}
97102
}
@@ -125,7 +130,9 @@ async function generatePublicLocalFormSubmissionDraftsFromStorage(
125130
)
126131

127132
return _orderBy(localFormSubmissionDrafts, (localFormSubmissionDraft) => {
128-
return localFormSubmissionDraft.draftSubmission?.createdAt
133+
return localFormSubmissionDraft.downloadStatus === 'SUCCESS'
134+
? localFormSubmissionDraft.draftSubmission?.createdAt
135+
: undefined
129136
})
130137
}
131138

@@ -144,6 +151,22 @@ async function generateLocalFormSubmissionDraftsFromStorage(
144151
deletedDraftIds,
145152
)
146153

154+
async function broadcastUpdate() {
155+
const draftsToBroadcast = Array.from(localFormSubmissionDraftsMap.values())
156+
157+
const orderedDrafts = _orderBy(
158+
draftsToBroadcast,
159+
(localFormSubmissionDraft) =>
160+
getLatestFormSubmissionDraftVersion(localFormSubmissionDraft.versions)
161+
?.createdAt,
162+
)
163+
164+
await executeDraftsListeners(orderedDrafts)
165+
}
166+
167+
// At this point we need to store the state of the drafts in localForage
168+
const draftsToDownload: SubmissionTypes.FormSubmissionDraft[] = []
169+
147170
for (const formSubmissionDraft of localDraftsStorage.syncedFormSubmissionDrafts) {
148171
if (
149172
// Unsycned version of draft takes priority over the synced version
@@ -153,33 +176,79 @@ async function generateLocalFormSubmissionDraftsFromStorage(
153176
// Remove any drafts deleted while offline
154177
!deletedDraftIds.has(formSubmissionDraft.id)
155178
) {
179+
draftsToDownload.push(formSubmissionDraft)
180+
localFormSubmissionDraftsMap.set(formSubmissionDraft.id, {
181+
...formSubmissionDraft,
182+
downloadStatus: 'PENDING',
183+
})
184+
}
185+
}
186+
187+
await broadcastUpdate()
188+
189+
// TODO Batch the downloads instead of sequentially
190+
if (draftsToDownload.length) {
191+
for (const formSubmissionDraft of draftsToDownload) {
192+
const currentValue = localFormSubmissionDraftsMap.get(
193+
formSubmissionDraft.id,
194+
)
195+
if (currentValue) {
196+
localFormSubmissionDraftsMap.set(formSubmissionDraft.id, {
197+
...currentValue,
198+
downloadStatus: 'DOWNLOADING',
199+
})
200+
}
201+
await broadcastUpdate()
202+
156203
const draftSubmission = await getDraftSubmission(
157204
formSubmissionDraft,
158205
).catch((err) => {
159206
console.warn(
160207
`Could not fetch draft submission for draft: ${formSubmissionDraft.id}`,
161208
err,
162209
)
210+
211+
if (
212+
err instanceof OneBlinkAppsError &&
213+
err.title === DRAFT_DATA_UNAVAILABLE_ERROR_TITLE
214+
) {
215+
localFormSubmissionDraftsMap.set(formSubmissionDraft.id, {
216+
...formSubmissionDraft,
217+
downloadStatus: 'NOT_AVAILABLE',
218+
})
219+
return
220+
}
221+
222+
localFormSubmissionDraftsMap.set(formSubmissionDraft.id, {
223+
...formSubmissionDraft,
224+
downloadStatus: 'ERROR',
225+
downloadError: err.message,
226+
})
227+
163228
return undefined
164229
})
165-
localFormSubmissionDraftsMap.set(formSubmissionDraft.id, {
166-
...formSubmissionDraft,
167-
draftSubmission,
168-
})
230+
if (draftSubmission) {
231+
localFormSubmissionDraftsMap.set(formSubmissionDraft.id, {
232+
...formSubmissionDraft,
233+
draftSubmission: draftSubmission,
234+
downloadStatus: 'SUCCESS',
235+
})
236+
}
237+
238+
await broadcastUpdate()
169239
}
170240
}
171241

172242
const localFormSubmissionDrafts = Array.from(
173243
localFormSubmissionDraftsMap.values(),
174244
)
175245

176-
return _orderBy(localFormSubmissionDrafts, (localFormSubmissionDraft) => {
177-
return (
178-
localFormSubmissionDraft.draftSubmission?.createdAt ||
246+
return _orderBy(
247+
localFormSubmissionDrafts,
248+
(localFormSubmissionDraft) =>
179249
getLatestFormSubmissionDraftVersion(localFormSubmissionDraft.versions)
180-
?.createdAt
181-
)
182-
})
250+
?.createdAt,
251+
)
183252
}
184253

185254
function errorHandler(error: Error): Error {
@@ -800,24 +869,29 @@ async function syncDrafts({
800869
localDraftsStorage.syncedFormSubmissionDrafts = formSubmissionDrafts
801870
}
802871

803-
await setAndBroadcastDrafts(localDraftsStorage)
804-
805-
if (localDraftsStorage.syncedFormSubmissionDrafts.length) {
806-
console.log(
807-
'Ensuring all draft data is available for offline use for synced drafts',
808-
localDraftsStorage.syncedFormSubmissionDrafts,
809-
)
810-
for (const formSubmissionDraft of localDraftsStorage.syncedFormSubmissionDrafts) {
811-
await getDraftSubmission(formSubmissionDraft, abortSignal).catch(
812-
(error) => {
813-
console.warn('Could not download Draft Data as JSON', error)
814-
},
872+
console.log('Downloading drafts in the background')
873+
setAndBroadcastDrafts(localDraftsStorage)
874+
.then(async () => {
875+
console.log('Finished syncing drafts.')
876+
})
877+
.catch((error) => {
878+
if (abortSignal?.aborted) {
879+
console.log('Syncing drafts has been aborted')
880+
return
881+
}
882+
console.warn(
883+
'Error while attempting download drafts in the background',
884+
error,
815885
)
816-
}
817-
}
886+
if (!(error instanceof OneBlinkAppsError)) {
887+
Sentry.captureException(error)
888+
}
889+
})
890+
.finally(() => {
891+
_isSyncingDrafts = false
892+
})
818893

819-
console.log('Finished syncing drafts.')
820-
_isSyncingDrafts = false
894+
// broadcast the drafts and download the draft data in the background
821895
} catch (error) {
822896
_isSyncingDrafts = false
823897
if (abortSignal?.aborted) {

src/apps/services/api/drafts.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import generateOneBlinkUploader from '../generateOneBlinkUploader'
1212
import { OneBlinkStorageError } from '@oneblink/storage'
1313
import generateOneBlinkDownloader from '../generateOneBlinkDownloader'
1414

15+
export const DRAFT_DATA_UNAVAILABLE_ERROR_TITLE = 'Draft Data Unavailable'
16+
1517
async function uploadDraftData(
1618
draftSubmission: DraftSubmission,
1719
onProgress?: ProgressListener,
@@ -186,7 +188,7 @@ async function downloadDraftData(
186188
throw new OneBlinkAppsError(
187189
"Data has been removed based on your administrator's draft data retention policy.",
188190
{
189-
title: 'Draft Data Unavailable',
191+
title: DRAFT_DATA_UNAVAILABLE_ERROR_TITLE,
190192
},
191193
)
192194
}

src/apps/types/submissions.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,21 @@ export type LocalFormSubmissionDraft = Omit<
104104
* remotely yet.
105105
*/
106106
versions: SubmissionTypes.FormSubmissionDraftVersion[] | undefined
107-
/**
108-
* The draft submission data. `undefined` if it has not been downloaded
109-
* locally yet.
110-
*/
111-
draftSubmission: DraftSubmission | undefined
112107
/** `true` if the draft was created by a public user (not logged in). */
113108
isPublic?: boolean
114-
}
109+
} & (
110+
| {
111+
downloadStatus: 'PENDING' | 'DOWNLOADING' | 'NOT_AVAILABLE'
112+
}
113+
| {
114+
downloadStatus: 'ERROR'
115+
downloadError: string
116+
}
117+
| {
118+
downloadStatus: 'SUCCESS'
119+
draftSubmission: DraftSubmission
120+
}
121+
)
115122

116123
export type FormSubmission = NewFormSubmission &
117124
BaseFormSubmission & {

src/hooks/useDrafts.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export function DraftsContextProvider({
100100
syncError: null,
101101
}))
102102
}, [])
103+
// TODO add formId to prioritize downloads for specific forms
103104
const syncDrafts = React.useCallback(
104105
async (abortSignal: AbortSignal | undefined) => {
105106
if (!isDraftsEnabled || isUsingFormsKey) {

0 commit comments

Comments
 (0)