@planningcenter/chat-react-native 3.34.0 → 3.35.0-rc.0
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/build/components/page/error_boundary.d.ts +1 -1
- package/build/index.d.ts +3 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -2
- package/build/index.js.map +1 -1
- package/build/utils/host_bridge.d.ts +20 -0
- package/build/utils/host_bridge.d.ts.map +1 -0
- package/build/utils/host_bridge.js +46 -0
- package/build/utils/host_bridge.js.map +1 -0
- package/build/utils/index.d.ts +10 -9
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +10 -9
- package/build/utils/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/utils/host_bridge.test.ts +164 -0
- package/src/index.tsx +3 -2
- package/src/utils/host_bridge.ts +72 -0
- package/src/utils/index.ts +10 -9
|
@@ -11,7 +11,7 @@ declare class ErrorBoundary extends React.Component<PropsWithChildren<{
|
|
|
11
11
|
componentDidCatch(error: any): void;
|
|
12
12
|
handleError(error: any): void;
|
|
13
13
|
handleReset: () => void;
|
|
14
|
-
render(): string | number | bigint | boolean |
|
|
14
|
+
render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
|
|
15
15
|
}
|
|
16
16
|
export default ErrorBoundary;
|
|
17
17
|
//# sourceMappingURL=error_boundary.d.ts.map
|
package/build/index.d.ts
CHANGED
|
@@ -5,13 +5,14 @@ export * from './contexts/chat_context';
|
|
|
5
5
|
export * from './contexts/session_context';
|
|
6
6
|
export * from './navigation';
|
|
7
7
|
export { ScreenLayout } from './navigation/screenLayout';
|
|
8
|
+
export { CustomEvent } from './polyfills/events/CustomEvent';
|
|
9
|
+
export { default as Event } from './polyfills/events/Event';
|
|
8
10
|
export * from './screens';
|
|
9
11
|
export { AgeCheckUnderageScreen } from './screens/age_check/age_check_underage_screen';
|
|
10
12
|
export * from './types';
|
|
11
13
|
export { platformFontWeightBold, Session, TemporaryDefaultColorsType, Uri } from './utils';
|
|
12
14
|
export * from './utils/client';
|
|
15
|
+
export * from './utils/host_bridge';
|
|
13
16
|
export * from './utils/native_adapters';
|
|
14
17
|
export { ResponseError } from './utils/response_error';
|
|
15
|
-
export { default as Event } from './polyfills/events/Event';
|
|
16
|
-
export { CustomEvent } from './polyfills/events/CustomEvent';
|
|
17
18
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACvF,cAAc,yBAAyB,CAAA;AACvC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,cAAc,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAA;AACtF,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,0BAA0B,EAAE,GAAG,EAAE,MAAM,SAAS,CAAA;AAC1F,cAAc,gBAAgB,CAAA;AAC9B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACvF,cAAc,yBAAyB,CAAA;AACvC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,cAAc,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AAC5D,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAC3D,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAA;AACtF,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,0BAA0B,EAAE,GAAG,EAAE,MAAM,SAAS,CAAA;AAC1F,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -5,13 +5,14 @@ export * from './contexts/chat_context';
|
|
|
5
5
|
export * from './contexts/session_context';
|
|
6
6
|
export * from './navigation';
|
|
7
7
|
export { ScreenLayout } from './navigation/screenLayout';
|
|
8
|
+
export { CustomEvent } from './polyfills/events/CustomEvent';
|
|
9
|
+
export { default as Event } from './polyfills/events/Event';
|
|
8
10
|
export * from './screens';
|
|
9
11
|
export { AgeCheckUnderageScreen } from './screens/age_check/age_check_underage_screen'; // TODO: add to barrel
|
|
10
12
|
export * from './types';
|
|
11
13
|
export { platformFontWeightBold, Session, Uri } from './utils';
|
|
12
14
|
export * from './utils/client';
|
|
15
|
+
export * from './utils/host_bridge';
|
|
13
16
|
export * from './utils/native_adapters';
|
|
14
17
|
export { ResponseError } from './utils/response_error';
|
|
15
|
-
export { default as Event } from './polyfills/events/Event';
|
|
16
|
-
export { CustomEvent } from './polyfills/events/CustomEvent';
|
|
17
18
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACvF,cAAc,yBAAyB,CAAA;AACvC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,cAAc,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAA,CAAC,sBAAsB;AAC7G,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAA8B,GAAG,EAAE,MAAM,SAAS,CAAA;AAC1F,cAAc,gBAAgB,CAAA;AAC9B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACvF,cAAc,yBAAyB,CAAA;AACvC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,cAAc,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AAC5D,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAC3D,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAA,CAAC,sBAAsB;AAC7G,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAA8B,GAAG,EAAE,MAAM,SAAS,CAAA;AAC1F,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA","sourcesContent":["import './icons/font_awesome'\n\nexport { GroupConversations } from './components'\nexport { ApiProvider, chatQueryClient, useFocusManager } from './contexts/api_provider'\nexport * from './contexts/chat_context'\nexport * from './contexts/session_context'\nexport * from './navigation'\nexport { ScreenLayout } from './navigation/screenLayout'\nexport { CustomEvent } from './polyfills/events/CustomEvent'\nexport { default as Event } from './polyfills/events/Event'\nexport * from './screens'\nexport { AgeCheckUnderageScreen } from './screens/age_check/age_check_underage_screen' // TODO: add to barrel\nexport * from './types'\nexport { platformFontWeightBold, Session, TemporaryDefaultColorsType, Uri } from './utils'\nexport * from './utils/client'\nexport * from './utils/host_bridge'\nexport * from './utils/native_adapters'\nexport { ResponseError } from './utils/response_error'\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type HostBridgeMessage = Record<string, unknown> & {
|
|
2
|
+
type?: unknown;
|
|
3
|
+
};
|
|
4
|
+
export type HostBridgeSubscription = {
|
|
5
|
+
remove: () => void;
|
|
6
|
+
};
|
|
7
|
+
export type HostBridgeMessaging = {
|
|
8
|
+
addMessageListener: (handler: (event: HostBridgeMessage) => void) => HostBridgeSubscription;
|
|
9
|
+
sendMessage: (message: HostBridgeMessage) => void;
|
|
10
|
+
};
|
|
11
|
+
export declare const createHostBridge: <TRequest extends string = string, TEvent extends {
|
|
12
|
+
type: string;
|
|
13
|
+
} = {
|
|
14
|
+
type: string;
|
|
15
|
+
}>(messaging: HostBridgeMessaging) => {
|
|
16
|
+
request: <T extends Record<string, unknown>>(type: TRequest, payload?: Record<string, unknown>) => Promise<T>;
|
|
17
|
+
send: (type: TRequest, payload?: Record<string, unknown>) => void;
|
|
18
|
+
subscribe: <E extends TEvent>(eventType: E["type"], handler: (event: E) => void) => (() => void);
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=host_bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host_bridge.d.ts","sourceRoot":"","sources":["../../src/utils/host_bridge.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAE5E,MAAM,MAAM,sBAAsB,GAAG;IAAE,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CAAA;AAE3D,MAAM,MAAM,mBAAmB,GAAG;IAChC,kBAAkB,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,KAAK,sBAAsB,CAAA;IAC3F,WAAW,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAClD,CAAA;AAOD,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,SAAS,MAAM,GAAG,MAAM,EAChC,MAAM,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAElD,WAAW,mBAAmB;cAsBlB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QACnC,QAAQ,YACL,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC/B,OAAO,CAAC,CAAC,CAAC;iBAaA,QAAQ,YAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAQ,IAAI;gBAKvD,CAAC,SAAS,MAAM,aACf,CAAC,CAAC,MAAM,CAAC,WACX,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KAC1B,CAAC,MAAM,IAAI,CAAC;CAOlB,CAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const createIdGenerator = () => {
|
|
2
|
+
let counter = 0;
|
|
3
|
+
return () => `req_${Date.now()}_${++counter}`;
|
|
4
|
+
};
|
|
5
|
+
export const createHostBridge = (messaging) => {
|
|
6
|
+
const pending = new Map();
|
|
7
|
+
const nextRequestId = createIdGenerator();
|
|
8
|
+
messaging.addMessageListener(event => {
|
|
9
|
+
const id = typeof event?.requestId === 'string' ? event.requestId : undefined;
|
|
10
|
+
if (!id)
|
|
11
|
+
return;
|
|
12
|
+
const entry = pending.get(id);
|
|
13
|
+
if (!entry)
|
|
14
|
+
return;
|
|
15
|
+
pending.delete(id);
|
|
16
|
+
if (event.error)
|
|
17
|
+
entry.reject(new Error(String(event.error)));
|
|
18
|
+
else
|
|
19
|
+
entry.resolve(event);
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
request: (type, payload = {}) => {
|
|
23
|
+
const requestId = nextRequestId();
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
pending.set(requestId, {
|
|
26
|
+
resolve: value => resolve(value),
|
|
27
|
+
reject,
|
|
28
|
+
});
|
|
29
|
+
// Spread payload first so it can't override the routing keys.
|
|
30
|
+
messaging.sendMessage({ ...payload, type, requestId });
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
send: (type, payload = {}) => {
|
|
34
|
+
// Spread payload first so it can't override the type.
|
|
35
|
+
messaging.sendMessage({ ...payload, type });
|
|
36
|
+
},
|
|
37
|
+
subscribe: (eventType, handler) => {
|
|
38
|
+
const sub = messaging.addMessageListener(event => {
|
|
39
|
+
if (event?.type === eventType)
|
|
40
|
+
handler(event);
|
|
41
|
+
});
|
|
42
|
+
return () => sub.remove();
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=host_bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host_bridge.js","sourceRoot":"","sources":["../../src/utils/host_bridge.ts"],"names":[],"mappings":"AASA,MAAM,iBAAiB,GAAG,GAAG,EAAE;IAC7B,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,OAAO,GAAG,EAAE,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;AAC/C,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAI9B,SAA8B,EAC9B,EAAE;IAKF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAA;IAC3C,MAAM,aAAa,GAAG,iBAAiB,EAAE,CAAA;IAEzC,SAAS,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE;QACnC,MAAM,EAAE,GAAG,OAAO,KAAK,EAAE,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAA;QAC7E,IAAI,CAAC,EAAE;YAAE,OAAM;QAEf,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC7B,IAAI,CAAC,KAAK;YAAE,OAAM;QAElB,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAClB,IAAI,KAAK,CAAC,KAAK;YAAE,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;;YACxD,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC,CAAC,CAAA;IAEF,OAAO;QACL,OAAO,EAAE,CACP,IAAc,EACd,UAAmC,EAAE,EACzB,EAAE;YACd,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;YAEjC,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACxC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE;oBACrB,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,KAAU,CAAC;oBACrC,MAAM;iBACP,CAAC,CAAA;gBACF,8DAA8D;gBAC9D,SAAS,CAAC,WAAW,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA;YACxD,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,EAAE,CAAC,IAAc,EAAE,UAAmC,EAAE,EAAQ,EAAE;YACpE,sDAAsD;YACtD,SAAS,CAAC,WAAW,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QAC7C,CAAC;QAED,SAAS,EAAE,CACT,SAAoB,EACpB,OAA2B,EACb,EAAE;YAChB,MAAM,GAAG,GAAG,SAAS,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE;gBAC/C,IAAI,KAAK,EAAE,IAAI,KAAK,SAAS;oBAAE,OAAO,CAAC,KAAqB,CAAC,CAAA;YAC/D,CAAC,CAAC,CAAA;YACF,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,CAAA;QAC3B,CAAC;KACF,CAAA;AACH,CAAC,CAAA","sourcesContent":["export type HostBridgeMessage = Record<string, unknown> & { type?: unknown }\n\nexport type HostBridgeSubscription = { remove: () => void }\n\nexport type HostBridgeMessaging = {\n addMessageListener: (handler: (event: HostBridgeMessage) => void) => HostBridgeSubscription\n sendMessage: (message: HostBridgeMessage) => void\n}\n\nconst createIdGenerator = () => {\n let counter = 0\n return () => `req_${Date.now()}_${++counter}`\n}\n\nexport const createHostBridge = <\n TRequest extends string = string,\n TEvent extends { type: string } = { type: string },\n>(\n messaging: HostBridgeMessaging\n) => {\n type Resolver = {\n resolve: (value: Record<string, unknown>) => void\n reject: (error: Error) => void\n }\n const pending = new Map<string, Resolver>()\n const nextRequestId = createIdGenerator()\n\n messaging.addMessageListener(event => {\n const id = typeof event?.requestId === 'string' ? event.requestId : undefined\n if (!id) return\n\n const entry = pending.get(id)\n if (!entry) return\n\n pending.delete(id)\n if (event.error) entry.reject(new Error(String(event.error)))\n else entry.resolve(event)\n })\n\n return {\n request: <T extends Record<string, unknown>>(\n type: TRequest,\n payload: Record<string, unknown> = {}\n ): Promise<T> => {\n const requestId = nextRequestId()\n\n return new Promise<T>((resolve, reject) => {\n pending.set(requestId, {\n resolve: value => resolve(value as T),\n reject,\n })\n // Spread payload first so it can't override the routing keys.\n messaging.sendMessage({ ...payload, type, requestId })\n })\n },\n\n send: (type: TRequest, payload: Record<string, unknown> = {}): void => {\n // Spread payload first so it can't override the type.\n messaging.sendMessage({ ...payload, type })\n },\n\n subscribe: <E extends TEvent>(\n eventType: E['type'],\n handler: (event: E) => void\n ): (() => void) => {\n const sub = messaging.addMessageListener(event => {\n if (event?.type === eventType) handler(event as unknown as E)\n })\n return () => sub.remove()\n },\n }\n}\n"]}
|
package/build/utils/index.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './styles';
|
|
4
|
-
export * from './client';
|
|
5
|
-
export * from './uri';
|
|
1
|
+
export * from './assert_keys_are_numbers';
|
|
2
|
+
export * from './attachment_kind';
|
|
6
3
|
export * from './cache';
|
|
4
|
+
export * from './client';
|
|
5
|
+
export * from './convert_attachments_for_create';
|
|
6
|
+
export * from './destructure_chat_group_graph_id';
|
|
7
|
+
export * from './host_bridge';
|
|
7
8
|
export * from './native_adapters';
|
|
8
9
|
export * from './pluralize';
|
|
9
10
|
export * from './reaction_constants';
|
|
10
|
-
export * from './
|
|
11
|
-
export * from './
|
|
12
|
-
export * from './assert_keys_are_numbers';
|
|
13
|
-
export * from './attachment_kind';
|
|
11
|
+
export * from './session';
|
|
12
|
+
export * from './styles';
|
|
14
13
|
export * from './system_messages';
|
|
14
|
+
export * from './theme';
|
|
15
|
+
export * from './uri';
|
|
15
16
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,kCAAkC,CAAA;AAChD,cAAc,mCAAmC,CAAA;AACjD,cAAc,eAAe,CAAA;AAC7B,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA;AACpC,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA;AACjC,cAAc,SAAS,CAAA;AACvB,cAAc,OAAO,CAAA"}
|
package/build/utils/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './styles';
|
|
4
|
-
export * from './client';
|
|
5
|
-
export * from './uri';
|
|
1
|
+
export * from './assert_keys_are_numbers';
|
|
2
|
+
export * from './attachment_kind';
|
|
6
3
|
export * from './cache';
|
|
4
|
+
export * from './client';
|
|
5
|
+
export * from './convert_attachments_for_create';
|
|
6
|
+
export * from './destructure_chat_group_graph_id';
|
|
7
|
+
export * from './host_bridge';
|
|
7
8
|
export * from './native_adapters';
|
|
8
9
|
export * from './pluralize';
|
|
9
10
|
export * from './reaction_constants';
|
|
10
|
-
export * from './
|
|
11
|
-
export * from './
|
|
12
|
-
export * from './assert_keys_are_numbers';
|
|
13
|
-
export * from './attachment_kind';
|
|
11
|
+
export * from './session';
|
|
12
|
+
export * from './styles';
|
|
14
13
|
export * from './system_messages';
|
|
14
|
+
export * from './theme';
|
|
15
|
+
export * from './uri';
|
|
15
16
|
//# sourceMappingURL=index.js.map
|
package/build/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,kCAAkC,CAAA;AAChD,cAAc,mCAAmC,CAAA;AACjD,cAAc,eAAe,CAAA;AAC7B,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA;AACpC,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA;AACjC,cAAc,SAAS,CAAA;AACvB,cAAc,OAAO,CAAA","sourcesContent":["export * from './assert_keys_are_numbers'\nexport * from './attachment_kind'\nexport * from './cache'\nexport * from './client'\nexport * from './convert_attachments_for_create'\nexport * from './destructure_chat_group_graph_id'\nexport * from './host_bridge'\nexport * from './native_adapters'\nexport * from './pluralize'\nexport * from './reaction_constants'\nexport * from './session'\nexport * from './styles'\nexport * from './system_messages'\nexport * from './theme'\nexport * from './uri'\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/chat-react-native",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.35.0-rc.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"react-native-url-polyfill": "^2.0.0",
|
|
66
66
|
"typescript": "~5.9.2"
|
|
67
67
|
},
|
|
68
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "dd9252ef05d2542745a31cf10b45083a9a365834"
|
|
69
69
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { createHostBridge, HostBridgeMessage, HostBridgeMessaging } from '../../utils/host_bridge'
|
|
2
|
+
|
|
3
|
+
const createFakeMessaging = () => {
|
|
4
|
+
const listeners: ((event: HostBridgeMessage) => void)[] = []
|
|
5
|
+
const sent: HostBridgeMessage[] = []
|
|
6
|
+
const remove = jest.fn()
|
|
7
|
+
|
|
8
|
+
const messaging: HostBridgeMessaging = {
|
|
9
|
+
addMessageListener: handler => {
|
|
10
|
+
listeners.push(handler)
|
|
11
|
+
return { remove }
|
|
12
|
+
},
|
|
13
|
+
sendMessage: message => {
|
|
14
|
+
sent.push(message)
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
messaging,
|
|
20
|
+
sent,
|
|
21
|
+
remove,
|
|
22
|
+
emit: (event: HostBridgeMessage) => {
|
|
23
|
+
listeners.forEach(l => l(event))
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('createHostBridge', () => {
|
|
29
|
+
describe('request', () => {
|
|
30
|
+
it('resolves with the host reply when requestId matches', async () => {
|
|
31
|
+
const fake = createFakeMessaging()
|
|
32
|
+
const bridge = createHostBridge(fake.messaging)
|
|
33
|
+
|
|
34
|
+
const promise = bridge.request<{ accessToken: string }>('refreshAccessToken')
|
|
35
|
+
const sent = fake.sent[0]
|
|
36
|
+
fake.emit({ requestId: sent.requestId as string, accessToken: 'abc' })
|
|
37
|
+
|
|
38
|
+
await expect(promise).resolves.toEqual({ requestId: sent.requestId, accessToken: 'abc' })
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('rejects with an Error when the host reply carries an error', async () => {
|
|
42
|
+
const fake = createFakeMessaging()
|
|
43
|
+
const bridge = createHostBridge(fake.messaging)
|
|
44
|
+
|
|
45
|
+
const promise = bridge.request('refreshAccessToken')
|
|
46
|
+
const sent = fake.sent[0]
|
|
47
|
+
fake.emit({ requestId: sent.requestId as string, error: 'boom' })
|
|
48
|
+
|
|
49
|
+
await expect(promise).rejects.toThrow('boom')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('ignores replies with non-matching requestIds', async () => {
|
|
53
|
+
const fake = createFakeMessaging()
|
|
54
|
+
const bridge = createHostBridge(fake.messaging)
|
|
55
|
+
|
|
56
|
+
const inFlight = bridge.request('refreshAccessToken')
|
|
57
|
+
fake.emit({ requestId: 'unrelated', accessToken: 'wrong' })
|
|
58
|
+
|
|
59
|
+
const sent = fake.sent[0]
|
|
60
|
+
fake.emit({ requestId: sent.requestId as string, accessToken: 'right' })
|
|
61
|
+
|
|
62
|
+
await expect(inFlight).resolves.toMatchObject({ accessToken: 'right' })
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('produces unique requestIds across calls', () => {
|
|
66
|
+
const fake = createFakeMessaging()
|
|
67
|
+
const bridge = createHostBridge(fake.messaging)
|
|
68
|
+
|
|
69
|
+
bridge.request('refreshAccessToken')
|
|
70
|
+
bridge.request('refreshAccessToken')
|
|
71
|
+
|
|
72
|
+
expect(fake.sent[0].requestId).not.toEqual(fake.sent[1].requestId)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('forwards the type and payload to sendMessage with a requestId', () => {
|
|
76
|
+
const fake = createFakeMessaging()
|
|
77
|
+
const bridge = createHostBridge(fake.messaging)
|
|
78
|
+
|
|
79
|
+
bridge.request('shouldRefreshAccessToken', { responseCode: 401 })
|
|
80
|
+
|
|
81
|
+
expect(fake.sent[0]).toMatchObject({
|
|
82
|
+
type: 'shouldRefreshAccessToken',
|
|
83
|
+
responseCode: 401,
|
|
84
|
+
})
|
|
85
|
+
expect(typeof fake.sent[0].requestId).toBe('string')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('payload cannot override the type or requestId routing keys', () => {
|
|
89
|
+
const fake = createFakeMessaging()
|
|
90
|
+
const bridge = createHostBridge(fake.messaging)
|
|
91
|
+
|
|
92
|
+
bridge.request('refreshAccessToken', {
|
|
93
|
+
type: 'somethingElse',
|
|
94
|
+
requestId: 'attacker-controlled',
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const sent = fake.sent[0]
|
|
98
|
+
expect(sent.type).toBe('refreshAccessToken')
|
|
99
|
+
expect(sent.requestId).not.toBe('attacker-controlled')
|
|
100
|
+
expect(typeof sent.requestId).toBe('string')
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
describe('send', () => {
|
|
105
|
+
it('sends type + payload without a requestId', () => {
|
|
106
|
+
const fake = createFakeMessaging()
|
|
107
|
+
const bridge = createHostBridge(fake.messaging)
|
|
108
|
+
|
|
109
|
+
bridge.send('setChannelId', { channelId: 'c-123' })
|
|
110
|
+
|
|
111
|
+
expect(fake.sent).toHaveLength(1)
|
|
112
|
+
expect(fake.sent[0]).toEqual({ type: 'setChannelId', channelId: 'c-123' })
|
|
113
|
+
expect(fake.sent[0].requestId).toBeUndefined()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('payload cannot override the type', () => {
|
|
117
|
+
const fake = createFakeMessaging()
|
|
118
|
+
const bridge = createHostBridge(fake.messaging)
|
|
119
|
+
|
|
120
|
+
bridge.send('setChannelId', { type: 'somethingElse', channelId: 'c-123' })
|
|
121
|
+
|
|
122
|
+
expect(fake.sent[0].type).toBe('setChannelId')
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('subscribe', () => {
|
|
127
|
+
type ConfigEvent = { type: 'chatChangeConfig'; config: { foo: string } }
|
|
128
|
+
type ClearCacheEvent = { type: 'chatClearCache' }
|
|
129
|
+
type AnyEvent = ConfigEvent | ClearCacheEvent
|
|
130
|
+
|
|
131
|
+
it('invokes the handler when the event type matches', () => {
|
|
132
|
+
const fake = createFakeMessaging()
|
|
133
|
+
const bridge = createHostBridge<string, AnyEvent>(fake.messaging)
|
|
134
|
+
const handler = jest.fn()
|
|
135
|
+
|
|
136
|
+
bridge.subscribe<ConfigEvent>('chatChangeConfig', handler)
|
|
137
|
+
fake.emit({ type: 'chatChangeConfig', config: { foo: 'bar' } })
|
|
138
|
+
|
|
139
|
+
expect(handler).toHaveBeenCalledWith({ type: 'chatChangeConfig', config: { foo: 'bar' } })
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('does not invoke the handler when the event type differs', () => {
|
|
143
|
+
const fake = createFakeMessaging()
|
|
144
|
+
const bridge = createHostBridge<string, AnyEvent>(fake.messaging)
|
|
145
|
+
const handler = jest.fn()
|
|
146
|
+
|
|
147
|
+
bridge.subscribe<ConfigEvent>('chatChangeConfig', handler)
|
|
148
|
+
fake.emit({ type: 'chatClearCache' })
|
|
149
|
+
|
|
150
|
+
expect(handler).not.toHaveBeenCalled()
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('returns an unsubscribe function that removes the listener', () => {
|
|
154
|
+
const fake = createFakeMessaging()
|
|
155
|
+
const bridge = createHostBridge<string, AnyEvent>(fake.messaging)
|
|
156
|
+
const handler = jest.fn()
|
|
157
|
+
|
|
158
|
+
const unsubscribe = bridge.subscribe<ConfigEvent>('chatChangeConfig', handler)
|
|
159
|
+
unsubscribe()
|
|
160
|
+
|
|
161
|
+
expect(fake.remove).toHaveBeenCalledTimes(1)
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
})
|
package/src/index.tsx
CHANGED
|
@@ -6,12 +6,13 @@ export * from './contexts/chat_context'
|
|
|
6
6
|
export * from './contexts/session_context'
|
|
7
7
|
export * from './navigation'
|
|
8
8
|
export { ScreenLayout } from './navigation/screenLayout'
|
|
9
|
+
export { CustomEvent } from './polyfills/events/CustomEvent'
|
|
10
|
+
export { default as Event } from './polyfills/events/Event'
|
|
9
11
|
export * from './screens'
|
|
10
12
|
export { AgeCheckUnderageScreen } from './screens/age_check/age_check_underage_screen' // TODO: add to barrel
|
|
11
13
|
export * from './types'
|
|
12
14
|
export { platformFontWeightBold, Session, TemporaryDefaultColorsType, Uri } from './utils'
|
|
13
15
|
export * from './utils/client'
|
|
16
|
+
export * from './utils/host_bridge'
|
|
14
17
|
export * from './utils/native_adapters'
|
|
15
18
|
export { ResponseError } from './utils/response_error'
|
|
16
|
-
export { default as Event } from './polyfills/events/Event'
|
|
17
|
-
export { CustomEvent } from './polyfills/events/CustomEvent'
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export type HostBridgeMessage = Record<string, unknown> & { type?: unknown }
|
|
2
|
+
|
|
3
|
+
export type HostBridgeSubscription = { remove: () => void }
|
|
4
|
+
|
|
5
|
+
export type HostBridgeMessaging = {
|
|
6
|
+
addMessageListener: (handler: (event: HostBridgeMessage) => void) => HostBridgeSubscription
|
|
7
|
+
sendMessage: (message: HostBridgeMessage) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const createIdGenerator = () => {
|
|
11
|
+
let counter = 0
|
|
12
|
+
return () => `req_${Date.now()}_${++counter}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const createHostBridge = <
|
|
16
|
+
TRequest extends string = string,
|
|
17
|
+
TEvent extends { type: string } = { type: string },
|
|
18
|
+
>(
|
|
19
|
+
messaging: HostBridgeMessaging
|
|
20
|
+
) => {
|
|
21
|
+
type Resolver = {
|
|
22
|
+
resolve: (value: Record<string, unknown>) => void
|
|
23
|
+
reject: (error: Error) => void
|
|
24
|
+
}
|
|
25
|
+
const pending = new Map<string, Resolver>()
|
|
26
|
+
const nextRequestId = createIdGenerator()
|
|
27
|
+
|
|
28
|
+
messaging.addMessageListener(event => {
|
|
29
|
+
const id = typeof event?.requestId === 'string' ? event.requestId : undefined
|
|
30
|
+
if (!id) return
|
|
31
|
+
|
|
32
|
+
const entry = pending.get(id)
|
|
33
|
+
if (!entry) return
|
|
34
|
+
|
|
35
|
+
pending.delete(id)
|
|
36
|
+
if (event.error) entry.reject(new Error(String(event.error)))
|
|
37
|
+
else entry.resolve(event)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
request: <T extends Record<string, unknown>>(
|
|
42
|
+
type: TRequest,
|
|
43
|
+
payload: Record<string, unknown> = {}
|
|
44
|
+
): Promise<T> => {
|
|
45
|
+
const requestId = nextRequestId()
|
|
46
|
+
|
|
47
|
+
return new Promise<T>((resolve, reject) => {
|
|
48
|
+
pending.set(requestId, {
|
|
49
|
+
resolve: value => resolve(value as T),
|
|
50
|
+
reject,
|
|
51
|
+
})
|
|
52
|
+
// Spread payload first so it can't override the routing keys.
|
|
53
|
+
messaging.sendMessage({ ...payload, type, requestId })
|
|
54
|
+
})
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
send: (type: TRequest, payload: Record<string, unknown> = {}): void => {
|
|
58
|
+
// Spread payload first so it can't override the type.
|
|
59
|
+
messaging.sendMessage({ ...payload, type })
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
subscribe: <E extends TEvent>(
|
|
63
|
+
eventType: E['type'],
|
|
64
|
+
handler: (event: E) => void
|
|
65
|
+
): (() => void) => {
|
|
66
|
+
const sub = messaging.addMessageListener(event => {
|
|
67
|
+
if (event?.type === eventType) handler(event as unknown as E)
|
|
68
|
+
})
|
|
69
|
+
return () => sub.remove()
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './styles'
|
|
4
|
-
export * from './client'
|
|
5
|
-
export * from './uri'
|
|
1
|
+
export * from './assert_keys_are_numbers'
|
|
2
|
+
export * from './attachment_kind'
|
|
6
3
|
export * from './cache'
|
|
4
|
+
export * from './client'
|
|
5
|
+
export * from './convert_attachments_for_create'
|
|
6
|
+
export * from './destructure_chat_group_graph_id'
|
|
7
|
+
export * from './host_bridge'
|
|
7
8
|
export * from './native_adapters'
|
|
8
9
|
export * from './pluralize'
|
|
9
10
|
export * from './reaction_constants'
|
|
10
|
-
export * from './
|
|
11
|
-
export * from './
|
|
12
|
-
export * from './assert_keys_are_numbers'
|
|
13
|
-
export * from './attachment_kind'
|
|
11
|
+
export * from './session'
|
|
12
|
+
export * from './styles'
|
|
14
13
|
export * from './system_messages'
|
|
14
|
+
export * from './theme'
|
|
15
|
+
export * from './uri'
|