@aglyn/plugins-outreach 1.0.0-beta.169 → 1.0.0-beta.171
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-outreach",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.171",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-next": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
37
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
38
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
39
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.171",
|
|
29
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.171",
|
|
30
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.171",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.171",
|
|
32
|
+
"@aglyn/shared-ui-next": "1.0.0-beta.171",
|
|
33
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.171",
|
|
34
|
+
"@aglyn/shared-util-http": "1.0.0-beta.171",
|
|
35
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.171",
|
|
36
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.171",
|
|
37
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.171",
|
|
38
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.171",
|
|
39
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.171",
|
|
40
40
|
"@swc/helpers": "0.5.23"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
@@ -17,6 +17,23 @@
|
|
|
17
17
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
18
|
import { useCallback, useEffect, useState } from "react";
|
|
19
19
|
import { OutreachRouteError } from "./use-outreach-api.js";
|
|
20
|
+
/**
|
|
21
|
+
* Whether two answers from the settings route say the same thing.
|
|
22
|
+
*
|
|
23
|
+
* Compared by their serialization rather than field by field: the document
|
|
24
|
+
* carries whatever the route returns, and a comparison that named today's
|
|
25
|
+
* fields would silently stop noticing a new one. Guarded, because a value
|
|
26
|
+
* that cannot be serialized must read as "different" — the safe direction,
|
|
27
|
+
* which merely costs a render.
|
|
28
|
+
*/ function sameSettings(left, right) {
|
|
29
|
+
if (left === right) return true;
|
|
30
|
+
if (!left || !right) return false;
|
|
31
|
+
try {
|
|
32
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
33
|
+
} catch (unused) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
20
37
|
/**
|
|
21
38
|
* The organization's compliance settings, read through `outreach/settings`
|
|
22
39
|
* (AGL-2980): the Compliance page edits them, and the sequence editor's
|
|
@@ -31,14 +48,37 @@ import { OutreachRouteError } from "./use-outreach-api.js";
|
|
|
31
48
|
useEffect(()=>{
|
|
32
49
|
if (!orgId) return undefined;
|
|
33
50
|
let current = true;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
/*
|
|
52
|
+
* THE SAME OBJECT WHEN NOTHING CHANGED (AGL-3279).
|
|
53
|
+
*
|
|
54
|
+
* This used to spread unconditionally, which stores a NEW state object
|
|
55
|
+
* on every run and re-renders even when the status is what it already
|
|
56
|
+
* was. That is harmless while the effect's dependencies are stable and
|
|
57
|
+
* a render loop the moment one of them is not — a re-render remakes
|
|
58
|
+
* the dependency, the effect runs, the state changes identity, and
|
|
59
|
+
* React stops it at "Maximum update depth exceeded" (the minified
|
|
60
|
+
* error #185 this page reported on 2026-09-21).
|
|
61
|
+
*
|
|
62
|
+
* Returning `previous` unchanged makes the hook immune to that: the
|
|
63
|
+
* effect may run as often as it likes without ever being the reason it
|
|
64
|
+
* runs again.
|
|
65
|
+
*/ setState((previous)=>{
|
|
66
|
+
const status = previous.settings ? previous.status : 'loading';
|
|
67
|
+
return status === previous.status ? previous : _extends({}, previous, {
|
|
68
|
+
status
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
api.readSettings().then((answer)=>current && setState((previous)=>// IDEMPOTENT (AGL-3279): a read that answers what the hook
|
|
72
|
+
// already holds leaves the state object alone. Identity is what
|
|
73
|
+
// a caller's effects and memos key on, so a fresh object for an
|
|
74
|
+
// unchanged answer is a re-render at best and, with an unstable
|
|
75
|
+
// dependency above, the render loop that ends in "Maximum
|
|
76
|
+
// update depth exceeded".
|
|
77
|
+
previous.status === 'ready' && previous.message === null && sameSettings(previous.settings, answer.settings) ? previous : {
|
|
78
|
+
status: 'ready',
|
|
79
|
+
settings: answer.settings,
|
|
80
|
+
message: null
|
|
81
|
+
})).catch((error)=>{
|
|
42
82
|
if (!current) return;
|
|
43
83
|
const refused = error instanceof OutreachRouteError && error.refused;
|
|
44
84
|
setState({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/use-outreach-settings.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { useCallback, useEffect, useState } from 'react'\nimport type { OutreachComplianceSettingsDocument } from '../model/outreach.types'\nimport { OutreachRouteError, type OutreachApi } from './use-outreach-api'\nimport type { OutreachLoadStatus } from './use-outreach-data'\n\nexport interface OutreachSettingsLoad {\n status: OutreachLoadStatus\n settings: OutreachComplianceSettingsDocument | null\n /** The route's sentence for an error or a refusal. */\n message: string | null\n /** Reads the settings again — after a save elsewhere, or to retry. */\n reload(): void\n}\n\n/**\n * The organization's compliance settings, read through `outreach/settings`\n * (AGL-2980): the Compliance page edits them, and the sequence editor's\n * preview prints the footer from them.\n */\nexport function useOutreachComplianceSettings(\n api: OutreachApi,\n orgId: string | null,\n): OutreachSettingsLoad {\n const [state, setState] = useState<Omit<OutreachSettingsLoad, 'reload'>>({\n status: 'loading',\n settings: null,\n message: null,\n })\n const [attempt, setAttempt] = useState(0)\n useEffect(() => {\n if (!orgId) return undefined\n let current = true\n setState((previous) =>
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/use-outreach-settings.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { useCallback, useEffect, useState } from 'react'\nimport type { OutreachComplianceSettingsDocument } from '../model/outreach.types'\nimport { OutreachRouteError, type OutreachApi } from './use-outreach-api'\nimport type { OutreachLoadStatus } from './use-outreach-data'\n\nexport interface OutreachSettingsLoad {\n status: OutreachLoadStatus\n settings: OutreachComplianceSettingsDocument | null\n /** The route's sentence for an error or a refusal. */\n message: string | null\n /** Reads the settings again — after a save elsewhere, or to retry. */\n reload(): void\n}\n\n/**\n * Whether two answers from the settings route say the same thing.\n *\n * Compared by their serialization rather than field by field: the document\n * carries whatever the route returns, and a comparison that named today's\n * fields would silently stop noticing a new one. Guarded, because a value\n * that cannot be serialized must read as \"different\" — the safe direction,\n * which merely costs a render.\n */\nfunction sameSettings(\n left: OutreachComplianceSettingsDocument | null,\n right: OutreachComplianceSettingsDocument | null,\n): boolean {\n if (left === right) return true\n if (!left || !right) return false\n try {\n return JSON.stringify(left) === JSON.stringify(right)\n } catch {\n return false\n }\n}\n\n/**\n * The organization's compliance settings, read through `outreach/settings`\n * (AGL-2980): the Compliance page edits them, and the sequence editor's\n * preview prints the footer from them.\n */\nexport function useOutreachComplianceSettings(\n api: OutreachApi,\n orgId: string | null,\n): OutreachSettingsLoad {\n const [state, setState] = useState<Omit<OutreachSettingsLoad, 'reload'>>({\n status: 'loading',\n settings: null,\n message: null,\n })\n const [attempt, setAttempt] = useState(0)\n useEffect(() => {\n if (!orgId) return undefined\n let current = true\n /*\n * THE SAME OBJECT WHEN NOTHING CHANGED (AGL-3279).\n *\n * This used to spread unconditionally, which stores a NEW state object\n * on every run and re-renders even when the status is what it already\n * was. That is harmless while the effect's dependencies are stable and\n * a render loop the moment one of them is not — a re-render remakes\n * the dependency, the effect runs, the state changes identity, and\n * React stops it at \"Maximum update depth exceeded\" (the minified\n * error #185 this page reported on 2026-09-21).\n *\n * Returning `previous` unchanged makes the hook immune to that: the\n * effect may run as often as it likes without ever being the reason it\n * runs again.\n */\n setState((previous) => {\n const status = previous.settings ? previous.status : 'loading'\n return status === previous.status ? previous : { ...previous, status }\n })\n api\n .readSettings()\n .then(\n (answer) =>\n current &&\n setState((previous) =>\n // IDEMPOTENT (AGL-3279): a read that answers what the hook\n // already holds leaves the state object alone. Identity is what\n // a caller's effects and memos key on, so a fresh object for an\n // unchanged answer is a re-render at best and, with an unstable\n // dependency above, the render loop that ends in \"Maximum\n // update depth exceeded\".\n previous.status === 'ready' &&\n previous.message === null &&\n sameSettings(previous.settings, answer.settings)\n ? previous\n : {\n status: 'ready',\n settings: answer.settings,\n message: null,\n },\n ),\n )\n .catch((error: unknown) => {\n if (!current) return\n const refused = error instanceof OutreachRouteError && error.refused\n setState({\n status: refused ? 'refused' : 'error',\n settings: null,\n message: (error as Error).message,\n })\n })\n return () => {\n current = false\n }\n }, [api, orgId, attempt])\n const reload = useCallback(() => setAttempt((count) => count + 1), [])\n return { ...state, reload }\n}\n"],"names":["useCallback","useEffect","useState","OutreachRouteError","sameSettings","left","right","JSON","stringify","useOutreachComplianceSettings","api","orgId","state","setState","status","settings","message","attempt","setAttempt","undefined","current","previous","readSettings","then","answer","catch","error","refused","reload","count"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAExD,SAASC,kBAAkB,QAA0B,wBAAoB;AAYzE;;;;;;;;CAQC,GACD,SAASC,aACPC,IAA+C,EAC/CC,KAAgD;IAEhD,IAAID,SAASC,OAAO,OAAO;IAC3B,IAAI,CAACD,QAAQ,CAACC,OAAO,OAAO;IAC5B,IAAI;QACF,OAAOC,KAAKC,SAAS,CAACH,UAAUE,KAAKC,SAAS,CAACF;IACjD,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASG,8BACdC,GAAgB,EAChBC,KAAoB;IAEpB,MAAM,CAACC,OAAOC,SAAS,GAAGX,SAA+C;QACvEY,QAAQ;QACRC,UAAU;QACVC,SAAS;IACX;IACA,MAAM,CAACC,SAASC,WAAW,GAAGhB,SAAS;IACvCD,UAAU;QACR,IAAI,CAACU,OAAO,OAAOQ;QACnB,IAAIC,UAAU;QACd;;;;;;;;;;;;;;KAcC,GACDP,SAAS,CAACQ;YACR,MAAMP,SAASO,SAASN,QAAQ,GAAGM,SAASP,MAAM,GAAG;YACrD,OAAOA,WAAWO,SAASP,MAAM,GAAGO,WAAW,aAAKA;gBAAUP;;QAChE;QACAJ,IACGY,YAAY,GACZC,IAAI,CACH,CAACC,SACCJ,WACAP,SAAS,CAACQ,WACR,2DAA2D;gBAC3D,gEAAgE;gBAChE,gEAAgE;gBAChE,gEAAgE;gBAChE,0DAA0D;gBAC1D,0BAA0B;gBAC1BA,SAASP,MAAM,KAAK,WACpBO,SAASL,OAAO,KAAK,QACrBZ,aAAaiB,SAASN,QAAQ,EAAES,OAAOT,QAAQ,IAC3CM,WACA;oBACEP,QAAQ;oBACRC,UAAUS,OAAOT,QAAQ;oBACzBC,SAAS;gBACX,IAGTS,KAAK,CAAC,CAACC;YACN,IAAI,CAACN,SAAS;YACd,MAAMO,UAAUD,iBAAiBvB,sBAAsBuB,MAAMC,OAAO;YACpEd,SAAS;gBACPC,QAAQa,UAAU,YAAY;gBAC9BZ,UAAU;gBACVC,SAAS,AAACU,MAAgBV,OAAO;YACnC;QACF;QACF,OAAO;YACLI,UAAU;QACZ;IACF,GAAG;QAACV;QAAKC;QAAOM;KAAQ;IACxB,MAAMW,SAAS5B,YAAY,IAAMkB,WAAW,CAACW,QAAUA,QAAQ,IAAI,EAAE;IACrE,OAAO,aAAKjB;QAAOgB;;AACrB"}
|