@quiltt/react 4.3.0 → 4.3.2
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.
- package/CHANGELOG.md +19 -0
- package/dist/{QuilttAuthProvider-12s-DNnAmo8E.js → QuilttAuthProvider-12s-D-Wr7LUT.js} +21 -11
- package/dist/index.d.ts +9 -12
- package/dist/index.js +12 -9
- package/dist/useQuilttConnector-12s-BJljrsWT.js +173 -0
- package/dist/{useQuilttInstitutions-12s-Djd5k9Ae.js → useQuilttInstitutions-12s-PJd2C3BF.js} +10 -7
- package/dist/{useQuilttSession-12s-QlPCR8A-.js → useQuilttSession-12s-CwVw-aOM.js} +1 -1
- package/package.json +2 -2
- package/src/components/QuilttButton.tsx +5 -3
- package/src/hooks/useQuilttConnector.ts +95 -32
- package/src/hooks/useQuilttInstitutions.ts +13 -10
- package/src/providers/QuilttAuthProvider.tsx +25 -11
- package/src/providers/QuilttProvider.tsx +13 -9
- package/src/providers/QuilttSettingsProvider.tsx +2 -2
- package/src/utils/isDeepEqual.ts +2 -1
- package/dist/useQuilttConnector-12s-BaTUXD56.js +0 -128
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @quiltt/react
|
|
2
2
|
|
|
3
|
+
## 4.3.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#372](https://github.com/quiltt/quiltt-js/pull/372) [`c022ebe`](https://github.com/quiltt/quiltt-js/commit/c022ebed0c82404a1bdbf5abbaf6e60b49f2d07a) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Hardened React SDK against unstable prop references by implementing ref-based callback wrappers and deep equality checks, eliminating unnecessary re-renders, event handler churn, and API calls without requiring customers to use useCallback.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`c022ebe`](https://github.com/quiltt/quiltt-js/commit/c022ebed0c82404a1bdbf5abbaf6e60b49f2d07a)]:
|
|
10
|
+
- @quiltt/core@4.3.2
|
|
11
|
+
|
|
12
|
+
## 4.3.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [#366](https://github.com/quiltt/quiltt-js/pull/366) [`dc376b5`](https://github.com/quiltt/quiltt-js/commit/dc376b52dd824d7867ca74677bbfd5c54eff5cdc) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Warn if useQuilttConnector is unmounted while in use
|
|
17
|
+
- [#365](https://github.com/quiltt/quiltt-js/pull/365) [`5f6b8af`](https://github.com/quiltt/quiltt-js/commit/5f6b8af153086c77bb2227d43ae7023fb0c47985) Thanks [@rubendinho](https://github.com/rubendinho)! - Standardize SDK agent tracking header to `Quiltt-SDK-Agent` and add support for custom Apollo Links injection
|
|
18
|
+
|
|
19
|
+
- Updated dependencies [[`dc376b5`](https://github.com/quiltt/quiltt-js/commit/dc376b52dd824d7867ca74677bbfd5c54eff5cdc)]:
|
|
20
|
+
- @quiltt/core@4.3.1
|
|
21
|
+
|
|
3
22
|
## 4.3.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
|
@@ -7,7 +7,7 @@ import './useSession-12s-7GOn4sUn.js';
|
|
|
7
7
|
import 'use-debounce';
|
|
8
8
|
import { jsx } from 'react/jsx-runtime';
|
|
9
9
|
import { ApolloProvider } from '@apollo/client/react/context/ApolloProvider.js';
|
|
10
|
-
import { u as useQuilttSession } from './useQuilttSession-12s-
|
|
10
|
+
import { u as useQuilttSession } from './useQuilttSession-12s-CwVw-aOM.js';
|
|
11
11
|
|
|
12
12
|
const useAuthenticateSession = (auth, setSession)=>{
|
|
13
13
|
const authenticateSession = useCallback(async (payload, callbacks)=>{
|
|
@@ -133,8 +133,9 @@ const useRevokeSession = (auth, session, setSession)=>{
|
|
|
133
133
|
}
|
|
134
134
|
if (obj1 instanceof Set && obj2 instanceof Set) {
|
|
135
135
|
if (obj1.size !== obj2.size) return false;
|
|
136
|
+
const arr2 = Array.from(obj2);
|
|
136
137
|
for (const item of obj1){
|
|
137
|
-
if (!
|
|
138
|
+
if (!arr2.some((value)=>isDeepEqual(item, value))) return false;
|
|
138
139
|
}
|
|
139
140
|
return true;
|
|
140
141
|
}
|
|
@@ -158,16 +159,25 @@ const useRevokeSession = (auth, session, setSession)=>{
|
|
|
158
159
|
* it into trusted storage. While this process is happening, the component is put
|
|
159
160
|
* into a loading state and the children are not rendered to prevent race conditions
|
|
160
161
|
* from triggering within the transitionary state.
|
|
161
|
-
*/ const QuilttAuthProvider = ({ token, children })=>{
|
|
162
|
+
*/ const QuilttAuthProvider = ({ graphqlClient, token, children })=>{
|
|
162
163
|
const { session, importSession } = useQuilttSession();
|
|
163
164
|
const previousSessionRef = useRef(session);
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
const previousTokenRef = useRef();
|
|
166
|
+
// Memoize the client to avoid unnecessary re-renders
|
|
167
|
+
const apolloClient = useMemo(()=>graphqlClient || new QuilttClient({
|
|
166
168
|
cache: new InMemoryCache()
|
|
167
|
-
}), [
|
|
168
|
-
|
|
169
|
+
}), [
|
|
170
|
+
graphqlClient
|
|
171
|
+
]);
|
|
172
|
+
// Import passed in token (only if value has changed)
|
|
169
173
|
useEffect(()=>{
|
|
170
|
-
if (token
|
|
174
|
+
if (token && token !== previousTokenRef.current) {
|
|
175
|
+
importSession(token);
|
|
176
|
+
previousTokenRef.current = token;
|
|
177
|
+
} else if (!token) {
|
|
178
|
+
// Reset ref when token becomes undefined to allow re-import of same token later
|
|
179
|
+
previousTokenRef.current = undefined;
|
|
180
|
+
}
|
|
171
181
|
}, [
|
|
172
182
|
token,
|
|
173
183
|
importSession
|
|
@@ -175,15 +185,15 @@ const useRevokeSession = (auth, session, setSession)=>{
|
|
|
175
185
|
// Reset Client Store when session changes (using deep comparison)
|
|
176
186
|
useEffect(()=>{
|
|
177
187
|
if (!isDeepEqual(session, previousSessionRef.current)) {
|
|
178
|
-
|
|
188
|
+
apolloClient.resetStore();
|
|
179
189
|
previousSessionRef.current = session;
|
|
180
190
|
}
|
|
181
191
|
}, [
|
|
182
192
|
session,
|
|
183
|
-
|
|
193
|
+
apolloClient
|
|
184
194
|
]);
|
|
185
195
|
return /*#__PURE__*/ jsx(ApolloProvider, {
|
|
186
|
-
client:
|
|
196
|
+
client: apolloClient,
|
|
187
197
|
children: children
|
|
188
198
|
});
|
|
189
199
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConnectorSDKCallbacks, Maybe, QuilttJWT, PasscodePayload, UnprocessableData, AuthAPI, UsernamePayload, ConnectorSDKConnectorOptions, InstitutionsData } from '@quiltt/core';
|
|
1
|
+
import { ConnectorSDKCallbacks, Maybe, QuilttJWT, PasscodePayload, UnprocessableData, AuthAPI, UsernamePayload, ConnectorSDKConnectorOptions, InstitutionsData, QuilttClient } from '@quiltt/core';
|
|
2
2
|
export * from '@quiltt/core';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import { JSX, ComponentType, ElementType, PropsWithChildren, MouseEvent, RefObject, useEffect, Dispatch, SetStateAction, FC } from 'react';
|
|
@@ -159,7 +159,9 @@ declare const useQuilttSettings: () => {
|
|
|
159
159
|
declare const useStorage: <T>(key: string, initialState?: Maybe<T>) => [Maybe<T> | undefined, Dispatch<SetStateAction<Maybe<T> | undefined>>];
|
|
160
160
|
|
|
161
161
|
type QuilttAuthProviderProps = PropsWithChildren & {
|
|
162
|
-
/**
|
|
162
|
+
/** A custom QuilttClient instance to use instead of the default */
|
|
163
|
+
graphqlClient?: QuilttClient;
|
|
164
|
+
/** The Quiltt Session token obtained from the server */
|
|
163
165
|
token?: string;
|
|
164
166
|
};
|
|
165
167
|
/**
|
|
@@ -170,19 +172,14 @@ type QuilttAuthProviderProps = PropsWithChildren & {
|
|
|
170
172
|
*/
|
|
171
173
|
declare const QuilttAuthProvider: FC<QuilttAuthProviderProps>;
|
|
172
174
|
|
|
173
|
-
type QuilttProviderProps = PropsWithChildren & {
|
|
174
|
-
/** The client ID for the client-side Auth API */
|
|
175
|
-
clientId?: string;
|
|
176
|
-
/** The Session token obtained from the server */
|
|
177
|
-
token?: string;
|
|
178
|
-
};
|
|
179
|
-
declare const QuilttProvider: FC<QuilttProviderProps>;
|
|
180
|
-
|
|
181
175
|
type QuilttSettingsProviderProps = PropsWithChildren & {
|
|
182
|
-
/** The Client ID to use for the
|
|
176
|
+
/** The Client ID to use for the passwordless Auth API */
|
|
183
177
|
clientId?: string;
|
|
184
178
|
};
|
|
185
179
|
declare const QuilttSettingsProvider: FC<QuilttSettingsProviderProps>;
|
|
186
180
|
|
|
181
|
+
type QuilttProviderProps = QuilttSettingsProviderProps & QuilttAuthProviderProps;
|
|
182
|
+
declare const QuilttProvider: FC<QuilttProviderProps>;
|
|
183
|
+
|
|
187
184
|
export { QuilttAuthProvider, QuilttButton, QuilttContainer, QuilttProvider, QuilttSettingsProvider, useAuthenticateSession, useEventListener, useIdentifySession, useImportSession, useIsomorphicLayoutEffect, useQuilttClient, useQuilttConnector, useQuilttInstitutions, useQuilttSession, useQuilttSettings, useRevokeSession, useSession, useStorage };
|
|
188
|
-
export type { AuthenticateSession, IdentifySession, ImportSession, RevokeSession, SetSession, UseQuilttInstitutions, UseQuilttSession };
|
|
185
|
+
export type { AuthenticateSession, IdentifySession, ImportSession, QuilttAuthProviderProps, QuilttSettingsProviderProps, RevokeSession, SetSession, UseQuilttInstitutions, UseQuilttSession };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export * from '@quiltt/core';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { useRef, useEffect } from 'react';
|
|
4
|
-
import { u as useQuilttConnector } from './useQuilttConnector-12s-
|
|
5
|
-
import { i as isDeepEqual, Q as QuilttAuthProvider } from './QuilttAuthProvider-12s-
|
|
6
|
-
export { b as useAuthenticateSession, a as useIdentifySession, u as useImportSession, c as useRevokeSession } from './QuilttAuthProvider-12s-
|
|
4
|
+
import { u as useQuilttConnector } from './useQuilttConnector-12s-BJljrsWT.js';
|
|
5
|
+
import { i as isDeepEqual, Q as QuilttAuthProvider } from './QuilttAuthProvider-12s-D-Wr7LUT.js';
|
|
6
|
+
export { b as useAuthenticateSession, a as useIdentifySession, u as useImportSession, c as useRevokeSession } from './QuilttAuthProvider-12s-D-Wr7LUT.js';
|
|
7
7
|
export { u as useEventListener } from './useEventListener-12s-D_-6QIXa.js';
|
|
8
8
|
export { u as useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect-12s-DeTHOKz1.js';
|
|
9
9
|
export { u as useQuilttClient } from './useQuilttClient-12s-CAAUait1.js';
|
|
10
|
-
export { u as useQuilttInstitutions } from './useQuilttInstitutions-12s-
|
|
11
|
-
export { u as useQuilttSession } from './useQuilttSession-12s-
|
|
10
|
+
export { u as useQuilttInstitutions } from './useQuilttInstitutions-12s-PJd2C3BF.js';
|
|
11
|
+
export { u as useQuilttSession } from './useQuilttSession-12s-CwVw-aOM.js';
|
|
12
12
|
export { u as useQuilttSettings } from './useQuilttSettings-12s--rCJoNHD.js';
|
|
13
13
|
export { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
14
14
|
export { u as useStorage } from './useStorage-12s-DHcq3Kuh.js';
|
|
@@ -78,9 +78,11 @@ import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmO
|
|
|
78
78
|
// 1. Pre-open validation
|
|
79
79
|
// 2. Preventing opening via event.preventDefault()
|
|
80
80
|
// 3. Setting up state before connector opens
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
-
|
|
81
|
+
onClick?.(event);
|
|
82
|
+
// Only open if event wasn't prevented
|
|
83
|
+
if (!event.defaultPrevented) {
|
|
84
|
+
open();
|
|
85
|
+
}
|
|
84
86
|
};
|
|
85
87
|
// Generate key for forced remounting if enabled, but respect user-provided key
|
|
86
88
|
const buttonKey = props.key ?? (forceRemountOnConnectionChange ? `${connectorId}-${connectionId || 'no-connection'}` : undefined);
|
|
@@ -159,11 +161,12 @@ import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmO
|
|
|
159
161
|
}, containerKey);
|
|
160
162
|
};
|
|
161
163
|
|
|
162
|
-
const QuilttProvider = ({ clientId, token, children })=>{
|
|
164
|
+
const QuilttProvider = ({ clientId, graphqlClient, token, children })=>{
|
|
163
165
|
return /*#__PURE__*/ jsx(QuilttSettingsProvider, {
|
|
164
166
|
clientId: clientId,
|
|
165
167
|
children: /*#__PURE__*/ jsx(QuilttAuthProvider, {
|
|
166
168
|
token: token,
|
|
169
|
+
graphqlClient: graphqlClient,
|
|
167
170
|
children: children
|
|
168
171
|
})
|
|
169
172
|
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
|
+
import { cdnBase } from '@quiltt/core';
|
|
4
|
+
import { u as useQuilttSession } from './useQuilttSession-12s-CwVw-aOM.js';
|
|
5
|
+
import { u as useScript } from './useScript-12s-JCgaTW9n.js';
|
|
6
|
+
import { i as isDeepEqual } from './QuilttAuthProvider-12s-D-Wr7LUT.js';
|
|
7
|
+
|
|
8
|
+
var version = "4.3.2";
|
|
9
|
+
|
|
10
|
+
const useQuilttConnector = (connectorId, options)=>{
|
|
11
|
+
const status = useScript(`${cdnBase}/v1/connector.js?agent=react-${version}`, {
|
|
12
|
+
nonce: options?.nonce
|
|
13
|
+
});
|
|
14
|
+
// This ensures we're not destructuring `session` before the script has loaded
|
|
15
|
+
const useQuilttSessionReturn = useQuilttSession();
|
|
16
|
+
const session = useQuilttSessionReturn?.session || null;
|
|
17
|
+
const [connector, setConnector] = useState();
|
|
18
|
+
const [isOpening, setIsOpening] = useState(false);
|
|
19
|
+
// Keep track of the previous connectionId to detect changes
|
|
20
|
+
const prevConnectionIdRef = useRef(options?.connectionId);
|
|
21
|
+
const prevConnectorIdRef = useRef(connectorId);
|
|
22
|
+
const prevInstitutionRef = useRef(options?.institution);
|
|
23
|
+
const connectorCreatedRef = useRef(false);
|
|
24
|
+
// Track whether the connector is currently open
|
|
25
|
+
const isConnectorOpenRef = useRef(false);
|
|
26
|
+
// Store callbacks in refs to maintain stable references
|
|
27
|
+
const callbacksRef = useRef(options || {});
|
|
28
|
+
useEffect(()=>{
|
|
29
|
+
callbacksRef.current = options || {};
|
|
30
|
+
});
|
|
31
|
+
// Set Session
|
|
32
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: trigger effects when script status changes too
|
|
33
|
+
useEffect(()=>{
|
|
34
|
+
if (typeof Quiltt === 'undefined') return;
|
|
35
|
+
Quiltt.authenticate(session?.token);
|
|
36
|
+
}, [
|
|
37
|
+
status,
|
|
38
|
+
session?.token
|
|
39
|
+
]);
|
|
40
|
+
// Set Connector
|
|
41
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: trigger effects when script status changes too
|
|
42
|
+
useEffect(()=>{
|
|
43
|
+
if (typeof Quiltt === 'undefined' || !connectorId) return;
|
|
44
|
+
const currentConnectionId = options?.connectionId;
|
|
45
|
+
const currentInstitution = options?.institution;
|
|
46
|
+
// Check for changes - use deep equality for institution object
|
|
47
|
+
const connectionIdChanged = prevConnectionIdRef.current !== currentConnectionId;
|
|
48
|
+
const connectorIdChanged = prevConnectorIdRef.current !== connectorId;
|
|
49
|
+
const institutionChanged = !isDeepEqual(prevInstitutionRef.current, currentInstitution);
|
|
50
|
+
const hasChanges = connectionIdChanged || connectorIdChanged || institutionChanged || !connectorCreatedRef.current;
|
|
51
|
+
// Update if there are changes, regardless of what the changes are
|
|
52
|
+
if (hasChanges) {
|
|
53
|
+
if (currentConnectionId) {
|
|
54
|
+
// Always use reconnect when connectionId is available
|
|
55
|
+
setConnector(Quiltt.reconnect(connectorId, {
|
|
56
|
+
connectionId: currentConnectionId
|
|
57
|
+
}));
|
|
58
|
+
} else {
|
|
59
|
+
// Use connect for new connections without connectionId
|
|
60
|
+
setConnector(Quiltt.connect(connectorId, {
|
|
61
|
+
institution: currentInstitution
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
// Update refs
|
|
65
|
+
connectorCreatedRef.current = true;
|
|
66
|
+
prevConnectionIdRef.current = currentConnectionId;
|
|
67
|
+
prevConnectorIdRef.current = connectorId;
|
|
68
|
+
prevInstitutionRef.current = currentInstitution;
|
|
69
|
+
}
|
|
70
|
+
}, [
|
|
71
|
+
connectorId,
|
|
72
|
+
options?.connectionId,
|
|
73
|
+
options?.institution,
|
|
74
|
+
status
|
|
75
|
+
]);
|
|
76
|
+
// Internal handlers to track connector state (stable references)
|
|
77
|
+
const handleOpen = useCallback((metadata)=>{
|
|
78
|
+
isConnectorOpenRef.current = true;
|
|
79
|
+
callbacksRef.current?.onOpen?.(metadata);
|
|
80
|
+
}, []);
|
|
81
|
+
const handleExit = useCallback((type, metadata)=>{
|
|
82
|
+
isConnectorOpenRef.current = false;
|
|
83
|
+
callbacksRef.current?.onExit?.(type, metadata);
|
|
84
|
+
}, []);
|
|
85
|
+
// Create stable wrapper functions for callbacks
|
|
86
|
+
const stableOnEvent = useCallback((type, metadata)=>{
|
|
87
|
+
callbacksRef.current?.onEvent?.(type, metadata);
|
|
88
|
+
}, []);
|
|
89
|
+
const stableOnLoad = useCallback((metadata)=>{
|
|
90
|
+
callbacksRef.current?.onLoad?.(metadata);
|
|
91
|
+
}, []);
|
|
92
|
+
const stableOnExitSuccess = useCallback((metadata)=>{
|
|
93
|
+
callbacksRef.current?.onExitSuccess?.(metadata);
|
|
94
|
+
}, []);
|
|
95
|
+
const stableOnExitAbort = useCallback((metadata)=>{
|
|
96
|
+
callbacksRef.current?.onExitAbort?.(metadata);
|
|
97
|
+
}, []);
|
|
98
|
+
const stableOnExitError = useCallback((metadata)=>{
|
|
99
|
+
callbacksRef.current?.onExitError?.(metadata);
|
|
100
|
+
}, []);
|
|
101
|
+
// Register event handlers (only re-runs when connector changes)
|
|
102
|
+
useEffect(()=>{
|
|
103
|
+
if (!connector) return;
|
|
104
|
+
// Capture which handlers we're registering to ensure proper cleanup
|
|
105
|
+
const registered = {
|
|
106
|
+
onEvent: callbacksRef.current?.onEvent ? stableOnEvent : null,
|
|
107
|
+
onOpen: callbacksRef.current?.onOpen ? handleOpen : null,
|
|
108
|
+
onLoad: callbacksRef.current?.onLoad ? stableOnLoad : null,
|
|
109
|
+
onExit: callbacksRef.current?.onExit ? handleExit : null,
|
|
110
|
+
onExitSuccess: callbacksRef.current?.onExitSuccess ? stableOnExitSuccess : null,
|
|
111
|
+
onExitAbort: callbacksRef.current?.onExitAbort ? stableOnExitAbort : null,
|
|
112
|
+
onExitError: callbacksRef.current?.onExitError ? stableOnExitError : null
|
|
113
|
+
};
|
|
114
|
+
if (registered.onEvent) connector.onEvent(registered.onEvent);
|
|
115
|
+
if (registered.onOpen) connector.onOpen(registered.onOpen);
|
|
116
|
+
if (registered.onLoad) connector.onLoad(registered.onLoad);
|
|
117
|
+
if (registered.onExit) connector.onExit(registered.onExit);
|
|
118
|
+
if (registered.onExitSuccess) connector.onExitSuccess(registered.onExitSuccess);
|
|
119
|
+
if (registered.onExitAbort) connector.onExitAbort(registered.onExitAbort);
|
|
120
|
+
if (registered.onExitError) connector.onExitError(registered.onExitError);
|
|
121
|
+
return ()=>{
|
|
122
|
+
if (registered.onEvent) connector.offEvent(registered.onEvent);
|
|
123
|
+
if (registered.onOpen) connector.offOpen(registered.onOpen);
|
|
124
|
+
if (registered.onLoad) connector.offLoad(registered.onLoad);
|
|
125
|
+
if (registered.onExit) connector.offExit(registered.onExit);
|
|
126
|
+
if (registered.onExitSuccess) connector.offExitSuccess(registered.onExitSuccess);
|
|
127
|
+
if (registered.onExitAbort) connector.offExitAbort(registered.onExitAbort);
|
|
128
|
+
if (registered.onExitError) connector.offExitError(registered.onExitError);
|
|
129
|
+
};
|
|
130
|
+
}, [
|
|
131
|
+
connector,
|
|
132
|
+
stableOnEvent,
|
|
133
|
+
handleOpen,
|
|
134
|
+
stableOnLoad,
|
|
135
|
+
handleExit,
|
|
136
|
+
stableOnExitSuccess,
|
|
137
|
+
stableOnExitAbort,
|
|
138
|
+
stableOnExitError
|
|
139
|
+
]);
|
|
140
|
+
// This is used to hide any potential race conditions from usage; allowing
|
|
141
|
+
// interaction before the script may have loaded.
|
|
142
|
+
useEffect(()=>{
|
|
143
|
+
if (connector && isOpening) {
|
|
144
|
+
setIsOpening(false);
|
|
145
|
+
connector.open();
|
|
146
|
+
}
|
|
147
|
+
}, [
|
|
148
|
+
connector,
|
|
149
|
+
isOpening
|
|
150
|
+
]);
|
|
151
|
+
// Cleanup effect - runs when the hook is torn down
|
|
152
|
+
useEffect(()=>{
|
|
153
|
+
return ()=>{
|
|
154
|
+
if (isConnectorOpenRef.current) {
|
|
155
|
+
console.error('[Quiltt] useQuilttConnector: Component unmounted while Connector is still open. ' + 'This may lead to memory leaks or unexpected behavior. ' + 'Ensure the Connector is properly closed before component unmount.');
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}, []);
|
|
159
|
+
const open = useCallback(()=>{
|
|
160
|
+
if (connectorId) {
|
|
161
|
+
setIsOpening(true);
|
|
162
|
+
} else {
|
|
163
|
+
throw new Error('Must provide `connectorId` to `open` Quiltt Connector with Method Call');
|
|
164
|
+
}
|
|
165
|
+
}, [
|
|
166
|
+
connectorId
|
|
167
|
+
]);
|
|
168
|
+
return {
|
|
169
|
+
open
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export { useQuilttConnector as u, version as v };
|
package/dist/{useQuilttInstitutions-12s-Djd5k9Ae.js → useQuilttInstitutions-12s-PJd2C3BF.js}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useMemo, useState,
|
|
2
|
+
import { useMemo, useState, useRef, useEffect, useCallback } from 'react';
|
|
3
3
|
import { InstitutionsAPI } from '@quiltt/core';
|
|
4
4
|
import { useDebounce } from 'use-debounce';
|
|
5
|
-
import { v as version } from './useQuilttConnector-12s-
|
|
5
|
+
import { v as version } from './useQuilttConnector-12s-BJljrsWT.js';
|
|
6
6
|
import { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
7
7
|
|
|
8
8
|
const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
@@ -27,6 +27,11 @@ const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
|
27
27
|
const [searchTerm] = useDebounce(searchTermInput, 350);
|
|
28
28
|
const [searchResults, setSearchResults] = useState([]);
|
|
29
29
|
const [isSearching, setIsSearching] = useState(false);
|
|
30
|
+
// Store callback in ref to maintain stable reference
|
|
31
|
+
const onErrorCallbackRef = useRef(onErrorCallback);
|
|
32
|
+
useEffect(()=>{
|
|
33
|
+
onErrorCallbackRef.current = onErrorCallback;
|
|
34
|
+
});
|
|
30
35
|
/**
|
|
31
36
|
* Start Search
|
|
32
37
|
* This function is used to initiate a search for institutions based on the provided term with
|
|
@@ -43,10 +48,8 @@ const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
|
43
48
|
const handleError = useCallback((message)=>{
|
|
44
49
|
const errorMessage = message || 'Unknown error occurred while searching institutions';
|
|
45
50
|
console.error('Quiltt Institutions Search Error:', errorMessage);
|
|
46
|
-
if (
|
|
47
|
-
}, [
|
|
48
|
-
onErrorCallback
|
|
49
|
-
]);
|
|
51
|
+
if (onErrorCallbackRef.current) onErrorCallbackRef.current(errorMessage);
|
|
52
|
+
}, []);
|
|
50
53
|
/**
|
|
51
54
|
* Run Search
|
|
52
55
|
* This effect will run when the searchTerm changes and is at least 2 characters long.
|
|
@@ -66,7 +69,7 @@ const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
|
66
69
|
}
|
|
67
70
|
}).catch((error)=>{
|
|
68
71
|
if (!abortController.signal.aborted) {
|
|
69
|
-
handleError(error
|
|
72
|
+
handleError(error?.message);
|
|
70
73
|
setIsSearching(false);
|
|
71
74
|
}
|
|
72
75
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useCallback } from 'react';
|
|
3
3
|
import { AuthAPI } from '@quiltt/core';
|
|
4
|
-
import { u as useImportSession, a as useIdentifySession, b as useAuthenticateSession, c as useRevokeSession } from './QuilttAuthProvider-12s-
|
|
4
|
+
import { u as useImportSession, a as useIdentifySession, b as useAuthenticateSession, c as useRevokeSession } from './QuilttAuthProvider-12s-D-Wr7LUT.js';
|
|
5
5
|
import { u as useQuilttSettings } from './useQuilttSettings-12s--rCJoNHD.js';
|
|
6
6
|
import { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quiltt/react",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.2",
|
|
4
4
|
"description": "React Components and Hooks for Quiltt Connector",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quiltt",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@apollo/client": "^3.14.0",
|
|
38
38
|
"use-debounce": "^10.0.4",
|
|
39
|
-
"@quiltt/core": "4.3.
|
|
39
|
+
"@quiltt/core": "4.3.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@biomejs/biome": "2.2.4",
|
|
@@ -127,10 +127,12 @@ export const QuilttButton = <T extends ElementType = 'button'>({
|
|
|
127
127
|
// 1. Pre-open validation
|
|
128
128
|
// 2. Preventing opening via event.preventDefault()
|
|
129
129
|
// 3. Setting up state before connector opens
|
|
130
|
-
|
|
130
|
+
onClick?.(event)
|
|
131
131
|
|
|
132
|
-
//
|
|
133
|
-
|
|
132
|
+
// Only open if event wasn't prevented
|
|
133
|
+
if (!event.defaultPrevented) {
|
|
134
|
+
open()
|
|
135
|
+
}
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
// Generate key for forced remounting if enabled, but respect user-provided key
|
|
@@ -11,6 +11,7 @@ import { cdnBase } from '@quiltt/core'
|
|
|
11
11
|
|
|
12
12
|
import { useQuilttSession } from '@/hooks/useQuilttSession'
|
|
13
13
|
import { useScript } from '@/hooks/useScript'
|
|
14
|
+
import { isDeepEqual } from '@/utils/isDeepEqual'
|
|
14
15
|
import { version } from '@/version'
|
|
15
16
|
|
|
16
17
|
declare const Quiltt: ConnectorSDK
|
|
@@ -33,8 +34,18 @@ export const useQuilttConnector = (
|
|
|
33
34
|
// Keep track of the previous connectionId to detect changes
|
|
34
35
|
const prevConnectionIdRef = useRef<string | undefined>(options?.connectionId)
|
|
35
36
|
const prevConnectorIdRef = useRef<string | undefined>(connectorId)
|
|
37
|
+
const prevInstitutionRef = useRef<string | undefined>(options?.institution)
|
|
36
38
|
const connectorCreatedRef = useRef<boolean>(false)
|
|
37
39
|
|
|
40
|
+
// Track whether the connector is currently open
|
|
41
|
+
const isConnectorOpenRef = useRef<boolean>(false)
|
|
42
|
+
|
|
43
|
+
// Store callbacks in refs to maintain stable references
|
|
44
|
+
const callbacksRef = useRef<ConnectorSDKConnectorOptions>(options || {})
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
callbacksRef.current = options || {}
|
|
47
|
+
})
|
|
48
|
+
|
|
38
49
|
// Set Session
|
|
39
50
|
// biome-ignore lint/correctness/useExhaustiveDependencies: trigger effects when script status changes too
|
|
40
51
|
useEffect(() => {
|
|
@@ -51,10 +62,15 @@ export const useQuilttConnector = (
|
|
|
51
62
|
const currentConnectionId = options?.connectionId
|
|
52
63
|
const currentInstitution = options?.institution
|
|
53
64
|
|
|
54
|
-
// Check for changes
|
|
65
|
+
// Check for changes - use deep equality for institution object
|
|
55
66
|
const connectionIdChanged = prevConnectionIdRef.current !== currentConnectionId
|
|
56
67
|
const connectorIdChanged = prevConnectorIdRef.current !== connectorId
|
|
57
|
-
const
|
|
68
|
+
const institutionChanged = !isDeepEqual(prevInstitutionRef.current, currentInstitution)
|
|
69
|
+
const hasChanges =
|
|
70
|
+
connectionIdChanged ||
|
|
71
|
+
connectorIdChanged ||
|
|
72
|
+
institutionChanged ||
|
|
73
|
+
!connectorCreatedRef.current
|
|
58
74
|
|
|
59
75
|
// Update if there are changes, regardless of what the changes are
|
|
60
76
|
if (hasChanges) {
|
|
@@ -70,49 +86,83 @@ export const useQuilttConnector = (
|
|
|
70
86
|
connectorCreatedRef.current = true
|
|
71
87
|
prevConnectionIdRef.current = currentConnectionId
|
|
72
88
|
prevConnectorIdRef.current = connectorId
|
|
89
|
+
prevInstitutionRef.current = currentInstitution
|
|
73
90
|
}
|
|
74
91
|
}, [connectorId, options?.connectionId, options?.institution, status])
|
|
75
92
|
|
|
76
|
-
//
|
|
93
|
+
// Internal handlers to track connector state (stable references)
|
|
94
|
+
const handleOpen = useCallback((metadata: any) => {
|
|
95
|
+
isConnectorOpenRef.current = true
|
|
96
|
+
callbacksRef.current?.onOpen?.(metadata)
|
|
97
|
+
}, [])
|
|
98
|
+
|
|
99
|
+
const handleExit = useCallback((type: any, metadata: any) => {
|
|
100
|
+
isConnectorOpenRef.current = false
|
|
101
|
+
callbacksRef.current?.onExit?.(type, metadata)
|
|
102
|
+
}, [])
|
|
103
|
+
|
|
104
|
+
// Create stable wrapper functions for callbacks
|
|
105
|
+
const stableOnEvent = useCallback((type: any, metadata: any) => {
|
|
106
|
+
callbacksRef.current?.onEvent?.(type, metadata)
|
|
107
|
+
}, [])
|
|
108
|
+
|
|
109
|
+
const stableOnLoad = useCallback((metadata: any) => {
|
|
110
|
+
callbacksRef.current?.onLoad?.(metadata)
|
|
111
|
+
}, [])
|
|
112
|
+
|
|
113
|
+
const stableOnExitSuccess = useCallback((metadata: any) => {
|
|
114
|
+
callbacksRef.current?.onExitSuccess?.(metadata)
|
|
115
|
+
}, [])
|
|
116
|
+
|
|
117
|
+
const stableOnExitAbort = useCallback((metadata: any) => {
|
|
118
|
+
callbacksRef.current?.onExitAbort?.(metadata)
|
|
119
|
+
}, [])
|
|
120
|
+
|
|
121
|
+
const stableOnExitError = useCallback((metadata: any) => {
|
|
122
|
+
callbacksRef.current?.onExitError?.(metadata)
|
|
123
|
+
}, [])
|
|
124
|
+
|
|
125
|
+
// Register event handlers (only re-runs when connector changes)
|
|
77
126
|
useEffect(() => {
|
|
78
127
|
if (!connector) return
|
|
79
128
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
129
|
+
// Capture which handlers we're registering to ensure proper cleanup
|
|
130
|
+
const registered = {
|
|
131
|
+
onEvent: callbacksRef.current?.onEvent ? stableOnEvent : null,
|
|
132
|
+
onOpen: callbacksRef.current?.onOpen ? handleOpen : null,
|
|
133
|
+
onLoad: callbacksRef.current?.onLoad ? stableOnLoad : null,
|
|
134
|
+
onExit: callbacksRef.current?.onExit ? handleExit : null,
|
|
135
|
+
onExitSuccess: callbacksRef.current?.onExitSuccess ? stableOnExitSuccess : null,
|
|
136
|
+
onExitAbort: callbacksRef.current?.onExitAbort ? stableOnExitAbort : null,
|
|
137
|
+
onExitError: callbacksRef.current?.onExitError ? stableOnExitError : null,
|
|
88
138
|
}
|
|
89
139
|
|
|
90
|
-
if (
|
|
91
|
-
if (
|
|
92
|
-
if (
|
|
93
|
-
if (
|
|
94
|
-
if (
|
|
95
|
-
if (
|
|
96
|
-
if (
|
|
140
|
+
if (registered.onEvent) connector.onEvent(registered.onEvent)
|
|
141
|
+
if (registered.onOpen) connector.onOpen(registered.onOpen)
|
|
142
|
+
if (registered.onLoad) connector.onLoad(registered.onLoad)
|
|
143
|
+
if (registered.onExit) connector.onExit(registered.onExit)
|
|
144
|
+
if (registered.onExitSuccess) connector.onExitSuccess(registered.onExitSuccess)
|
|
145
|
+
if (registered.onExitAbort) connector.onExitAbort(registered.onExitAbort)
|
|
146
|
+
if (registered.onExitError) connector.onExitError(registered.onExitError)
|
|
97
147
|
|
|
98
148
|
return () => {
|
|
99
|
-
if (
|
|
100
|
-
if (
|
|
101
|
-
if (
|
|
102
|
-
if (
|
|
103
|
-
if (
|
|
104
|
-
if (
|
|
105
|
-
if (
|
|
149
|
+
if (registered.onEvent) connector.offEvent(registered.onEvent)
|
|
150
|
+
if (registered.onOpen) connector.offOpen(registered.onOpen)
|
|
151
|
+
if (registered.onLoad) connector.offLoad(registered.onLoad)
|
|
152
|
+
if (registered.onExit) connector.offExit(registered.onExit)
|
|
153
|
+
if (registered.onExitSuccess) connector.offExitSuccess(registered.onExitSuccess)
|
|
154
|
+
if (registered.onExitAbort) connector.offExitAbort(registered.onExitAbort)
|
|
155
|
+
if (registered.onExitError) connector.offExitError(registered.onExitError)
|
|
106
156
|
}
|
|
107
157
|
}, [
|
|
108
158
|
connector,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
159
|
+
stableOnEvent,
|
|
160
|
+
handleOpen,
|
|
161
|
+
stableOnLoad,
|
|
162
|
+
handleExit,
|
|
163
|
+
stableOnExitSuccess,
|
|
164
|
+
stableOnExitAbort,
|
|
165
|
+
stableOnExitError,
|
|
116
166
|
])
|
|
117
167
|
|
|
118
168
|
// This is used to hide any potential race conditions from usage; allowing
|
|
@@ -124,6 +174,19 @@ export const useQuilttConnector = (
|
|
|
124
174
|
}
|
|
125
175
|
}, [connector, isOpening])
|
|
126
176
|
|
|
177
|
+
// Cleanup effect - runs when the hook is torn down
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
return () => {
|
|
180
|
+
if (isConnectorOpenRef.current) {
|
|
181
|
+
console.error(
|
|
182
|
+
'[Quiltt] useQuilttConnector: Component unmounted while Connector is still open. ' +
|
|
183
|
+
'This may lead to memory leaks or unexpected behavior. ' +
|
|
184
|
+
'Ensure the Connector is properly closed before component unmount.'
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}, [])
|
|
189
|
+
|
|
127
190
|
const open = useCallback(() => {
|
|
128
191
|
if (connectorId) {
|
|
129
192
|
setIsOpening(true)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
4
4
|
|
|
5
5
|
import type { ErrorData, InstitutionsData } from '@quiltt/core'
|
|
6
6
|
import { InstitutionsAPI } from '@quiltt/core'
|
|
@@ -53,6 +53,12 @@ export const useQuilttInstitutions: UseQuilttInstitutions = (connectorId, onErro
|
|
|
53
53
|
|
|
54
54
|
const [isSearching, setIsSearching] = useState(false)
|
|
55
55
|
|
|
56
|
+
// Store callback in ref to maintain stable reference
|
|
57
|
+
const onErrorCallbackRef = useRef(onErrorCallback)
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
onErrorCallbackRef.current = onErrorCallback
|
|
60
|
+
})
|
|
61
|
+
|
|
56
62
|
/**
|
|
57
63
|
* Start Search
|
|
58
64
|
* This function is used to initiate a search for institutions based on the provided term with
|
|
@@ -69,15 +75,12 @@ export const useQuilttInstitutions: UseQuilttInstitutions = (connectorId, onErro
|
|
|
69
75
|
setSearchTermInput(term)
|
|
70
76
|
}, [])
|
|
71
77
|
|
|
72
|
-
const handleError = useCallback(
|
|
73
|
-
|
|
74
|
-
const errorMessage = message || 'Unknown error occurred while searching institutions'
|
|
78
|
+
const handleError = useCallback((message: string) => {
|
|
79
|
+
const errorMessage = message || 'Unknown error occurred while searching institutions'
|
|
75
80
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
[onErrorCallback]
|
|
80
|
-
)
|
|
81
|
+
console.error('Quiltt Institutions Search Error:', errorMessage)
|
|
82
|
+
if (onErrorCallbackRef.current) onErrorCallbackRef.current(errorMessage)
|
|
83
|
+
}, [])
|
|
81
84
|
|
|
82
85
|
/**
|
|
83
86
|
* Run Search
|
|
@@ -104,7 +107,7 @@ export const useQuilttInstitutions: UseQuilttInstitutions = (connectorId, onErro
|
|
|
104
107
|
})
|
|
105
108
|
.catch((error) => {
|
|
106
109
|
if (!abortController.signal.aborted) {
|
|
107
|
-
handleError(error
|
|
110
|
+
handleError(error?.message)
|
|
108
111
|
setIsSearching(false)
|
|
109
112
|
}
|
|
110
113
|
})
|
|
@@ -9,8 +9,10 @@ import { InMemoryCache, QuilttClient } from '@quiltt/core'
|
|
|
9
9
|
import { useQuilttSession } from '@/hooks'
|
|
10
10
|
import { isDeepEqual } from '@/utils'
|
|
11
11
|
|
|
12
|
-
type QuilttAuthProviderProps = PropsWithChildren & {
|
|
13
|
-
/**
|
|
12
|
+
export type QuilttAuthProviderProps = PropsWithChildren & {
|
|
13
|
+
/** A custom QuilttClient instance to use instead of the default */
|
|
14
|
+
graphqlClient?: QuilttClient
|
|
15
|
+
/** The Quiltt Session token obtained from the server */
|
|
14
16
|
token?: string
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -20,33 +22,45 @@ type QuilttAuthProviderProps = PropsWithChildren & {
|
|
|
20
22
|
* into a loading state and the children are not rendered to prevent race conditions
|
|
21
23
|
* from triggering within the transitionary state.
|
|
22
24
|
*/
|
|
23
|
-
export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({
|
|
25
|
+
export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({
|
|
26
|
+
graphqlClient,
|
|
27
|
+
token,
|
|
28
|
+
children,
|
|
29
|
+
}) => {
|
|
24
30
|
const { session, importSession } = useQuilttSession()
|
|
25
31
|
const previousSessionRef = useRef(session)
|
|
32
|
+
const previousTokenRef = useRef<string | undefined>()
|
|
26
33
|
|
|
27
|
-
//
|
|
28
|
-
const
|
|
34
|
+
// Memoize the client to avoid unnecessary re-renders
|
|
35
|
+
const apolloClient = useMemo(
|
|
29
36
|
() =>
|
|
37
|
+
graphqlClient ||
|
|
30
38
|
new QuilttClient({
|
|
31
39
|
cache: new InMemoryCache(),
|
|
32
40
|
}),
|
|
33
|
-
[]
|
|
41
|
+
[graphqlClient]
|
|
34
42
|
)
|
|
35
43
|
|
|
36
|
-
// Import passed in token
|
|
44
|
+
// Import passed in token (only if value has changed)
|
|
37
45
|
useEffect(() => {
|
|
38
|
-
if (token
|
|
46
|
+
if (token && token !== previousTokenRef.current) {
|
|
47
|
+
importSession(token)
|
|
48
|
+
previousTokenRef.current = token
|
|
49
|
+
} else if (!token) {
|
|
50
|
+
// Reset ref when token becomes undefined to allow re-import of same token later
|
|
51
|
+
previousTokenRef.current = undefined
|
|
52
|
+
}
|
|
39
53
|
}, [token, importSession])
|
|
40
54
|
|
|
41
55
|
// Reset Client Store when session changes (using deep comparison)
|
|
42
56
|
useEffect(() => {
|
|
43
57
|
if (!isDeepEqual(session, previousSessionRef.current)) {
|
|
44
|
-
|
|
58
|
+
apolloClient.resetStore()
|
|
45
59
|
previousSessionRef.current = session
|
|
46
60
|
}
|
|
47
|
-
}, [session,
|
|
61
|
+
}, [session, apolloClient])
|
|
48
62
|
|
|
49
|
-
return <ApolloProvider client={
|
|
63
|
+
return <ApolloProvider client={apolloClient}>{children}</ApolloProvider>
|
|
50
64
|
}
|
|
51
65
|
|
|
52
66
|
export default QuilttAuthProvider
|
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
import type { FC
|
|
1
|
+
import type { FC } from 'react'
|
|
2
2
|
|
|
3
|
+
import type { QuilttAuthProviderProps } from './QuilttAuthProvider'
|
|
3
4
|
import { QuilttAuthProvider } from './QuilttAuthProvider'
|
|
5
|
+
import type { QuilttSettingsProviderProps } from './QuilttSettingsProvider'
|
|
4
6
|
import { QuilttSettingsProvider } from './QuilttSettingsProvider'
|
|
5
7
|
|
|
6
|
-
type QuilttProviderProps =
|
|
7
|
-
/** The client ID for the client-side Auth API */
|
|
8
|
-
clientId?: string
|
|
9
|
-
/** The Session token obtained from the server */
|
|
10
|
-
token?: string
|
|
11
|
-
}
|
|
8
|
+
type QuilttProviderProps = QuilttSettingsProviderProps & QuilttAuthProviderProps
|
|
12
9
|
|
|
13
|
-
export const QuilttProvider: FC<QuilttProviderProps> = ({
|
|
10
|
+
export const QuilttProvider: FC<QuilttProviderProps> = ({
|
|
11
|
+
clientId,
|
|
12
|
+
graphqlClient,
|
|
13
|
+
token,
|
|
14
|
+
children,
|
|
15
|
+
}) => {
|
|
14
16
|
return (
|
|
15
17
|
<QuilttSettingsProvider clientId={clientId}>
|
|
16
|
-
<QuilttAuthProvider token={token}
|
|
18
|
+
<QuilttAuthProvider token={token} graphqlClient={graphqlClient}>
|
|
19
|
+
{children}
|
|
20
|
+
</QuilttAuthProvider>
|
|
17
21
|
</QuilttSettingsProvider>
|
|
18
22
|
)
|
|
19
23
|
}
|
|
@@ -5,8 +5,8 @@ import { useState } from 'react'
|
|
|
5
5
|
|
|
6
6
|
import { QuilttSettings } from '@/contexts/QuilttSettings'
|
|
7
7
|
|
|
8
|
-
type QuilttSettingsProviderProps = PropsWithChildren & {
|
|
9
|
-
/** The Client ID to use for the
|
|
8
|
+
export type QuilttSettingsProviderProps = PropsWithChildren & {
|
|
9
|
+
/** The Client ID to use for the passwordless Auth API */
|
|
10
10
|
clientId?: string
|
|
11
11
|
}
|
|
12
12
|
|
package/src/utils/isDeepEqual.ts
CHANGED
|
@@ -31,8 +31,9 @@ export const isDeepEqual = (obj1: unknown, obj2: unknown): boolean => {
|
|
|
31
31
|
}
|
|
32
32
|
if (obj1 instanceof Set && obj2 instanceof Set) {
|
|
33
33
|
if (obj1.size !== obj2.size) return false
|
|
34
|
+
const arr2 = Array.from(obj2)
|
|
34
35
|
for (const item of obj1) {
|
|
35
|
-
if (!
|
|
36
|
+
if (!arr2.some((value) => isDeepEqual(item, value))) return false
|
|
36
37
|
}
|
|
37
38
|
return true
|
|
38
39
|
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
|
-
import { cdnBase } from '@quiltt/core';
|
|
4
|
-
import { u as useQuilttSession } from './useQuilttSession-12s-QlPCR8A-.js';
|
|
5
|
-
import { u as useScript } from './useScript-12s-JCgaTW9n.js';
|
|
6
|
-
|
|
7
|
-
var version = "4.3.0";
|
|
8
|
-
|
|
9
|
-
const useQuilttConnector = (connectorId, options)=>{
|
|
10
|
-
const status = useScript(`${cdnBase}/v1/connector.js?agent=react-${version}`, {
|
|
11
|
-
nonce: options?.nonce
|
|
12
|
-
});
|
|
13
|
-
// This ensures we're not destructuring `session` before the script has loaded
|
|
14
|
-
const useQuilttSessionReturn = useQuilttSession();
|
|
15
|
-
const session = useQuilttSessionReturn?.session || null;
|
|
16
|
-
const [connector, setConnector] = useState();
|
|
17
|
-
const [isOpening, setIsOpening] = useState(false);
|
|
18
|
-
// Keep track of the previous connectionId to detect changes
|
|
19
|
-
const prevConnectionIdRef = useRef(options?.connectionId);
|
|
20
|
-
const prevConnectorIdRef = useRef(connectorId);
|
|
21
|
-
const connectorCreatedRef = useRef(false);
|
|
22
|
-
// Set Session
|
|
23
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: trigger effects when script status changes too
|
|
24
|
-
useEffect(()=>{
|
|
25
|
-
if (typeof Quiltt === 'undefined') return;
|
|
26
|
-
Quiltt.authenticate(session?.token);
|
|
27
|
-
}, [
|
|
28
|
-
status,
|
|
29
|
-
session?.token
|
|
30
|
-
]);
|
|
31
|
-
// Set Connector
|
|
32
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: trigger effects when script status changes too
|
|
33
|
-
useEffect(()=>{
|
|
34
|
-
if (typeof Quiltt === 'undefined' || !connectorId) return;
|
|
35
|
-
const currentConnectionId = options?.connectionId;
|
|
36
|
-
const currentInstitution = options?.institution;
|
|
37
|
-
// Check for changes
|
|
38
|
-
const connectionIdChanged = prevConnectionIdRef.current !== currentConnectionId;
|
|
39
|
-
const connectorIdChanged = prevConnectorIdRef.current !== connectorId;
|
|
40
|
-
const hasChanges = connectionIdChanged || connectorIdChanged || !connectorCreatedRef.current;
|
|
41
|
-
// Update if there are changes, regardless of what the changes are
|
|
42
|
-
if (hasChanges) {
|
|
43
|
-
if (currentConnectionId) {
|
|
44
|
-
// Always use reconnect when connectionId is available
|
|
45
|
-
setConnector(Quiltt.reconnect(connectorId, {
|
|
46
|
-
connectionId: currentConnectionId
|
|
47
|
-
}));
|
|
48
|
-
} else {
|
|
49
|
-
// Use connect for new connections without connectionId
|
|
50
|
-
setConnector(Quiltt.connect(connectorId, {
|
|
51
|
-
institution: currentInstitution
|
|
52
|
-
}));
|
|
53
|
-
}
|
|
54
|
-
// Update refs
|
|
55
|
-
connectorCreatedRef.current = true;
|
|
56
|
-
prevConnectionIdRef.current = currentConnectionId;
|
|
57
|
-
prevConnectorIdRef.current = connectorId;
|
|
58
|
-
}
|
|
59
|
-
}, [
|
|
60
|
-
connectorId,
|
|
61
|
-
options?.connectionId,
|
|
62
|
-
options?.institution,
|
|
63
|
-
status
|
|
64
|
-
]);
|
|
65
|
-
// Register event handlers
|
|
66
|
-
useEffect(()=>{
|
|
67
|
-
if (!connector) return;
|
|
68
|
-
const handlers = {
|
|
69
|
-
onEvent: options?.onEvent,
|
|
70
|
-
onOpen: options?.onOpen,
|
|
71
|
-
onLoad: options?.onLoad,
|
|
72
|
-
onExit: options?.onExit,
|
|
73
|
-
onExitSuccess: options?.onExitSuccess,
|
|
74
|
-
onExitAbort: options?.onExitAbort,
|
|
75
|
-
onExitError: options?.onExitError
|
|
76
|
-
};
|
|
77
|
-
if (handlers.onEvent) connector.onEvent(handlers.onEvent);
|
|
78
|
-
if (handlers.onOpen) connector.onOpen(handlers.onOpen);
|
|
79
|
-
if (handlers.onLoad) connector.onLoad(handlers.onLoad);
|
|
80
|
-
if (handlers.onExit) connector.onExit(handlers.onExit);
|
|
81
|
-
if (handlers.onExitSuccess) connector.onExitSuccess(handlers.onExitSuccess);
|
|
82
|
-
if (handlers.onExitAbort) connector.onExitAbort(handlers.onExitAbort);
|
|
83
|
-
if (handlers.onExitError) connector.onExitError(handlers.onExitError);
|
|
84
|
-
return ()=>{
|
|
85
|
-
if (handlers.onEvent) connector.offEvent(handlers.onEvent);
|
|
86
|
-
if (handlers.onOpen) connector.offOpen(handlers.onOpen);
|
|
87
|
-
if (handlers.onLoad) connector.offLoad(handlers.onLoad);
|
|
88
|
-
if (handlers.onExit) connector.offExit(handlers.onExit);
|
|
89
|
-
if (handlers.onExitSuccess) connector.offExitSuccess(handlers.onExitSuccess);
|
|
90
|
-
if (handlers.onExitAbort) connector.offExitAbort(handlers.onExitAbort);
|
|
91
|
-
if (handlers.onExitError) connector.offExitError(handlers.onExitError);
|
|
92
|
-
};
|
|
93
|
-
}, [
|
|
94
|
-
connector,
|
|
95
|
-
options?.onEvent,
|
|
96
|
-
options?.onOpen,
|
|
97
|
-
options?.onLoad,
|
|
98
|
-
options?.onExit,
|
|
99
|
-
options?.onExitSuccess,
|
|
100
|
-
options?.onExitAbort,
|
|
101
|
-
options?.onExitError
|
|
102
|
-
]);
|
|
103
|
-
// This is used to hide any potential race conditions from usage; allowing
|
|
104
|
-
// interaction before the script may have loaded.
|
|
105
|
-
useEffect(()=>{
|
|
106
|
-
if (connector && isOpening) {
|
|
107
|
-
setIsOpening(false);
|
|
108
|
-
connector.open();
|
|
109
|
-
}
|
|
110
|
-
}, [
|
|
111
|
-
connector,
|
|
112
|
-
isOpening
|
|
113
|
-
]);
|
|
114
|
-
const open = useCallback(()=>{
|
|
115
|
-
if (connectorId) {
|
|
116
|
-
setIsOpening(true);
|
|
117
|
-
} else {
|
|
118
|
-
throw new Error('Must provide `connectorId` to `open` Quiltt Connector with Method Call');
|
|
119
|
-
}
|
|
120
|
-
}, [
|
|
121
|
-
connectorId
|
|
122
|
-
]);
|
|
123
|
-
return {
|
|
124
|
-
open
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
export { useQuilttConnector as u, version as v };
|