@go-avro/avro-js 0.0.47 → 0.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -9,10 +9,9 @@ import { CacheData } from '../types/cache';
|
|
|
9
9
|
import { Waiver } from '../types/api/Waiver';
|
|
10
10
|
import type { EmailSucceededPayload, EmailFailedPayload, EmailType } from '../types/api/EmailNotification';
|
|
11
11
|
import type { BulkDeleteBillsResponse, BulkEmailBillsResponse } from '../client/hooks/bills';
|
|
12
|
-
/** Callbacks for a tracked email request. */
|
|
13
12
|
export interface TrackEmailOptions {
|
|
14
13
|
emailType?: EmailType;
|
|
15
|
-
|
|
14
|
+
requestId?: string;
|
|
16
15
|
timeout?: number;
|
|
17
16
|
onSuccess?: (data: EmailSucceededPayload) => void;
|
|
18
17
|
onFailure?: (data: EmailFailedPayload) => void;
|
|
@@ -750,20 +749,7 @@ export declare class AvroQueryClient {
|
|
|
750
749
|
query?: string;
|
|
751
750
|
offset?: number;
|
|
752
751
|
}, cancelToken?: CancelToken, headers?: Record<string, string>): Promise<any>;
|
|
753
|
-
/**
|
|
754
|
-
* Lazily register socket listeners for email_succeeded / email_failed.
|
|
755
|
-
* Listeners live on the socket (not in a React effect) so they survive
|
|
756
|
-
* component unmounts.
|
|
757
|
-
*/
|
|
758
752
|
private _initEmailListeners;
|
|
759
|
-
/**
|
|
760
|
-
* Track an outbound email request.
|
|
761
|
-
*
|
|
762
|
-
* Generates a `request_id`, registers socket listeners (once), and returns
|
|
763
|
-
* the ID so callers can pass it in the HTTP body. The backend emits
|
|
764
|
-
* `email_succeeded` / `email_failed` to the user's GUID room; this method
|
|
765
|
-
* correlates the event by `request_id` and fires the appropriate callback.
|
|
766
|
-
*/
|
|
767
753
|
trackEmail(options?: TrackEmailOptions): string;
|
|
768
754
|
sendEmail(emailId: string, formData: FormData, progressUpdateCallback?: (loaded: number, total: number) => void): Promise<void>;
|
|
769
755
|
sendBillEmail(billId: string, body?: {
|
|
@@ -1100,12 +1100,6 @@ export class AvroQueryClient {
|
|
|
1100
1100
|
throw new StandardError(500, 'Failed to fetch sessions');
|
|
1101
1101
|
});
|
|
1102
1102
|
}
|
|
1103
|
-
/* ── Email delivery tracking ──────────────────────────────────────── */
|
|
1104
|
-
/**
|
|
1105
|
-
* Lazily register socket listeners for email_succeeded / email_failed.
|
|
1106
|
-
* Listeners live on the socket (not in a React effect) so they survive
|
|
1107
|
-
* component unmounts.
|
|
1108
|
-
*/
|
|
1109
1103
|
_initEmailListeners() {
|
|
1110
1104
|
if (this._emailListenersInit)
|
|
1111
1105
|
return;
|
|
@@ -1114,7 +1108,6 @@ export class AvroQueryClient {
|
|
|
1114
1108
|
const entry = this._emailTracking.get(data.request_id);
|
|
1115
1109
|
if (!entry)
|
|
1116
1110
|
return;
|
|
1117
|
-
// Reset timeout — more recipients may follow for the same request_id
|
|
1118
1111
|
clearTimeout(entry.timerId);
|
|
1119
1112
|
entry.timerId = setTimeout(() => {
|
|
1120
1113
|
this._emailTracking.delete(data.request_id);
|
|
@@ -1132,18 +1125,14 @@ export class AvroQueryClient {
|
|
|
1132
1125
|
entry.onFailure?.(data);
|
|
1133
1126
|
});
|
|
1134
1127
|
}
|
|
1135
|
-
/**
|
|
1136
|
-
* Track an outbound email request.
|
|
1137
|
-
*
|
|
1138
|
-
* Generates a `request_id`, registers socket listeners (once), and returns
|
|
1139
|
-
* the ID so callers can pass it in the HTTP body. The backend emits
|
|
1140
|
-
* `email_succeeded` / `email_failed` to the user's GUID room; this method
|
|
1141
|
-
* correlates the event by `request_id` and fires the appropriate callback.
|
|
1142
|
-
*/
|
|
1143
1128
|
trackEmail(options = {}) {
|
|
1144
1129
|
this._initEmailListeners();
|
|
1145
|
-
const requestId = uuidv4();
|
|
1130
|
+
const requestId = options.requestId ?? uuidv4();
|
|
1146
1131
|
const { timeout = 30000 } = options;
|
|
1132
|
+
const existing = this._emailTracking.get(requestId);
|
|
1133
|
+
if (existing) {
|
|
1134
|
+
clearTimeout(existing.timerId);
|
|
1135
|
+
}
|
|
1147
1136
|
const timerId = setTimeout(() => {
|
|
1148
1137
|
this._emailTracking.delete(requestId);
|
|
1149
1138
|
options.onTimeout?.(requestId);
|
|
@@ -5,45 +5,20 @@ export interface EmailResult {
|
|
|
5
5
|
status: EmailResultStatus;
|
|
6
6
|
emailType?: EmailType;
|
|
7
7
|
recipient?: string;
|
|
8
|
-
/**
|
|
9
|
-
* Bill GUID when the event came from a bill email — single or bulk.
|
|
10
|
-
* Use this to correlate per-bill outcomes when one `requestId`
|
|
11
|
-
* covers many bills (`POST /company/<id>/bills/bulk`).
|
|
12
|
-
*/
|
|
13
8
|
billGuid?: string;
|
|
14
9
|
error?: EmailFailedPayload['error'];
|
|
15
10
|
}
|
|
16
11
|
export interface UseEmailStatusOptions {
|
|
17
|
-
/** How long to wait for a socket event before firing `onTimeout` (ms). Default 30 000. */
|
|
18
12
|
timeout?: number;
|
|
19
|
-
/** Called when the backend reports success. */
|
|
20
13
|
onSuccess?: (result: EmailResult) => void;
|
|
21
|
-
/** Called when the backend reports failure. */
|
|
22
14
|
onFailure?: (result: EmailResult) => void;
|
|
23
|
-
/** Called when neither success nor failure arrives within the timeout window. */
|
|
24
15
|
onTimeout?: (requestId: string) => void;
|
|
25
16
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Subscribe to backend email delivery notifications via Socket.IO.
|
|
28
|
-
*
|
|
29
|
-
* Delegates to `AvroQueryClient.trackEmail()` which registers socket
|
|
30
|
-
* listeners on the client itself (not in a React effect), so
|
|
31
|
-
* notifications survive component unmounts.
|
|
32
|
-
*
|
|
33
|
-
* Usage:
|
|
34
|
-
* ```ts
|
|
35
|
-
* const { trackEmail, pending, results } = useEmailStatus({
|
|
36
|
-
* onSuccess: (r) => toast.success(`Email sent to ${r.recipient}`),
|
|
37
|
-
* onFailure: (r) => toast.error(`Email failed: ${r.error?.message}`),
|
|
38
|
-
* onTimeout: (id) => toast.warn("Email status unknown"),
|
|
39
|
-
* });
|
|
40
|
-
*
|
|
41
|
-
* const requestId = trackEmail("bill");
|
|
42
|
-
* await avroQueryClient.sendBillingEmail({ billId, request_id: requestId });
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
17
|
export declare function useEmailStatus(options?: UseEmailStatusOptions): {
|
|
46
|
-
readonly trackEmail: (emailType?: EmailType
|
|
18
|
+
readonly trackEmail: (emailType?: EmailType, opts?: {
|
|
19
|
+
requestId?: string;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
}) => string;
|
|
47
22
|
readonly pending: Map<string, {
|
|
48
23
|
emailType?: EmailType;
|
|
49
24
|
trackedAt: number;
|
|
@@ -1,45 +1,21 @@
|
|
|
1
1
|
import { useCallback, useRef, useState } from 'react';
|
|
2
2
|
import { useAvroQueryClient } from '../../client/AvroQueryClientProvider';
|
|
3
|
-
/* ──────────────────────────────────────────────────────────────────────── */
|
|
4
|
-
/* Hook */
|
|
5
|
-
/* ──────────────────────────────────────────────────────────────────────── */
|
|
6
|
-
/**
|
|
7
|
-
* Subscribe to backend email delivery notifications via Socket.IO.
|
|
8
|
-
*
|
|
9
|
-
* Delegates to `AvroQueryClient.trackEmail()` which registers socket
|
|
10
|
-
* listeners on the client itself (not in a React effect), so
|
|
11
|
-
* notifications survive component unmounts.
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* ```ts
|
|
15
|
-
* const { trackEmail, pending, results } = useEmailStatus({
|
|
16
|
-
* onSuccess: (r) => toast.success(`Email sent to ${r.recipient}`),
|
|
17
|
-
* onFailure: (r) => toast.error(`Email failed: ${r.error?.message}`),
|
|
18
|
-
* onTimeout: (id) => toast.warn("Email status unknown"),
|
|
19
|
-
* });
|
|
20
|
-
*
|
|
21
|
-
* const requestId = trackEmail("bill");
|
|
22
|
-
* await avroQueryClient.sendBillingEmail({ billId, request_id: requestId });
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
3
|
export function useEmailStatus(options = {}) {
|
|
26
4
|
const { timeout = 30000 } = options;
|
|
27
5
|
const client = useAvroQueryClient();
|
|
28
|
-
// Use refs for callbacks so the client-level handlers always see the latest
|
|
29
6
|
const onSuccessRef = useRef(options.onSuccess);
|
|
30
7
|
const onFailureRef = useRef(options.onFailure);
|
|
31
8
|
const onTimeoutRef = useRef(options.onTimeout);
|
|
32
9
|
onSuccessRef.current = options.onSuccess;
|
|
33
10
|
onFailureRef.current = options.onFailure;
|
|
34
11
|
onTimeoutRef.current = options.onTimeout;
|
|
35
|
-
// Expose reactive state so the UI can render pending / completed
|
|
36
12
|
const [pending, setPending] = useState(new Map());
|
|
37
13
|
const [results, setResults] = useState(new Map());
|
|
38
|
-
|
|
39
|
-
const trackEmail = useCallback((emailType) => {
|
|
14
|
+
const trackEmail = useCallback((emailType, opts = {}) => {
|
|
40
15
|
const requestId = client.trackEmail({
|
|
41
16
|
emailType,
|
|
42
|
-
|
|
17
|
+
requestId: opts.requestId,
|
|
18
|
+
timeout: opts.timeout ?? timeout,
|
|
43
19
|
onSuccess: (data) => {
|
|
44
20
|
const result = {
|
|
45
21
|
requestId: data.request_id,
|