@the-portland-company/shell 0.41.1 → 0.43.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.
@@ -1557,7 +1557,12 @@ function standardPolitogyNav(opts) {
1557
1557
  return rows.filter((r) => !r.flag || avail[r.flag]).map(({ flag: _flag, ...item }) => item);
1558
1558
  };
1559
1559
  const buildPetition = () => [
1560
- { label: "Dashboard", icon: icon("home"), href: withOrg(PETITION_MODE_ORIGIN) }
1560
+ opts.petitionsItem ? {
1561
+ label: "Dashboard",
1562
+ icon: icon("home"),
1563
+ href: opts.petitionsItem.href ?? withOrg(PETITION_MODE_ORIGIN),
1564
+ children: opts.petitionsItem.children
1565
+ } : { label: "Dashboard", icon: icon("home"), href: withOrg(PETITION_MODE_ORIGIN) }
1561
1566
  ];
1562
1567
  const mode = opts.mode ?? "relationship";
1563
1568
  const items = mode === "campaign" ? buildCampaign() : mode === "petition" ? buildPetition() : buildRelationship();
@@ -2948,10 +2953,11 @@ function FrameBody({
2948
2953
  // An app that OWNS a standard nav entry (Contacts) nests its own routes
2949
2954
  // under it. This is routing data, not chrome configuration.
2950
2955
  contactsItem: data?.contactsItem,
2956
+ petitionsItem: data?.petitionsItem,
2951
2957
  mode: navMode,
2952
2958
  activeOrgId
2953
2959
  }),
2954
- [data?.contactsItem, navMode, activeOrgId]
2960
+ [data?.contactsItem, data?.petitionsItem, navMode, activeOrgId]
2955
2961
  );
2956
2962
  const navItems = core?.nav ?? derivedNavItems;
2957
2963
  const derivedBreadcrumbs = react.useMemo(
@@ -3065,9 +3071,13 @@ function ChromeStateProvider({
3065
3071
  const [orgId, setOrgIdState] = react.useState(null);
3066
3072
  const [navDisplay, setNavDisplayState] = react.useState(NAV_DISPLAY_DEFAULT);
3067
3073
  const navReconciled = react.useRef(false);
3074
+ const orgReconciled = react.useRef(false);
3075
+ const hadBootSelector = react.useRef(false);
3068
3076
  react.useEffect(() => {
3069
3077
  try {
3070
- const storedOrg = resolveActiveOrg() ?? localStorage.getItem(ORG_STORAGE_KEY);
3078
+ const selector = resolveActiveOrg();
3079
+ if (selector) hadBootSelector.current = true;
3080
+ const storedOrg = selector ?? localStorage.getItem(ORG_STORAGE_KEY);
3071
3081
  if (storedOrg) setOrgIdState(storedOrg);
3072
3082
  } catch {
3073
3083
  }
@@ -3105,6 +3115,51 @@ function ChromeStateProvider({
3105
3115
  if (!orgs || orgs.length === 0) return;
3106
3116
  setOrgIdState((prev) => prev && orgs.some((o) => o.id === prev) ? prev : orgs[0].id);
3107
3117
  }, [orgs]);
3118
+ const activeOrgRef = react.useRef(null);
3119
+ const memberOrgsRef = react.useRef([]);
3120
+ const controlledOrgRef = react.useRef(controlledOrg);
3121
+ controlledOrgRef.current = controlledOrg;
3122
+ const applyExternalOrg = react.useCallback((next) => {
3123
+ if (!isOrgId(next)) return;
3124
+ const members = memberOrgsRef.current;
3125
+ if (members.length === 0) return;
3126
+ if (!members.some((o) => o.id === next)) return;
3127
+ if (next === activeOrgRef.current) return;
3128
+ const controlled = controlledOrgRef.current;
3129
+ if (controlled) (controlled.onExternalSwitch ?? controlled.onSwitch)(next);
3130
+ else setOrgIdState(next);
3131
+ }, []);
3132
+ react.useEffect(() => {
3133
+ const onStorage = (e) => {
3134
+ if (e.key === ORG_STORAGE_KEY) applyExternalOrg(e.newValue);
3135
+ };
3136
+ const onWake = () => {
3137
+ applyExternalOrg(readActiveOrgCookie());
3138
+ };
3139
+ window.addEventListener("storage", onStorage);
3140
+ window.addEventListener("focus", onWake);
3141
+ document.addEventListener("visibilitychange", onWake);
3142
+ return () => {
3143
+ window.removeEventListener("storage", onStorage);
3144
+ window.removeEventListener("focus", onWake);
3145
+ document.removeEventListener("visibilitychange", onWake);
3146
+ };
3147
+ }, [applyExternalOrg]);
3148
+ react.useEffect(() => {
3149
+ if (orgReconciled.current || !chromeMe.me) return;
3150
+ if (hadBootSelector.current) {
3151
+ orgReconciled.current = true;
3152
+ return;
3153
+ }
3154
+ const pref = chromeMe.me.activeOrg;
3155
+ if (!isOrgId(pref)) {
3156
+ orgReconciled.current = true;
3157
+ return;
3158
+ }
3159
+ if (memberOrgsRef.current.length === 0) return;
3160
+ orgReconciled.current = true;
3161
+ applyExternalOrg(pref ?? null);
3162
+ }, [chromeMe.me, orgs, controlledOrg, applyExternalOrg]);
3108
3163
  const setOrgId = react.useCallback((id) => {
3109
3164
  setOrgIdState(id);
3110
3165
  try {
@@ -3164,9 +3219,16 @@ function ChromeStateProvider({
3164
3219
  };
3165
3220
  }, [controlledOrg, orgId, setOrgId, orgs, orgsFetching, orgsError, navDisplay, setNavDisplay]);
3166
3221
  const activeOrgId = value.orgId;
3222
+ activeOrgRef.current = activeOrgId;
3223
+ memberOrgsRef.current = value.orgs;
3224
+ const lastPatchedOrg = react.useRef(null);
3167
3225
  react.useEffect(() => {
3168
3226
  if (!isOrgId(activeOrgId)) return;
3169
3227
  writeActiveOrgCookie(activeOrgId);
3228
+ try {
3229
+ localStorage.setItem(ORG_STORAGE_KEY, activeOrgId);
3230
+ } catch {
3231
+ }
3170
3232
  try {
3171
3233
  const url = new URL(window.location.href);
3172
3234
  if (url.searchParams.get(ACTIVE_ORG_QUERY_PARAM) !== activeOrgId) {
@@ -3175,7 +3237,17 @@ function ChromeStateProvider({
3175
3237
  }
3176
3238
  } catch {
3177
3239
  }
3178
- }, [activeOrgId]);
3240
+ const known = chromeMe.me?.activeOrg ?? null;
3241
+ if (chromeMe.me && activeOrgId !== known && activeOrgId !== lastPatchedOrg.current) {
3242
+ lastPatchedOrg.current = activeOrgId;
3243
+ chromeFetch?.("me/preferences", {
3244
+ method: "PATCH",
3245
+ headers: { "Content-Type": "application/json" },
3246
+ body: JSON.stringify({ activeOrg: activeOrgId })
3247
+ })?.catch(() => {
3248
+ });
3249
+ }
3250
+ }, [activeOrgId, chromeMe.me, chromeFetch]);
3179
3251
  return /* @__PURE__ */ jsxRuntime.jsx(ChromeStateContext.Provider, { value, children });
3180
3252
  }
3181
3253
  function AppDevNotes({