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,