@the-portland-company/shell 0.55.0 → 0.57.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/CHANGELOG.md +70 -0
- package/dist/app-chrome.cjs +412 -63
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +13 -0
- package/dist/app-chrome.d.ts +13 -0
- package/dist/app-chrome.js +136 -10
- package/dist/app-chrome.js.map +1 -1
- package/dist/{chunk-K5GWJSHE.js → chunk-TMOZTLUZ.js} +281 -58
- package/dist/chunk-TMOZTLUZ.js.map +1 -0
- package/dist/native.cjs +279 -54
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +6 -1
- package/dist/native.d.ts +6 -1
- package/dist/native.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-K5GWJSHE.js.map +0 -1
package/dist/app-chrome.d.cts
CHANGED
|
@@ -119,6 +119,19 @@ declare const POLITOGY_APPS: Record<PolitogyAppKey, {
|
|
|
119
119
|
origin: string;
|
|
120
120
|
storagePrefix: string;
|
|
121
121
|
mode?: PolitogyAppMode;
|
|
122
|
+
/**
|
|
123
|
+
* DUAL-MODE apps (today: a2p — Specs/A2P: "the SMS/MMS delivery engine
|
|
124
|
+
* for Relationship Mode and Campaign Mode") have no single fixed mode.
|
|
125
|
+
* `mode` above stays the fallback/default identity for any code that
|
|
126
|
+
* still reads `meta.mode` directly; FrameBody instead resolves the
|
|
127
|
+
* EFFECTIVE mode via `resolveActiveMode` (?mode= query → the
|
|
128
|
+
* `politogy_active_mode` cookie → `defaultMode`) and uses that for the
|
|
129
|
+
* nav tree, the Mode pill, and the brand lockup.
|
|
130
|
+
*/
|
|
131
|
+
dual?: {
|
|
132
|
+
modes: readonly StandardNavMode[];
|
|
133
|
+
defaultMode: StandardNavMode;
|
|
134
|
+
};
|
|
122
135
|
/** See PolitogyFrameGrant. Omitted => the app may pass no frame input. */
|
|
123
136
|
grants?: readonly PolitogyFrameGrant[];
|
|
124
137
|
/**
|
package/dist/app-chrome.d.ts
CHANGED
|
@@ -119,6 +119,19 @@ declare const POLITOGY_APPS: Record<PolitogyAppKey, {
|
|
|
119
119
|
origin: string;
|
|
120
120
|
storagePrefix: string;
|
|
121
121
|
mode?: PolitogyAppMode;
|
|
122
|
+
/**
|
|
123
|
+
* DUAL-MODE apps (today: a2p — Specs/A2P: "the SMS/MMS delivery engine
|
|
124
|
+
* for Relationship Mode and Campaign Mode") have no single fixed mode.
|
|
125
|
+
* `mode` above stays the fallback/default identity for any code that
|
|
126
|
+
* still reads `meta.mode` directly; FrameBody instead resolves the
|
|
127
|
+
* EFFECTIVE mode via `resolveActiveMode` (?mode= query → the
|
|
128
|
+
* `politogy_active_mode` cookie → `defaultMode`) and uses that for the
|
|
129
|
+
* nav tree, the Mode pill, and the brand lockup.
|
|
130
|
+
*/
|
|
131
|
+
dual?: {
|
|
132
|
+
modes: readonly StandardNavMode[];
|
|
133
|
+
defaultMode: StandardNavMode;
|
|
134
|
+
};
|
|
122
135
|
/** See PolitogyFrameGrant. Omitted => the app may pass no frame input. */
|
|
123
136
|
grants?: readonly PolitogyFrameGrant[];
|
|
124
137
|
/**
|
package/dist/app-chrome.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { broadcastLogout, watchLogout } from './chunk-LL6LSJAH.js';
|
|
3
|
-
import { clearBetaBannerDismissal, standardPolitogyNav, NavAppShell, NavChromeProvider, FuelAction, NavDisplayAction, TopBarWidgetSlot, SupportAction, NotificationsAction, useChromeMe, useThemePreference, useNavChromeFetcher, useChromeOrgs } from './chunk-
|
|
3
|
+
import { clearBetaBannerDismissal, standardPolitogyNav, NavAppShell, NavChromeProvider, FuelAction, NavDisplayAction, TopBarWidgetSlot, SpecOverlayAction, SupportAction, NotificationsAction, useChromeMe, useThemePreference, useNavChromeFetcher, useChromeOrgs } from './chunk-TMOZTLUZ.js';
|
|
4
4
|
import './chunk-U4RZPZET.js';
|
|
5
5
|
import { NAV_DISPLAY_DEFAULT, resolveNavDisplay, isNavDisplayPreference, writeNavDisplayCookie, writeNavDisplayLocal, NAV_DISPLAY_STORAGE_KEY, readNavDisplayCookie } from './chunk-6UHJV2FH.js';
|
|
6
6
|
import { resolveActiveOrg, isOrgId, writeActiveOrgCookie, ACTIVE_ORG_QUERY_PARAM, readActiveOrgCookie } from './chunk-W2HITMV5.js';
|
|
@@ -78,6 +78,58 @@ function resolveIdentity(input) {
|
|
|
78
78
|
loaded: input.loaded ?? false
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
// src/mode-preference.ts
|
|
83
|
+
var ACTIVE_MODE_COOKIE = "politogy_active_mode";
|
|
84
|
+
var VALID_MODES = ["relationship", "campaign", "petition"];
|
|
85
|
+
function isStandardNavMode(v) {
|
|
86
|
+
return typeof v === "string" && VALID_MODES.includes(v);
|
|
87
|
+
}
|
|
88
|
+
function readActiveModeCookie() {
|
|
89
|
+
if (typeof document === "undefined") return null;
|
|
90
|
+
try {
|
|
91
|
+
const m = document.cookie.match(
|
|
92
|
+
new RegExp(`(?:^|;\\s*)${ACTIVE_MODE_COOKIE}=([^;]+)`)
|
|
93
|
+
);
|
|
94
|
+
const v = m ? decodeURIComponent(m[1]) : null;
|
|
95
|
+
return isStandardNavMode(v) ? v : null;
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function writeActiveModeCookie(mode) {
|
|
101
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
102
|
+
const host = window.location.hostname;
|
|
103
|
+
const domainAttr = /(^|\.)politogyvrm\.com$/i.test(host) ? "; Domain=.politogyvrm.com" : "";
|
|
104
|
+
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
105
|
+
document.cookie = `${ACTIVE_MODE_COOKIE}=${encodeURIComponent(
|
|
106
|
+
mode
|
|
107
|
+
)}; Path=/; Max-Age=31536000; SameSite=Lax${domainAttr}${secure}`;
|
|
108
|
+
}
|
|
109
|
+
function modeFromSearch(search) {
|
|
110
|
+
if (!search) return null;
|
|
111
|
+
try {
|
|
112
|
+
const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
|
|
113
|
+
return params.get("mode");
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function resolveActiveMode({
|
|
119
|
+
search,
|
|
120
|
+
modes,
|
|
121
|
+
defaultMode
|
|
122
|
+
}) {
|
|
123
|
+
const fromQuery = modeFromSearch(search);
|
|
124
|
+
if (fromQuery && isStandardNavMode(fromQuery) && modes.includes(fromQuery)) {
|
|
125
|
+
return fromQuery;
|
|
126
|
+
}
|
|
127
|
+
const fromCookie = readActiveModeCookie();
|
|
128
|
+
if (fromCookie && modes.includes(fromCookie)) {
|
|
129
|
+
return fromCookie;
|
|
130
|
+
}
|
|
131
|
+
return defaultMode;
|
|
132
|
+
}
|
|
81
133
|
var BOOT_STAGE_TABLE = [
|
|
82
134
|
{ id: "s0_session", weight: 5, critical: true, waitsOn: "Politogy ID session resolution" },
|
|
83
135
|
{ id: "s1_org", weight: 10, critical: true, waitsOn: "Chrome API /api/chrome/me + orgs" },
|
|
@@ -447,7 +499,17 @@ var POLITOGY_APPS = {
|
|
|
447
499
|
// Campaign Mode apps — each scoped to Campaign, so they render the Campaign
|
|
448
500
|
// tree and a static Campaign pill. Not yet deployed; hidden from the Campaign
|
|
449
501
|
// nav by CAMPAIGN_FEATURE_AVAILABILITY until their launch flips the flag.
|
|
450
|
-
|
|
502
|
+
// Dual-mode: a2p is the SMS/MMS delivery engine for BOTH Relationship Mode
|
|
503
|
+
// and Campaign Mode (Specs/A2P). `mode: 'campaign'` stays the fallback for
|
|
504
|
+
// anything reading `meta.mode` directly; the effective mode a user sees is
|
|
505
|
+
// resolved per-visit (see `dual`, resolveActiveMode).
|
|
506
|
+
a2p: {
|
|
507
|
+
title: "A2P Messaging",
|
|
508
|
+
origin: "https://a2p.politogyvrm.com",
|
|
509
|
+
storagePrefix: "a2p",
|
|
510
|
+
mode: "campaign",
|
|
511
|
+
dual: { modes: ["relationship", "campaign"], defaultMode: "campaign" }
|
|
512
|
+
},
|
|
451
513
|
phonebank: { title: "Phone Banking", origin: "https://phonebank.politogyvrm.com", storagePrefix: "phonebank", mode: "campaign" },
|
|
452
514
|
canvass: { title: "Field Canvassing", origin: "https://canvass.politogyvrm.com", storagePrefix: "canvass", mode: "campaign" },
|
|
453
515
|
signs: { title: "Sign Mapping", origin: "https://signs.politogyvrm.com", storagePrefix: "signs", mode: "campaign" }
|
|
@@ -584,9 +646,19 @@ function FrameBody({
|
|
|
584
646
|
});
|
|
585
647
|
const userMenu = core?.userMenu ?? derivedUserMenu;
|
|
586
648
|
const footer = useChromeFooter(buildMeta ?? {});
|
|
587
|
-
const
|
|
649
|
+
const resolvedDualMode = meta.dual ? resolveActiveMode({
|
|
650
|
+
search: search ?? (typeof window !== "undefined" ? window.location.search : void 0),
|
|
651
|
+
modes: meta.dual.modes,
|
|
652
|
+
defaultMode: meta.dual.defaultMode
|
|
653
|
+
}) : void 0;
|
|
654
|
+
const appMode = resolvedDualMode ?? meta.mode;
|
|
588
655
|
const derivedMode = useChromeMode(appMode ? { pinnedModeId: appMode } : void 0);
|
|
589
656
|
const mode = core?.mode ?? derivedMode;
|
|
657
|
+
const coreLiveMode = app === CORE_APP ? pathname.startsWith("/campaign") ? "campaign" : pathname.startsWith("/relationship") ? "relationship" : void 0 : void 0;
|
|
658
|
+
const cookieMode = coreLiveMode ?? (meta.dual ? void 0 : meta.mode);
|
|
659
|
+
useEffect(() => {
|
|
660
|
+
if (cookieMode) writeActiveModeCookie(cookieMode);
|
|
661
|
+
}, [cookieMode]);
|
|
590
662
|
const coreOrgs = chrome?.orgs ?? [];
|
|
591
663
|
const activeCoreOrg = coreOrgs.find((o) => o.id === chrome?.orgId) ?? null;
|
|
592
664
|
const navDisplay = chrome?.navDisplay ?? "both";
|
|
@@ -959,18 +1031,71 @@ function AppDevNotes({
|
|
|
959
1031
|
}),
|
|
960
1032
|
[storagePrefix, identity.role]
|
|
961
1033
|
);
|
|
962
|
-
|
|
963
|
-
|
|
1034
|
+
const activatedRef = useRef(false);
|
|
1035
|
+
const activationWaitersRef = useRef([]);
|
|
1036
|
+
const activate = useCallback(() => {
|
|
1037
|
+
if (activatedRef.current) return;
|
|
1038
|
+
activatedRef.current = true;
|
|
1039
|
+
const waiters = activationWaitersRef.current;
|
|
1040
|
+
activationWaitersRef.current = [];
|
|
1041
|
+
waiters.forEach((resolve) => resolve());
|
|
1042
|
+
}, []);
|
|
1043
|
+
const waitForActivation = useCallback(() => {
|
|
1044
|
+
if (activatedRef.current) return Promise.resolve();
|
|
1045
|
+
return new Promise((resolve) => {
|
|
1046
|
+
activationWaitersRef.current.push(resolve);
|
|
1047
|
+
});
|
|
1048
|
+
}, []);
|
|
1049
|
+
const lazyAdapter = useMemo(() => {
|
|
1050
|
+
const GATED_METHODS = [
|
|
1051
|
+
"fetchTasks",
|
|
1052
|
+
"fetchTaskTypes",
|
|
1053
|
+
"fetchTaskLists",
|
|
1054
|
+
"fetchCollaborators",
|
|
1055
|
+
"fetchCapabilities",
|
|
1056
|
+
"getAppLinkStatus",
|
|
1057
|
+
"fetchBugReports",
|
|
1058
|
+
"fetchBugReportTypes"
|
|
1059
|
+
];
|
|
1060
|
+
const wrapped = { ...adapter };
|
|
1061
|
+
for (const method of GATED_METHODS) {
|
|
1062
|
+
const original = adapter[method].bind(adapter);
|
|
1063
|
+
wrapped[method] = (...args) => waitForActivation().then(() => original(...args));
|
|
1064
|
+
}
|
|
1065
|
+
let idleScheduled = false;
|
|
1066
|
+
const originalUnreadCounts = adapter.fetchUnreadCounts.bind(adapter);
|
|
1067
|
+
wrapped.fetchUnreadCounts = () => {
|
|
1068
|
+
if (idleScheduled || typeof window === "undefined") return originalUnreadCounts();
|
|
1069
|
+
idleScheduled = true;
|
|
1070
|
+
return new Promise((resolve, reject) => {
|
|
1071
|
+
const run = () => originalUnreadCounts().then(resolve, reject);
|
|
1072
|
+
const ric = window.requestIdleCallback;
|
|
1073
|
+
if (ric) ric(run);
|
|
1074
|
+
else setTimeout(run, 0);
|
|
1075
|
+
});
|
|
1076
|
+
};
|
|
1077
|
+
return wrapped;
|
|
1078
|
+
}, [adapter, waitForActivation]);
|
|
1079
|
+
if (devNotes?.providedByApp) return /* @__PURE__ */ jsx(DevNotesSurface, { activate, children });
|
|
1080
|
+
return /* @__PURE__ */ jsx(DevNotesProvider, { adapter: lazyAdapter, user, config, children: /* @__PURE__ */ jsx(DevNotesSurface, { activate, children }) });
|
|
964
1081
|
}
|
|
965
1082
|
var DevNotesTasksContext = createContext({
|
|
966
1083
|
ready: false,
|
|
967
1084
|
openTasks: () => {
|
|
1085
|
+
},
|
|
1086
|
+
activate: () => {
|
|
968
1087
|
}
|
|
969
1088
|
});
|
|
970
|
-
function DevNotesSurface({
|
|
1089
|
+
function DevNotesSurface({
|
|
1090
|
+
activate,
|
|
1091
|
+
children
|
|
1092
|
+
}) {
|
|
971
1093
|
const [tasksOpen, setTasksOpen] = useState(false);
|
|
972
|
-
const openTasks = useCallback(() =>
|
|
973
|
-
|
|
1094
|
+
const openTasks = useCallback(() => {
|
|
1095
|
+
activate();
|
|
1096
|
+
setTasksOpen(true);
|
|
1097
|
+
}, [activate]);
|
|
1098
|
+
const tasksCtx = useMemo(() => ({ ready: true, openTasks, activate }), [openTasks, activate]);
|
|
974
1099
|
return /* @__PURE__ */ jsxs(DevNotesTasksContext.Provider, { value: tasksCtx, children: [
|
|
975
1100
|
children,
|
|
976
1101
|
tasksOpen ? /* @__PURE__ */ jsx(DevNotesTaskListModal, { open: true, onClose: () => setTasksOpen(false) }) : null,
|
|
@@ -1026,14 +1151,15 @@ function ChromeTopBarActions() {
|
|
|
1026
1151
|
children: /* @__PURE__ */ jsx(ChromeDevNotesMenu, {})
|
|
1027
1152
|
}
|
|
1028
1153
|
),
|
|
1154
|
+
/* @__PURE__ */ jsx(SpecOverlayAction, { orgId: state.orgId }),
|
|
1029
1155
|
/* @__PURE__ */ jsx(SupportAction, { orgId: state.orgId }),
|
|
1030
1156
|
/* @__PURE__ */ jsx(NotificationsAction, { orgId: state.orgId })
|
|
1031
1157
|
] });
|
|
1032
1158
|
}
|
|
1033
1159
|
function ChromeDevNotesMenu() {
|
|
1034
|
-
const { ready, openTasks } = useContext(DevNotesTasksContext);
|
|
1160
|
+
const { ready, openTasks, activate } = useContext(DevNotesTasksContext);
|
|
1035
1161
|
if (!ready) return null;
|
|
1036
|
-
return /* @__PURE__ */ jsx(DevNotesMenu, { position: "top-right", onViewTasks: openTasks });
|
|
1162
|
+
return /* @__PURE__ */ jsx("span", { style: { display: "contents" }, onPointerDownCapture: activate, onFocusCapture: activate, onKeyDownCapture: activate, children: /* @__PURE__ */ jsx(DevNotesMenu, { position: "top-right", onViewTasks: openTasks }) });
|
|
1037
1163
|
}
|
|
1038
1164
|
function useIsSuperAdmin() {
|
|
1039
1165
|
const state = useChromeState();
|