diff --git a/CHANGELOG.md b/CHANGELOG.md index dc631d4d..d02b95b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `jobService.registerJobsListener()` and `jobService.notifyJobSubmitted()` for reacting to job submissions - sms MFA setup support to `useMfa()` and `` - `mfaService` for MFA setup APIs and MFA requirement helpers - export `LoadingWithMessage` component @@ -17,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- `submissionService.submit()` to notify job listeners when a submission includes a `jobId` - **[BREAKING]** `LoginAttemptResponse` to replace `mfaCodeCallback` with `mfa.codeCallback` and `mfa.method` - **[BREAKING]** `useLogin()` to remove `isMfaCodeRequired`; use `mfaMethod` as the source of truth for an active MFA step - **[BREAKING]** `useMfa()` to require an `mfaMethod` parameter for `beginMfaSetup(mfaMethod)` diff --git a/package-lock.json b/package-lock.json index a6874bae..f6fe7dee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4629,7 +4629,7 @@ }, "node_modules/@oneblink/types": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/oneblink/types.git#f3828e0e3888734731ca145de59170c45b2a65a3", + "resolved": "git+ssh://git@github.com/oneblink/types.git#2efbcc5818202693b13fef39ebb9ea2402f1815b", "dev": true, "license": "GPL-3.0-only", "dependencies": { diff --git a/src/apps/job-service.ts b/src/apps/job-service.ts index d5b1b636..5e821560 100644 --- a/src/apps/job-service.ts +++ b/src/apps/job-service.ts @@ -11,6 +11,63 @@ import tenants from './tenants' import { SubmissionTypes } from '@oneblink/types' import Sentry from './Sentry' +export type JobListenerEvent = { + action: 'SUBMITTED' + jobId: string +} + +export type JobsListener = (event: JobListenerEvent) => unknown + +const jobsListeners: Array = [] + +/** + * Register a listener function that will be called when jobs state should be + * updated after a submission. + * + * #### Example + * + * ```js + * const listener = ({ action, jobId }) => { + * if (action === 'SUBMITTED') { + * // remove job from local state... + * } + * } + * const deregister = jobService.registerJobsListener(listener) + * + * // When no longer needed, remember to deregister the listener + * deregister() + * ``` + * + * @param listener + * @returns + */ +export function registerJobsListener(listener: JobsListener): () => void { + jobsListeners.push(listener) + + return () => { + const index = jobsListeners.indexOf(listener) + if (index !== -1) { + jobsListeners.splice(index, 1) + } + } +} + +function executeJobsListeners(event: JobListenerEvent) { + for (const jobsListener of jobsListeners) { + jobsListener(event) + } +} + +/** + * Notify listeners that a job has been submitted and should no longer appear in + * the jobs list. + * + * @param jobId + */ +export function notifyJobSubmitted(jobId: string) { + executeJobsListeners({ action: 'SUBMITTED', jobId }) +} + async function removePendingSubmissions( jobList: SubmissionTypes.FormsAppJob[], ) { diff --git a/src/apps/services/submit.ts b/src/apps/services/submit.ts index 473ebed4..e8db83f0 100644 --- a/src/apps/services/submit.ts +++ b/src/apps/services/submit.ts @@ -27,6 +27,13 @@ import serverValidateForm from './server-validation' import OneBlinkAppsError from './errors/oneBlinkAppsError' import { uploadFormSubmission } from './api/submissions' import { syncDrafts } from '../draft-service' +import { notifyJobSubmitted } from '../job-service' + +function notifyJobSubmittedIfApplicable(formSubmission: FormSubmission) { + if (formSubmission.jobId) { + notifyJobSubmitted(formSubmission.jobId) + } +} type SubmissionParams = { formSubmission: FormSubmission @@ -106,6 +113,7 @@ export default async function submit({ 'Offline or always submitting via pending queue - saving submission to pending queue..', ) await addFormSubmissionToPendingQueue(formSubmission) + notifyJobSubmittedIfApplicable(formSubmission) return Object.assign({}, formSubmission, { isOffline: isOffline(), isInPendingQueue: true, @@ -144,6 +152,7 @@ export default async function submit({ 'Attachments still uploading - saving submission to pending queue..', ) await addFormSubmissionToPendingQueue(formSubmission) + notifyJobSubmittedIfApplicable(formSubmission) return Object.assign({}, formSubmission, { isOffline: false, isInPendingQueue: true, @@ -260,6 +269,8 @@ export default async function submit({ await removePrefillFormData(formSubmission.preFillFormDataId) } + notifyJobSubmittedIfApplicable(formSubmission) + return formSubmissionResult } catch (error: OneBlinkAppsError | unknown) { if (error instanceof OneBlinkAppsError) { @@ -290,6 +301,7 @@ export default async function submit({ error, ) await addFormSubmissionToPendingQueue(formSubmission) + notifyJobSubmittedIfApplicable(formSubmission) return Object.assign({}, formSubmission, { isOffline: true, isInPendingQueue: true,