fix: Transak modal not opening — namespace querySelector + wallet check

- Use DOM API (createElementNS + createElement) to build foreignObject
  panel instead of innerHTML, ensuring proper HTML namespace for
  querySelector to traverse SVG→HTML boundary
- Query buttons/inputs from HTML panel root instead of SVG overlay
- Remove wallet address requirement for demo mode (use zero address)
- Add console logging and .catch() for Transak widget debugging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-05 17:07:28 -08:00
parent 1335c38d48
commit f9bafc8ef0
1 changed files with 44 additions and 37 deletions

View File

@ -2491,28 +2491,34 @@ class FolkFlowsApp extends HTMLElement {
return;
}
// Source/outcome: keep config panel
// Source/outcome: keep config panel — use DOM APIs for proper namespace handling
const panelW = 280;
const panelH = 260;
const panelX = s.w + 12;
const panelY = 0;
overlay.innerHTML += `
<foreignObject x="${panelX}" y="${panelY}" width="${panelW}" height="${panelH}">
<div xmlns="http://www.w3.org/1999/xhtml" class="inline-config-panel" style="height:${panelH}px">
<div class="icp-tabs">
<button class="icp-tab icp-tab--active" data-icp-tab="config">Config</button>
<button class="icp-tab" data-icp-tab="analytics">Analytics</button>
<button class="icp-tab" data-icp-tab="allocations">Alloc</button>
</div>
<div class="icp-body">${this.renderInlineConfigContent(node)}</div>
<div class="icp-toolbar">
<button class="iet-done" style="background:var(--rflows-btn-done);color:white">Done</button>
<button class="iet-delete" style="background:var(--rflows-btn-delete);color:white">Delete</button>
<button class="iet-panel" style="background:var(--rs-border-strong);color:var(--rs-text-primary)">...</button>
</div>
</div>
</foreignObject>`;
const fo = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
fo.setAttribute("x", String(panelX));
fo.setAttribute("y", String(panelY));
fo.setAttribute("width", String(panelW));
fo.setAttribute("height", String(panelH));
const panelDiv = document.createElement("div");
panelDiv.className = "inline-config-panel";
panelDiv.style.height = `${panelH}px`;
panelDiv.innerHTML = `
<div class="icp-tabs">
<button class="icp-tab icp-tab--active" data-icp-tab="config">Config</button>
<button class="icp-tab" data-icp-tab="analytics">Analytics</button>
<button class="icp-tab" data-icp-tab="allocations">Alloc</button>
</div>
<div class="icp-body">${this.renderInlineConfigContent(node)}</div>
<div class="icp-toolbar">
<button class="iet-done" style="background:var(--rflows-btn-done);color:white">Done</button>
<button class="iet-delete" style="background:var(--rflows-btn-delete);color:white">Delete</button>
<button class="iet-panel" style="background:var(--rs-border-strong);color:var(--rs-text-primary)">...</button>
</div>`;
fo.appendChild(panelDiv);
overlay.appendChild(fo);
g.appendChild(overlay);
this.attachInlineConfigListeners(g, node);
@ -2801,57 +2807,60 @@ class FolkFlowsApp extends HTMLElement {
const overlay = g.querySelector(".inline-edit-overlay");
if (!overlay) return;
// Get the HTML panel inside foreignObject for reliable cross-namespace queries
const htmlPanel = overlay.querySelector("foreignObject")?.querySelector(".inline-config-panel") as HTMLElement | null;
const queryRoot = htmlPanel || overlay;
// Tab switching
overlay.querySelectorAll(".icp-tab").forEach((el) => {
queryRoot.querySelectorAll(".icp-tab").forEach((el) => {
el.addEventListener("click", (e: Event) => {
e.stopPropagation();
const tab = (el as HTMLElement).dataset.icpTab as "config" | "analytics" | "allocations";
if (!tab || tab === this.inlineConfigTab) return;
this.inlineConfigTab = tab;
overlay.querySelectorAll(".icp-tab").forEach((t) => t.classList.remove("icp-tab--active"));
queryRoot.querySelectorAll(".icp-tab").forEach((t) => t.classList.remove("icp-tab--active"));
el.classList.add("icp-tab--active");
const body = overlay.querySelector(".icp-body") as HTMLElement;
const body = queryRoot.querySelector(".icp-body") as HTMLElement;
if (body) body.innerHTML = this.renderInlineConfigContent(node);
this.attachInlineConfigFieldListeners(overlay as Element, node);
this.attachInlineConfigFieldListeners(queryRoot as Element, node);
});
});
// Field listeners
this.attachInlineConfigFieldListeners(overlay, node);
this.attachInlineConfigFieldListeners(queryRoot, node);
// Threshold drag handles (funnel)
// Threshold drag handles (funnel — on SVG overlay, not HTML panel)
this.attachThresholdDragListeners(overlay, node);
// Done button
overlay.querySelector(".iet-done")?.addEventListener("click", (e: Event) => {
queryRoot.querySelector(".iet-done")?.addEventListener("click", (e: Event) => {
e.stopPropagation();
this.exitInlineEdit();
});
// Delete button
overlay.querySelector(".iet-delete")?.addEventListener("click", (e: Event) => {
queryRoot.querySelector(".iet-delete")?.addEventListener("click", (e: Event) => {
e.stopPropagation();
this.deleteNode(node.id);
this.exitInlineEdit();
});
// "..." panel button
overlay.querySelector(".iet-panel")?.addEventListener("click", (e: Event) => {
queryRoot.querySelector(".iet-panel")?.addEventListener("click", (e: Event) => {
e.stopPropagation();
this.exitInlineEdit();
this.openEditor(node.id);
});
// Fund Now button (source card type)
overlay.querySelector("[data-icp-action='fund']")?.addEventListener("click", (e: Event) => {
const fundBtn = queryRoot.querySelector("[data-icp-action='fund']");
fundBtn?.addEventListener("click", (e: Event) => {
e.stopPropagation();
const sd = node.data as SourceNodeData;
const flowId = this.flowId || this.getAttribute("flow-id") || "";
if (!sd.walletAddress) {
alert("Configure a wallet address first");
return;
}
this.openTransakWidget(flowId, sd.walletAddress);
// Use configured wallet or demo default (Transak staging accepts any valid address)
const wallet = sd.walletAddress || "0x0000000000000000000000000000000000000000";
this.openTransakWidget(flowId, wallet).catch((err) => console.error("[Transak] Error:", err));
});
// Click-outside handler — listen on shadow root to avoid retargeting issues
@ -3174,6 +3183,7 @@ class FolkFlowsApp extends HTMLElement {
}
private async openTransakWidget(flowId: string, walletAddress: string) {
console.log("[Transak] Opening widget for flow:", flowId, "wallet:", walletAddress);
// Fetch Transak config from server
let apiKey = "STAGING_KEY";
let env = "STAGING";
@ -3633,11 +3643,8 @@ class FolkFlowsApp extends HTMLElement {
// Fund with card
backdrop.querySelector('[data-action="fund-with-card"]')?.addEventListener("click", () => {
const flowId = this.flowId || this.getAttribute("flow-id") || "";
if (!d.walletAddress) {
alert("Configure a wallet address first (use rIdentity passkey or enter manually)");
return;
}
this.openTransakWidget(flowId, d.walletAddress);
const wallet = d.walletAddress || "0x0000000000000000000000000000000000000000";
this.openTransakWidget(flowId, wallet);
});
// Connect with EncryptID