@bobfrankston/rmfmail 1.1.141 → 1.1.142

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/client/app.ts CHANGED
@@ -949,12 +949,32 @@ async function openCompose(mode: ComposeMode, overrideMsg?: any, overrideAccount
949
949
  const initJson = JSON.stringify(init);
950
950
  try { sessionStorage.setItem("composeInit", initJson); }
951
951
  catch (e: any) { console.error("[compose] sessionStorage.setItem failed:", e?.message || e); }
952
+ // Stash on the parent window so a same-origin iframe can pull it via
953
+ // window.parent (most reliable handoff under WebView2 custom-protocol —
954
+ // sessionStorage and postMessage both fail intermittently). Keyed by
955
+ // a unique token in the iframe URL so concurrent composes don't crosswire.
956
+ const composeKey = "init-" + Math.random().toString(36).slice(2, 10);
957
+ (window as any).__mailxComposeInits = (window as any).__mailxComposeInits || {};
958
+ (window as any).__mailxComposeInits[composeKey] = init;
959
+ // Expose the key on the iframe so its IIFE can fetch the right blob.
960
+ try { (frame as any).dataset.composeKey = composeKey; } catch { /* */ }
952
961
  const post = (): void => {
953
- try { frame?.contentWindow?.postMessage({ type: "compose-init", init }, "*"); } catch { /* */ }
962
+ try { frame?.contentWindow?.postMessage({ type: "compose-init", init, composeKey }, "*"); }
963
+ catch (e: any) { logClientEvent("compose-post-failed", { err: e?.message || String(e) }); }
954
964
  };
965
+ logClientEvent("compose-handoff", { hasFrame: !!frame, hasContentWindow: !!frame?.contentWindow, composeKey, bodyBytes: initJson.length });
955
966
  post();
956
967
  // Iframe may not be loaded yet — re-post on load so the listener exists.
957
- try { frame?.addEventListener("load", post, { once: true }); } catch { /* */ }
968
+ try { frame?.addEventListener("load", () => { logClientEvent("compose-iframe-loaded", { composeKey }); post(); }); } catch { /* */ }
969
+ // And keep posting every 100ms for up to 3s as a defense-in-depth — if
970
+ // both load and the initial post race the wrong way, the iframe's IIFE
971
+ // will see the payload on its next tick.
972
+ let attempts = 0;
973
+ const heartbeat = setInterval(() => {
974
+ attempts++;
975
+ post();
976
+ if (attempts >= 30) clearInterval(heartbeat);
977
+ }, 100);
958
978
  }
959
979
 
960
980
  function showComposeOverlay(title = "Compose"): HTMLIFrameElement {
@@ -4167,16 +4167,46 @@ function scheduleDraftSave() {
4167
4167
  var _postedInit = null;
4168
4168
  var _parentInitReady = !!sessionStorage.getItem("composeInit");
4169
4169
  var _parentInitListeners = [];
4170
+ var _msgEventCount = 0;
4170
4171
  window.addEventListener("message", (e) => {
4172
+ _msgEventCount++;
4171
4173
  if (e.data?.type === "compose-init-ready") {
4172
4174
  _parentInitReady = true;
4173
4175
  for (const fn of _parentInitListeners.splice(0)) fn();
4174
4176
  } else if (e.data?.type === "compose-init" && e.data.init) {
4177
+ if (!_postedInit) {
4178
+ try {
4179
+ logClientEvent("compose-init-received", { src: "postMessage" });
4180
+ } catch {
4181
+ }
4182
+ }
4175
4183
  _postedInit = e.data.init;
4176
4184
  _parentInitReady = true;
4177
4185
  for (const fn of _parentInitListeners.splice(0)) fn();
4178
4186
  }
4179
4187
  });
4188
+ function pullInitFromParent() {
4189
+ try {
4190
+ const myFrame = window.frameElement;
4191
+ const key = myFrame?.dataset?.composeKey;
4192
+ if (!key) return null;
4193
+ const stash = window.parent?.__mailxComposeInits;
4194
+ const init = stash?.[key];
4195
+ if (init) {
4196
+ try {
4197
+ logClientEvent("compose-init-received", { src: "parent-stash", key });
4198
+ } catch {
4199
+ }
4200
+ return init;
4201
+ }
4202
+ } catch (e) {
4203
+ try {
4204
+ logClientEvent("compose-init-pull-failed", { err: e?.message || String(e) });
4205
+ } catch {
4206
+ }
4207
+ }
4208
+ return null;
4209
+ }
4180
4210
  function waitForParentInit(maxMs) {
4181
4211
  if (_parentInitReady) return Promise.resolve();
4182
4212
  return new Promise((resolve) => {
@@ -4192,17 +4222,20 @@ function waitForParentInit(maxMs) {
4192
4222
  }
4193
4223
  (async () => {
4194
4224
  _ctick("init IIFE start");
4195
- if (!sessionStorage.getItem("composeInit") && !_postedInit) {
4225
+ let parentInit = pullInitFromParent();
4226
+ if (!parentInit && !sessionStorage.getItem("composeInit") && !_postedInit) {
4196
4227
  _ctick("waiting for parent init");
4197
4228
  await waitForParentInit(1500);
4198
- _ctick("parent init received");
4229
+ _ctick(`parent init received (msgEvents=${_msgEventCount})`);
4230
+ if (!_postedInit) parentInit = pullInitFromParent();
4199
4231
  }
4200
4232
  const stored = sessionStorage.getItem("composeInit");
4201
- const initRaw = _postedInit || (stored ? JSON.parse(stored) : null);
4233
+ const initRaw = parentInit || _postedInit || (stored ? JSON.parse(stored) : null);
4202
4234
  if (initRaw) {
4203
4235
  sessionStorage.removeItem("composeInit");
4204
4236
  const init = initRaw;
4205
- _ctick(`init parsed (mode=${init.mode}, bodyHtml=${init.bodyHtml?.length || 0} bytes, src=${_postedInit ? "postMessage" : "sessionStorage"})`);
4237
+ const src = parentInit ? "parent-stash" : _postedInit ? "postMessage" : "sessionStorage";
4238
+ _ctick(`init parsed (mode=${init.mode}, bodyHtml=${init.bodyHtml?.length || 0} bytes, src=${src})`);
4206
4239
  if (init.accounts && init.accounts.length > 0) {
4207
4240
  applyInit(init);
4208
4241
  _ctick("applyInit done \u2014 compose visible");