@oxyhq/bloom 0.64.0 → 0.65.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.
Files changed (28) hide show
  1. package/lib/commonjs/connection-status/index.js +40 -0
  2. package/lib/commonjs/connection-status/index.js.map +1 -0
  3. package/lib/commonjs/connection-status/index.web.js +36 -0
  4. package/lib/commonjs/connection-status/index.web.js.map +1 -0
  5. package/lib/commonjs/connection-status/shared.js +68 -0
  6. package/lib/commonjs/connection-status/shared.js.map +1 -0
  7. package/lib/module/connection-status/index.js +34 -0
  8. package/lib/module/connection-status/index.js.map +1 -0
  9. package/lib/module/connection-status/index.web.js +31 -0
  10. package/lib/module/connection-status/index.web.js.map +1 -0
  11. package/lib/module/connection-status/shared.js +65 -0
  12. package/lib/module/connection-status/shared.js.map +1 -0
  13. package/lib/typescript/commonjs/connection-status/index.d.ts +8 -0
  14. package/lib/typescript/commonjs/connection-status/index.d.ts.map +1 -0
  15. package/lib/typescript/commonjs/connection-status/index.web.d.ts +8 -0
  16. package/lib/typescript/commonjs/connection-status/index.web.d.ts.map +1 -0
  17. package/lib/typescript/commonjs/connection-status/shared.d.ts +24 -0
  18. package/lib/typescript/commonjs/connection-status/shared.d.ts.map +1 -0
  19. package/lib/typescript/module/connection-status/index.d.ts +8 -0
  20. package/lib/typescript/module/connection-status/index.d.ts.map +1 -0
  21. package/lib/typescript/module/connection-status/index.web.d.ts +8 -0
  22. package/lib/typescript/module/connection-status/index.web.d.ts.map +1 -0
  23. package/lib/typescript/module/connection-status/shared.d.ts +24 -0
  24. package/lib/typescript/module/connection-status/shared.d.ts.map +1 -0
  25. package/package.json +24 -3
  26. package/src/connection-status/index.tsx +38 -0
  27. package/src/connection-status/index.web.tsx +36 -0
  28. package/src/connection-status/shared.ts +83 -0
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ConnectionStatusToasts = ConnectionStatusToasts;
7
+ var _react = require("react");
8
+ var _netinfo = _interopRequireDefault(require("@react-native-community/netinfo"));
9
+ var _shared = require("./shared.js");
10
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ /**
12
+ * Connection loss as a toast, not a layout-shifting banner.
13
+ *
14
+ * Every Oxy app needs this and none of them should hand-roll it: a coloured bar
15
+ * sliding in above the header pushes the whole screen down, has to be themed
16
+ * again in each app, and competes with the app's own chrome. The toast host is
17
+ * already mounted, already themed, already stacks.
18
+ *
19
+ * NATIVE variant: `@react-native-community/netinfo` (an Expo-managed dependency
20
+ * every Oxy app already ships) reports both link state and reachability. The
21
+ * web fork uses `navigator.onLine` instead so Vite/SPA consumers need no extra
22
+ * dependency at all.
23
+ */
24
+
25
+ /** Mount once at the app root. Renders nothing. */
26
+ function ConnectionStatusToasts(props = {}) {
27
+ const [isOnline, setIsOnline] = (0, _react.useState)(null);
28
+ (0, _react.useEffect)(() => {
29
+ // `isInternetReachable` is null until the first probe resolves; treat that
30
+ // as "connected as far as we know" so a captive-portal check never flashes
31
+ // an outage on launch.
32
+ return _netinfo.default.addEventListener(state => {
33
+ setIsOnline(Boolean(state.isConnected && state.isInternetReachable !== false));
34
+ });
35
+ }, []);
36
+ (0, _shared.useConnectionStatusToasts)(isOnline, props);
37
+ return null;
38
+ }
39
+ ConnectionStatusToasts.displayName = 'ConnectionStatusToasts';
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_netinfo","_interopRequireDefault","_shared","e","__esModule","default","ConnectionStatusToasts","props","isOnline","setIsOnline","useState","useEffect","NetInfo","addEventListener","state","Boolean","isConnected","isInternetReachable","useConnectionStatusToasts","displayName"],"sourceRoot":"../../../src","sources":["connection-status/index.tsx"],"mappings":";;;;;;AAaA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,QAAA,GAAAC,sBAAA,CAAAF,OAAA;AAEA,IAAAG,OAAA,GAAAH,OAAA;AAAuF,SAAAE,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAhBvF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAQA;AACO,SAASG,sBAAsBA,CAACC,KAAkC,GAAG,CAAC,CAAC,EAAQ;EACpF,MAAM,CAACC,QAAQ,EAAEC,WAAW,CAAC,GAAG,IAAAC,eAAQ,EAAiB,IAAI,CAAC;EAE9D,IAAAC,gBAAS,EAAC,MAAM;IACd;IACA;IACA;IACA,OAAOC,gBAAO,CAACC,gBAAgB,CAAEC,KAAK,IAAK;MACzCL,WAAW,CAACM,OAAO,CAACD,KAAK,CAACE,WAAW,IAAIF,KAAK,CAACG,mBAAmB,KAAK,KAAK,CAAC,CAAC;IAChF,CAAC,CAAC;EACJ,CAAC,EAAE,EAAE,CAAC;EAEN,IAAAC,iCAAyB,EAACV,QAAQ,EAAED,KAAK,CAAC;EAC1C,OAAO,IAAI;AACb;AAEAD,sBAAsB,CAACa,WAAW,GAAG,wBAAwB","ignoreList":[]}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ConnectionStatusToasts = ConnectionStatusToasts;
7
+ var _react = require("react");
8
+ var _shared = require("./shared.js");
9
+ /**
10
+ * Web variant — see `./index.tsx` for what this is and why it is a toast.
11
+ *
12
+ * `navigator.onLine` + the `online`/`offline` events are the browser's own
13
+ * signal, so a Vite/SPA consumer needs no native dependency to get the same
14
+ * behaviour. (The browser only knows about the link, not reachability; that is
15
+ * the honest limit of what a web app can detect without polling.)
16
+ */
17
+
18
+ /** Mount once at the app root. Renders nothing. */
19
+ function ConnectionStatusToasts(props = {}) {
20
+ const [isOnline, setIsOnline] = (0, _react.useState)(null);
21
+ (0, _react.useEffect)(() => {
22
+ if (typeof navigator === 'undefined' || typeof window === 'undefined') return undefined;
23
+ setIsOnline(navigator.onLine);
24
+ const update = () => setIsOnline(navigator.onLine);
25
+ window.addEventListener('online', update);
26
+ window.addEventListener('offline', update);
27
+ return () => {
28
+ window.removeEventListener('online', update);
29
+ window.removeEventListener('offline', update);
30
+ };
31
+ }, []);
32
+ (0, _shared.useConnectionStatusToasts)(isOnline, props);
33
+ return null;
34
+ }
35
+ ConnectionStatusToasts.displayName = 'ConnectionStatusToasts';
36
+ //# sourceMappingURL=index.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_shared","ConnectionStatusToasts","props","isOnline","setIsOnline","useState","useEffect","navigator","window","undefined","onLine","update","addEventListener","removeEventListener","useConnectionStatusToasts","displayName"],"sourceRoot":"../../../src","sources":["connection-status/index.web.tsx"],"mappings":";;;;;;AAQA,IAAAA,MAAA,GAAAC,OAAA;AAEA,IAAAC,OAAA,GAAAD,OAAA;AAVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAOA;AACO,SAASE,sBAAsBA,CAACC,KAAkC,GAAG,CAAC,CAAC,EAAQ;EACpF,MAAM,CAACC,QAAQ,EAAEC,WAAW,CAAC,GAAG,IAAAC,eAAQ,EAAiB,IAAI,CAAC;EAE9D,IAAAC,gBAAS,EAAC,MAAM;IACd,IAAI,OAAOC,SAAS,KAAK,WAAW,IAAI,OAAOC,MAAM,KAAK,WAAW,EAAE,OAAOC,SAAS;IAEvFL,WAAW,CAACG,SAAS,CAACG,MAAM,CAAC;IAC7B,MAAMC,MAAM,GAAGA,CAAA,KAAMP,WAAW,CAACG,SAAS,CAACG,MAAM,CAAC;IAClDF,MAAM,CAACI,gBAAgB,CAAC,QAAQ,EAAED,MAAM,CAAC;IACzCH,MAAM,CAACI,gBAAgB,CAAC,SAAS,EAAED,MAAM,CAAC;IAC1C,OAAO,MAAM;MACXH,MAAM,CAACK,mBAAmB,CAAC,QAAQ,EAAEF,MAAM,CAAC;MAC5CH,MAAM,CAACK,mBAAmB,CAAC,SAAS,EAAEF,MAAM,CAAC;IAC/C,CAAC;EACH,CAAC,EAAE,EAAE,CAAC;EAEN,IAAAG,iCAAyB,EAACX,QAAQ,EAAED,KAAK,CAAC;EAC1C,OAAO,IAAI;AACb;AAEAD,sBAAsB,CAACc,WAAW,GAAG,wBAAwB","ignoreList":[]}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useConnectionStatusToasts = useConnectionStatusToasts;
7
+ var _react = require("react");
8
+ var _index = require("../toast/index.js");
9
+ /** One id for the whole lifecycle, so the toast updates in place. */
10
+ const TOAST_ID = 'bloom-connection-status';
11
+ const DEFAULTS = {
12
+ offlineMessage: 'No internet connection',
13
+ reconnectingMessage: 'Reconnecting…',
14
+ restoredMessage: 'Back online',
15
+ reconnectingDelayMs: 3000,
16
+ restoredDurationMs: 2500
17
+ };
18
+
19
+ /**
20
+ * Drive the connection toast from an `isOnline` signal.
21
+ *
22
+ * Platform-agnostic half of `ConnectionStatusToasts`: the forks differ only in
23
+ * where the signal comes from (NetInfo on native, `navigator.onLine` on web),
24
+ * never in what the user sees.
25
+ *
26
+ * `isOnline === null` means "not determined yet" and shows nothing — a first
27
+ * paint must not accuse the user of being offline.
28
+ */
29
+ function useConnectionStatusToasts(isOnline, {
30
+ offlineMessage = DEFAULTS.offlineMessage,
31
+ reconnectingMessage = DEFAULTS.reconnectingMessage,
32
+ restoredMessage = DEFAULTS.restoredMessage,
33
+ reconnectingDelayMs = DEFAULTS.reconnectingDelayMs,
34
+ restoredDurationMs = DEFAULTS.restoredDurationMs
35
+ } = {}) {
36
+ // Whether an outage toast is currently up, so coming back online only
37
+ // announces itself to someone who saw the outage.
38
+ const wasOffline = (0, _react.useRef)(false);
39
+ (0, _react.useEffect)(() => {
40
+ if (isOnline === null) return undefined;
41
+ if (!isOnline) {
42
+ wasOffline.current = true;
43
+ // Persistent: an outage ends when the network says so, not on a timer.
44
+ _index.toast.error(offlineMessage, {
45
+ id: TOAST_ID,
46
+ duration: Infinity,
47
+ dismissible: false
48
+ });
49
+ const escalate = setTimeout(() => {
50
+ _index.toast.warning(reconnectingMessage, {
51
+ id: TOAST_ID,
52
+ duration: Infinity,
53
+ dismissible: false
54
+ });
55
+ }, reconnectingDelayMs);
56
+ return () => clearTimeout(escalate);
57
+ }
58
+ if (wasOffline.current) {
59
+ wasOffline.current = false;
60
+ _index.toast.success(restoredMessage, {
61
+ id: TOAST_ID,
62
+ duration: restoredDurationMs
63
+ });
64
+ }
65
+ return undefined;
66
+ }, [isOnline, offlineMessage, reconnectingMessage, restoredMessage, reconnectingDelayMs, restoredDurationMs]);
67
+ }
68
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_index","TOAST_ID","DEFAULTS","offlineMessage","reconnectingMessage","restoredMessage","reconnectingDelayMs","restoredDurationMs","useConnectionStatusToasts","isOnline","wasOffline","useRef","useEffect","undefined","current","toast","error","id","duration","Infinity","dismissible","escalate","setTimeout","warning","clearTimeout","success"],"sourceRoot":"../../../src","sources":["connection-status/shared.ts"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAEA,IAAAC,MAAA,GAAAD,OAAA;AAEA;AACA,MAAME,QAAQ,GAAG,yBAAyB;AAe1C,MAAMC,QAAQ,GAAG;EACfC,cAAc,EAAE,wBAAwB;EACxCC,mBAAmB,EAAE,eAAe;EACpCC,eAAe,EAAE,aAAa;EAC9BC,mBAAmB,EAAE,IAAI;EACzBC,kBAAkB,EAAE;AACtB,CAAU;;AAEV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,yBAAyBA,CACvCC,QAAwB,EACxB;EACEN,cAAc,GAAGD,QAAQ,CAACC,cAAc;EACxCC,mBAAmB,GAAGF,QAAQ,CAACE,mBAAmB;EAClDC,eAAe,GAAGH,QAAQ,CAACG,eAAe;EAC1CC,mBAAmB,GAAGJ,QAAQ,CAACI,mBAAmB;EAClDC,kBAAkB,GAAGL,QAAQ,CAACK;AACH,CAAC,GAAG,CAAC,CAAC,EAC7B;EACN;EACA;EACA,MAAMG,UAAU,GAAG,IAAAC,aAAM,EAAC,KAAK,CAAC;EAEhC,IAAAC,gBAAS,EAAC,MAAM;IACd,IAAIH,QAAQ,KAAK,IAAI,EAAE,OAAOI,SAAS;IAEvC,IAAI,CAACJ,QAAQ,EAAE;MACbC,UAAU,CAACI,OAAO,GAAG,IAAI;MACzB;MACAC,YAAK,CAACC,KAAK,CAACb,cAAc,EAAE;QAAEc,EAAE,EAAEhB,QAAQ;QAAEiB,QAAQ,EAAEC,QAAQ;QAAEC,WAAW,EAAE;MAAM,CAAC,CAAC;MACrF,MAAMC,QAAQ,GAAGC,UAAU,CAAC,MAAM;QAChCP,YAAK,CAACQ,OAAO,CAACnB,mBAAmB,EAAE;UACjCa,EAAE,EAAEhB,QAAQ;UACZiB,QAAQ,EAAEC,QAAQ;UAClBC,WAAW,EAAE;QACf,CAAC,CAAC;MACJ,CAAC,EAAEd,mBAAmB,CAAC;MACvB,OAAO,MAAMkB,YAAY,CAACH,QAAQ,CAAC;IACrC;IAEA,IAAIX,UAAU,CAACI,OAAO,EAAE;MACtBJ,UAAU,CAACI,OAAO,GAAG,KAAK;MAC1BC,YAAK,CAACU,OAAO,CAACpB,eAAe,EAAE;QAAEY,EAAE,EAAEhB,QAAQ;QAAEiB,QAAQ,EAAEX;MAAmB,CAAC,CAAC;IAChF;IACA,OAAOM,SAAS;EAClB,CAAC,EAAE,CACDJ,QAAQ,EACRN,cAAc,EACdC,mBAAmB,EACnBC,eAAe,EACfC,mBAAmB,EACnBC,kBAAkB,CACnB,CAAC;AACJ","ignoreList":[]}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Connection loss as a toast, not a layout-shifting banner.
5
+ *
6
+ * Every Oxy app needs this and none of them should hand-roll it: a coloured bar
7
+ * sliding in above the header pushes the whole screen down, has to be themed
8
+ * again in each app, and competes with the app's own chrome. The toast host is
9
+ * already mounted, already themed, already stacks.
10
+ *
11
+ * NATIVE variant: `@react-native-community/netinfo` (an Expo-managed dependency
12
+ * every Oxy app already ships) reports both link state and reachability. The
13
+ * web fork uses `navigator.onLine` instead so Vite/SPA consumers need no extra
14
+ * dependency at all.
15
+ */
16
+ import { useEffect, useState } from 'react';
17
+ import NetInfo from '@react-native-community/netinfo';
18
+ import { useConnectionStatusToasts } from "./shared.js";
19
+ /** Mount once at the app root. Renders nothing. */
20
+ export function ConnectionStatusToasts(props = {}) {
21
+ const [isOnline, setIsOnline] = useState(null);
22
+ useEffect(() => {
23
+ // `isInternetReachable` is null until the first probe resolves; treat that
24
+ // as "connected as far as we know" so a captive-portal check never flashes
25
+ // an outage on launch.
26
+ return NetInfo.addEventListener(state => {
27
+ setIsOnline(Boolean(state.isConnected && state.isInternetReachable !== false));
28
+ });
29
+ }, []);
30
+ useConnectionStatusToasts(isOnline, props);
31
+ return null;
32
+ }
33
+ ConnectionStatusToasts.displayName = 'ConnectionStatusToasts';
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useEffect","useState","NetInfo","useConnectionStatusToasts","ConnectionStatusToasts","props","isOnline","setIsOnline","addEventListener","state","Boolean","isConnected","isInternetReachable","displayName"],"sourceRoot":"../../../src","sources":["connection-status/index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,SAAS,EAAEC,QAAQ,QAAQ,OAAO;AAC3C,OAAOC,OAAO,MAAM,iCAAiC;AAErD,SAASC,yBAAyB,QAA0C,aAAU;AAItF;AACA,OAAO,SAASC,sBAAsBA,CAACC,KAAkC,GAAG,CAAC,CAAC,EAAQ;EACpF,MAAM,CAACC,QAAQ,EAAEC,WAAW,CAAC,GAAGN,QAAQ,CAAiB,IAAI,CAAC;EAE9DD,SAAS,CAAC,MAAM;IACd;IACA;IACA;IACA,OAAOE,OAAO,CAACM,gBAAgB,CAAEC,KAAK,IAAK;MACzCF,WAAW,CAACG,OAAO,CAACD,KAAK,CAACE,WAAW,IAAIF,KAAK,CAACG,mBAAmB,KAAK,KAAK,CAAC,CAAC;IAChF,CAAC,CAAC;EACJ,CAAC,EAAE,EAAE,CAAC;EAENT,yBAAyB,CAACG,QAAQ,EAAED,KAAK,CAAC;EAC1C,OAAO,IAAI;AACb;AAEAD,sBAAsB,CAACS,WAAW,GAAG,wBAAwB","ignoreList":[]}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Web variant — see `./index.tsx` for what this is and why it is a toast.
5
+ *
6
+ * `navigator.onLine` + the `online`/`offline` events are the browser's own
7
+ * signal, so a Vite/SPA consumer needs no native dependency to get the same
8
+ * behaviour. (The browser only knows about the link, not reachability; that is
9
+ * the honest limit of what a web app can detect without polling.)
10
+ */
11
+ import { useEffect, useState } from 'react';
12
+ import { useConnectionStatusToasts } from "./shared.js";
13
+ /** Mount once at the app root. Renders nothing. */
14
+ export function ConnectionStatusToasts(props = {}) {
15
+ const [isOnline, setIsOnline] = useState(null);
16
+ useEffect(() => {
17
+ if (typeof navigator === 'undefined' || typeof window === 'undefined') return undefined;
18
+ setIsOnline(navigator.onLine);
19
+ const update = () => setIsOnline(navigator.onLine);
20
+ window.addEventListener('online', update);
21
+ window.addEventListener('offline', update);
22
+ return () => {
23
+ window.removeEventListener('online', update);
24
+ window.removeEventListener('offline', update);
25
+ };
26
+ }, []);
27
+ useConnectionStatusToasts(isOnline, props);
28
+ return null;
29
+ }
30
+ ConnectionStatusToasts.displayName = 'ConnectionStatusToasts';
31
+ //# sourceMappingURL=index.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useEffect","useState","useConnectionStatusToasts","ConnectionStatusToasts","props","isOnline","setIsOnline","navigator","window","undefined","onLine","update","addEventListener","removeEventListener","displayName"],"sourceRoot":"../../../src","sources":["connection-status/index.web.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,SAAS,EAAEC,QAAQ,QAAQ,OAAO;AAE3C,SAASC,yBAAyB,QAA0C,aAAU;AAItF;AACA,OAAO,SAASC,sBAAsBA,CAACC,KAAkC,GAAG,CAAC,CAAC,EAAQ;EACpF,MAAM,CAACC,QAAQ,EAAEC,WAAW,CAAC,GAAGL,QAAQ,CAAiB,IAAI,CAAC;EAE9DD,SAAS,CAAC,MAAM;IACd,IAAI,OAAOO,SAAS,KAAK,WAAW,IAAI,OAAOC,MAAM,KAAK,WAAW,EAAE,OAAOC,SAAS;IAEvFH,WAAW,CAACC,SAAS,CAACG,MAAM,CAAC;IAC7B,MAAMC,MAAM,GAAGA,CAAA,KAAML,WAAW,CAACC,SAAS,CAACG,MAAM,CAAC;IAClDF,MAAM,CAACI,gBAAgB,CAAC,QAAQ,EAAED,MAAM,CAAC;IACzCH,MAAM,CAACI,gBAAgB,CAAC,SAAS,EAAED,MAAM,CAAC;IAC1C,OAAO,MAAM;MACXH,MAAM,CAACK,mBAAmB,CAAC,QAAQ,EAAEF,MAAM,CAAC;MAC5CH,MAAM,CAACK,mBAAmB,CAAC,SAAS,EAAEF,MAAM,CAAC;IAC/C,CAAC;EACH,CAAC,EAAE,EAAE,CAAC;EAENT,yBAAyB,CAACG,QAAQ,EAAED,KAAK,CAAC;EAC1C,OAAO,IAAI;AACb;AAEAD,sBAAsB,CAACW,WAAW,GAAG,wBAAwB","ignoreList":[]}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+
3
+ import { useEffect, useRef } from 'react';
4
+ import { toast } from "../toast/index.js";
5
+
6
+ /** One id for the whole lifecycle, so the toast updates in place. */
7
+ const TOAST_ID = 'bloom-connection-status';
8
+ const DEFAULTS = {
9
+ offlineMessage: 'No internet connection',
10
+ reconnectingMessage: 'Reconnecting…',
11
+ restoredMessage: 'Back online',
12
+ reconnectingDelayMs: 3000,
13
+ restoredDurationMs: 2500
14
+ };
15
+
16
+ /**
17
+ * Drive the connection toast from an `isOnline` signal.
18
+ *
19
+ * Platform-agnostic half of `ConnectionStatusToasts`: the forks differ only in
20
+ * where the signal comes from (NetInfo on native, `navigator.onLine` on web),
21
+ * never in what the user sees.
22
+ *
23
+ * `isOnline === null` means "not determined yet" and shows nothing — a first
24
+ * paint must not accuse the user of being offline.
25
+ */
26
+ export function useConnectionStatusToasts(isOnline, {
27
+ offlineMessage = DEFAULTS.offlineMessage,
28
+ reconnectingMessage = DEFAULTS.reconnectingMessage,
29
+ restoredMessage = DEFAULTS.restoredMessage,
30
+ reconnectingDelayMs = DEFAULTS.reconnectingDelayMs,
31
+ restoredDurationMs = DEFAULTS.restoredDurationMs
32
+ } = {}) {
33
+ // Whether an outage toast is currently up, so coming back online only
34
+ // announces itself to someone who saw the outage.
35
+ const wasOffline = useRef(false);
36
+ useEffect(() => {
37
+ if (isOnline === null) return undefined;
38
+ if (!isOnline) {
39
+ wasOffline.current = true;
40
+ // Persistent: an outage ends when the network says so, not on a timer.
41
+ toast.error(offlineMessage, {
42
+ id: TOAST_ID,
43
+ duration: Infinity,
44
+ dismissible: false
45
+ });
46
+ const escalate = setTimeout(() => {
47
+ toast.warning(reconnectingMessage, {
48
+ id: TOAST_ID,
49
+ duration: Infinity,
50
+ dismissible: false
51
+ });
52
+ }, reconnectingDelayMs);
53
+ return () => clearTimeout(escalate);
54
+ }
55
+ if (wasOffline.current) {
56
+ wasOffline.current = false;
57
+ toast.success(restoredMessage, {
58
+ id: TOAST_ID,
59
+ duration: restoredDurationMs
60
+ });
61
+ }
62
+ return undefined;
63
+ }, [isOnline, offlineMessage, reconnectingMessage, restoredMessage, reconnectingDelayMs, restoredDurationMs]);
64
+ }
65
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useEffect","useRef","toast","TOAST_ID","DEFAULTS","offlineMessage","reconnectingMessage","restoredMessage","reconnectingDelayMs","restoredDurationMs","useConnectionStatusToasts","isOnline","wasOffline","undefined","current","error","id","duration","Infinity","dismissible","escalate","setTimeout","warning","clearTimeout","success"],"sourceRoot":"../../../src","sources":["connection-status/shared.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,MAAM,QAAQ,OAAO;AAEzC,SAASC,KAAK,QAAQ,mBAAU;;AAEhC;AACA,MAAMC,QAAQ,GAAG,yBAAyB;AAe1C,MAAMC,QAAQ,GAAG;EACfC,cAAc,EAAE,wBAAwB;EACxCC,mBAAmB,EAAE,eAAe;EACpCC,eAAe,EAAE,aAAa;EAC9BC,mBAAmB,EAAE,IAAI;EACzBC,kBAAkB,EAAE;AACtB,CAAU;;AAEV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,yBAAyBA,CACvCC,QAAwB,EACxB;EACEN,cAAc,GAAGD,QAAQ,CAACC,cAAc;EACxCC,mBAAmB,GAAGF,QAAQ,CAACE,mBAAmB;EAClDC,eAAe,GAAGH,QAAQ,CAACG,eAAe;EAC1CC,mBAAmB,GAAGJ,QAAQ,CAACI,mBAAmB;EAClDC,kBAAkB,GAAGL,QAAQ,CAACK;AACH,CAAC,GAAG,CAAC,CAAC,EAC7B;EACN;EACA;EACA,MAAMG,UAAU,GAAGX,MAAM,CAAC,KAAK,CAAC;EAEhCD,SAAS,CAAC,MAAM;IACd,IAAIW,QAAQ,KAAK,IAAI,EAAE,OAAOE,SAAS;IAEvC,IAAI,CAACF,QAAQ,EAAE;MACbC,UAAU,CAACE,OAAO,GAAG,IAAI;MACzB;MACAZ,KAAK,CAACa,KAAK,CAACV,cAAc,EAAE;QAAEW,EAAE,EAAEb,QAAQ;QAAEc,QAAQ,EAAEC,QAAQ;QAAEC,WAAW,EAAE;MAAM,CAAC,CAAC;MACrF,MAAMC,QAAQ,GAAGC,UAAU,CAAC,MAAM;QAChCnB,KAAK,CAACoB,OAAO,CAAChB,mBAAmB,EAAE;UACjCU,EAAE,EAAEb,QAAQ;UACZc,QAAQ,EAAEC,QAAQ;UAClBC,WAAW,EAAE;QACf,CAAC,CAAC;MACJ,CAAC,EAAEX,mBAAmB,CAAC;MACvB,OAAO,MAAMe,YAAY,CAACH,QAAQ,CAAC;IACrC;IAEA,IAAIR,UAAU,CAACE,OAAO,EAAE;MACtBF,UAAU,CAACE,OAAO,GAAG,KAAK;MAC1BZ,KAAK,CAACsB,OAAO,CAACjB,eAAe,EAAE;QAAES,EAAE,EAAEb,QAAQ;QAAEc,QAAQ,EAAER;MAAmB,CAAC,CAAC;IAChF;IACA,OAAOI,SAAS;EAClB,CAAC,EAAE,CACDF,QAAQ,EACRN,cAAc,EACdC,mBAAmB,EACnBC,eAAe,EACfC,mBAAmB,EACnBC,kBAAkB,CACnB,CAAC;AACJ","ignoreList":[]}
@@ -0,0 +1,8 @@
1
+ import { type ConnectionStatusToastsProps } from './shared';
2
+ export type { ConnectionStatusToastsProps } from './shared';
3
+ /** Mount once at the app root. Renders nothing. */
4
+ export declare function ConnectionStatusToasts(props?: ConnectionStatusToastsProps): null;
5
+ export declare namespace ConnectionStatusToasts {
6
+ var displayName: string;
7
+ }
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/index.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAA6B,KAAK,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAEvF,YAAY,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAE5D,mDAAmD;AACnD,wBAAgB,sBAAsB,CAAC,KAAK,GAAE,2BAAgC,GAAG,IAAI,CAcpF;yBAde,sBAAsB"}
@@ -0,0 +1,8 @@
1
+ import { type ConnectionStatusToastsProps } from './shared';
2
+ export type { ConnectionStatusToastsProps } from './shared';
3
+ /** Mount once at the app root. Renders nothing. */
4
+ export declare function ConnectionStatusToasts(props?: ConnectionStatusToastsProps): null;
5
+ export declare namespace ConnectionStatusToasts {
6
+ var displayName: string;
7
+ }
8
+ //# sourceMappingURL=index.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/index.web.tsx"],"names":[],"mappings":"AAUA,OAAO,EAA6B,KAAK,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAEvF,YAAY,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAE5D,mDAAmD;AACnD,wBAAgB,sBAAsB,CAAC,KAAK,GAAE,2BAAgC,GAAG,IAAI,CAkBpF;yBAlBe,sBAAsB"}
@@ -0,0 +1,24 @@
1
+ export interface ConnectionStatusToastsProps {
2
+ /** Shown as soon as the device reports no connection. */
3
+ offlineMessage?: string;
4
+ /** Replaces `offlineMessage` once the outage has lasted `reconnectingDelayMs`. */
5
+ reconnectingMessage?: string;
6
+ /** Shown briefly when the connection comes back. */
7
+ restoredMessage?: string;
8
+ /** How long an outage must last before it reads as "reconnecting". */
9
+ reconnectingDelayMs?: number;
10
+ /** How long the restored toast stays up. */
11
+ restoredDurationMs?: number;
12
+ }
13
+ /**
14
+ * Drive the connection toast from an `isOnline` signal.
15
+ *
16
+ * Platform-agnostic half of `ConnectionStatusToasts`: the forks differ only in
17
+ * where the signal comes from (NetInfo on native, `navigator.onLine` on web),
18
+ * never in what the user sees.
19
+ *
20
+ * `isOnline === null` means "not determined yet" and shows nothing — a first
21
+ * paint must not accuse the user of being offline.
22
+ */
23
+ export declare function useConnectionStatusToasts(isOnline: boolean | null, { offlineMessage, reconnectingMessage, restoredMessage, reconnectingDelayMs, restoredDurationMs, }?: ConnectionStatusToastsProps): void;
24
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/shared.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,2BAA2B;IAC1C,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAUD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,OAAO,GAAG,IAAI,EACxB,EACE,cAAwC,EACxC,mBAAkD,EAClD,eAA0C,EAC1C,mBAAkD,EAClD,kBAAgD,GACjD,GAAE,2BAAgC,GAClC,IAAI,CAmCN"}
@@ -0,0 +1,8 @@
1
+ import { type ConnectionStatusToastsProps } from './shared';
2
+ export type { ConnectionStatusToastsProps } from './shared';
3
+ /** Mount once at the app root. Renders nothing. */
4
+ export declare function ConnectionStatusToasts(props?: ConnectionStatusToastsProps): null;
5
+ export declare namespace ConnectionStatusToasts {
6
+ var displayName: string;
7
+ }
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/index.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAA6B,KAAK,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAEvF,YAAY,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAE5D,mDAAmD;AACnD,wBAAgB,sBAAsB,CAAC,KAAK,GAAE,2BAAgC,GAAG,IAAI,CAcpF;yBAde,sBAAsB"}
@@ -0,0 +1,8 @@
1
+ import { type ConnectionStatusToastsProps } from './shared';
2
+ export type { ConnectionStatusToastsProps } from './shared';
3
+ /** Mount once at the app root. Renders nothing. */
4
+ export declare function ConnectionStatusToasts(props?: ConnectionStatusToastsProps): null;
5
+ export declare namespace ConnectionStatusToasts {
6
+ var displayName: string;
7
+ }
8
+ //# sourceMappingURL=index.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/index.web.tsx"],"names":[],"mappings":"AAUA,OAAO,EAA6B,KAAK,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAEvF,YAAY,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAE5D,mDAAmD;AACnD,wBAAgB,sBAAsB,CAAC,KAAK,GAAE,2BAAgC,GAAG,IAAI,CAkBpF;yBAlBe,sBAAsB"}
@@ -0,0 +1,24 @@
1
+ export interface ConnectionStatusToastsProps {
2
+ /** Shown as soon as the device reports no connection. */
3
+ offlineMessage?: string;
4
+ /** Replaces `offlineMessage` once the outage has lasted `reconnectingDelayMs`. */
5
+ reconnectingMessage?: string;
6
+ /** Shown briefly when the connection comes back. */
7
+ restoredMessage?: string;
8
+ /** How long an outage must last before it reads as "reconnecting". */
9
+ reconnectingDelayMs?: number;
10
+ /** How long the restored toast stays up. */
11
+ restoredDurationMs?: number;
12
+ }
13
+ /**
14
+ * Drive the connection toast from an `isOnline` signal.
15
+ *
16
+ * Platform-agnostic half of `ConnectionStatusToasts`: the forks differ only in
17
+ * where the signal comes from (NetInfo on native, `navigator.onLine` on web),
18
+ * never in what the user sees.
19
+ *
20
+ * `isOnline === null` means "not determined yet" and shows nothing — a first
21
+ * paint must not accuse the user of being offline.
22
+ */
23
+ export declare function useConnectionStatusToasts(isOnline: boolean | null, { offlineMessage, reconnectingMessage, restoredMessage, reconnectingDelayMs, restoredDurationMs, }?: ConnectionStatusToastsProps): void;
24
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/shared.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,2BAA2B;IAC1C,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAUD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,OAAO,GAAG,IAAI,EACxB,EACE,cAAwC,EACxC,mBAAkD,EAClD,eAA0C,EAC1C,mBAAkD,EAClD,kBAAgD,GACjD,GAAE,2BAAgC,GAClC,IAAI,CAmCN"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.64.0",
3
+ "version": "0.65.0",
4
4
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -148,6 +148,22 @@
148
148
  "default": "./lib/commonjs/portal/index.js"
149
149
  }
150
150
  },
151
+ "./connection-status": {
152
+ "react-native": "./src/connection-status/index.tsx",
153
+ "browser": {
154
+ "types": "./lib/typescript/module/connection-status/index.web.d.ts",
155
+ "import": "./lib/module/connection-status/index.web.js",
156
+ "require": "./lib/commonjs/connection-status/index.web.js"
157
+ },
158
+ "import": {
159
+ "types": "./lib/typescript/module/connection-status/index.d.ts",
160
+ "default": "./lib/module/connection-status/index.js"
161
+ },
162
+ "require": {
163
+ "types": "./lib/typescript/commonjs/connection-status/index.d.ts",
164
+ "default": "./lib/commonjs/connection-status/index.js"
165
+ }
166
+ },
151
167
  "./overlay": {
152
168
  "react-native": "./src/overlay/index.tsx",
153
169
  "import": {
@@ -1210,7 +1226,8 @@
1210
1226
  "storybook": "^10",
1211
1227
  "ts-jest": "^29.4.6",
1212
1228
  "typescript": "~5.9.2",
1213
- "vite": "^7"
1229
+ "vite": "^7",
1230
+ "@react-native-community/netinfo": "^12.0.1"
1214
1231
  },
1215
1232
  "peerDependencies": {
1216
1233
  "expo": "*",
@@ -1229,7 +1246,8 @@
1229
1246
  "react-native-safe-area-context": ">=5.0.0",
1230
1247
  "react-native-screens": ">=3.16.0",
1231
1248
  "react-native-svg": ">=13.0.0",
1232
- "nativewind": ">=5.0.0"
1249
+ "nativewind": ">=5.0.0",
1250
+ "@react-native-community/netinfo": ""
1233
1251
  },
1234
1252
  "peerDependenciesMeta": {
1235
1253
  "expo": {
@@ -1249,6 +1267,9 @@
1249
1267
  },
1250
1268
  "nativewind": {
1251
1269
  "optional": true
1270
+ },
1271
+ "@react-native-community/netinfo": {
1272
+ "optional": true
1252
1273
  }
1253
1274
  },
1254
1275
  "react-native-builder-bob": {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Connection loss as a toast, not a layout-shifting banner.
3
+ *
4
+ * Every Oxy app needs this and none of them should hand-roll it: a coloured bar
5
+ * sliding in above the header pushes the whole screen down, has to be themed
6
+ * again in each app, and competes with the app's own chrome. The toast host is
7
+ * already mounted, already themed, already stacks.
8
+ *
9
+ * NATIVE variant: `@react-native-community/netinfo` (an Expo-managed dependency
10
+ * every Oxy app already ships) reports both link state and reachability. The
11
+ * web fork uses `navigator.onLine` instead so Vite/SPA consumers need no extra
12
+ * dependency at all.
13
+ */
14
+ import { useEffect, useState } from 'react';
15
+ import NetInfo from '@react-native-community/netinfo';
16
+
17
+ import { useConnectionStatusToasts, type ConnectionStatusToastsProps } from './shared';
18
+
19
+ export type { ConnectionStatusToastsProps } from './shared';
20
+
21
+ /** Mount once at the app root. Renders nothing. */
22
+ export function ConnectionStatusToasts(props: ConnectionStatusToastsProps = {}): null {
23
+ const [isOnline, setIsOnline] = useState<boolean | null>(null);
24
+
25
+ useEffect(() => {
26
+ // `isInternetReachable` is null until the first probe resolves; treat that
27
+ // as "connected as far as we know" so a captive-portal check never flashes
28
+ // an outage on launch.
29
+ return NetInfo.addEventListener((state) => {
30
+ setIsOnline(Boolean(state.isConnected && state.isInternetReachable !== false));
31
+ });
32
+ }, []);
33
+
34
+ useConnectionStatusToasts(isOnline, props);
35
+ return null;
36
+ }
37
+
38
+ ConnectionStatusToasts.displayName = 'ConnectionStatusToasts';
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Web variant — see `./index.tsx` for what this is and why it is a toast.
3
+ *
4
+ * `navigator.onLine` + the `online`/`offline` events are the browser's own
5
+ * signal, so a Vite/SPA consumer needs no native dependency to get the same
6
+ * behaviour. (The browser only knows about the link, not reachability; that is
7
+ * the honest limit of what a web app can detect without polling.)
8
+ */
9
+ import { useEffect, useState } from 'react';
10
+
11
+ import { useConnectionStatusToasts, type ConnectionStatusToastsProps } from './shared';
12
+
13
+ export type { ConnectionStatusToastsProps } from './shared';
14
+
15
+ /** Mount once at the app root. Renders nothing. */
16
+ export function ConnectionStatusToasts(props: ConnectionStatusToastsProps = {}): null {
17
+ const [isOnline, setIsOnline] = useState<boolean | null>(null);
18
+
19
+ useEffect(() => {
20
+ if (typeof navigator === 'undefined' || typeof window === 'undefined') return undefined;
21
+
22
+ setIsOnline(navigator.onLine);
23
+ const update = () => setIsOnline(navigator.onLine);
24
+ window.addEventListener('online', update);
25
+ window.addEventListener('offline', update);
26
+ return () => {
27
+ window.removeEventListener('online', update);
28
+ window.removeEventListener('offline', update);
29
+ };
30
+ }, []);
31
+
32
+ useConnectionStatusToasts(isOnline, props);
33
+ return null;
34
+ }
35
+
36
+ ConnectionStatusToasts.displayName = 'ConnectionStatusToasts';
@@ -0,0 +1,83 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ import { toast } from '../toast';
4
+
5
+ /** One id for the whole lifecycle, so the toast updates in place. */
6
+ const TOAST_ID = 'bloom-connection-status';
7
+
8
+ export interface ConnectionStatusToastsProps {
9
+ /** Shown as soon as the device reports no connection. */
10
+ offlineMessage?: string;
11
+ /** Replaces `offlineMessage` once the outage has lasted `reconnectingDelayMs`. */
12
+ reconnectingMessage?: string;
13
+ /** Shown briefly when the connection comes back. */
14
+ restoredMessage?: string;
15
+ /** How long an outage must last before it reads as "reconnecting". */
16
+ reconnectingDelayMs?: number;
17
+ /** How long the restored toast stays up. */
18
+ restoredDurationMs?: number;
19
+ }
20
+
21
+ const DEFAULTS = {
22
+ offlineMessage: 'No internet connection',
23
+ reconnectingMessage: 'Reconnecting…',
24
+ restoredMessage: 'Back online',
25
+ reconnectingDelayMs: 3000,
26
+ restoredDurationMs: 2500,
27
+ } as const;
28
+
29
+ /**
30
+ * Drive the connection toast from an `isOnline` signal.
31
+ *
32
+ * Platform-agnostic half of `ConnectionStatusToasts`: the forks differ only in
33
+ * where the signal comes from (NetInfo on native, `navigator.onLine` on web),
34
+ * never in what the user sees.
35
+ *
36
+ * `isOnline === null` means "not determined yet" and shows nothing — a first
37
+ * paint must not accuse the user of being offline.
38
+ */
39
+ export function useConnectionStatusToasts(
40
+ isOnline: boolean | null,
41
+ {
42
+ offlineMessage = DEFAULTS.offlineMessage,
43
+ reconnectingMessage = DEFAULTS.reconnectingMessage,
44
+ restoredMessage = DEFAULTS.restoredMessage,
45
+ reconnectingDelayMs = DEFAULTS.reconnectingDelayMs,
46
+ restoredDurationMs = DEFAULTS.restoredDurationMs,
47
+ }: ConnectionStatusToastsProps = {},
48
+ ): void {
49
+ // Whether an outage toast is currently up, so coming back online only
50
+ // announces itself to someone who saw the outage.
51
+ const wasOffline = useRef(false);
52
+
53
+ useEffect(() => {
54
+ if (isOnline === null) return undefined;
55
+
56
+ if (!isOnline) {
57
+ wasOffline.current = true;
58
+ // Persistent: an outage ends when the network says so, not on a timer.
59
+ toast.error(offlineMessage, { id: TOAST_ID, duration: Infinity, dismissible: false });
60
+ const escalate = setTimeout(() => {
61
+ toast.warning(reconnectingMessage, {
62
+ id: TOAST_ID,
63
+ duration: Infinity,
64
+ dismissible: false,
65
+ });
66
+ }, reconnectingDelayMs);
67
+ return () => clearTimeout(escalate);
68
+ }
69
+
70
+ if (wasOffline.current) {
71
+ wasOffline.current = false;
72
+ toast.success(restoredMessage, { id: TOAST_ID, duration: restoredDurationMs });
73
+ }
74
+ return undefined;
75
+ }, [
76
+ isOnline,
77
+ offlineMessage,
78
+ reconnectingMessage,
79
+ restoredMessage,
80
+ reconnectingDelayMs,
81
+ restoredDurationMs,
82
+ ]);
83
+ }