Files
archy/docker/justworks/business/public/assets/shell.js
T
yaya 94899e69d3 feat: integrate Just Works app and native business setup
Preserve hosted website UX inside Archipelago, add isolated Business and owner-authorized native wallet setup, and include deployment handoff. Based on main with native signer import already accepted.
2026-10-08 19:39:38 +01:00

10 lines
4.7 KiB
JavaScript

const prefix=location.pathname.split('/').slice(0,-1).join('/');
const token=new URLSearchParams(location.search).get('table')||'';
const stage=document.querySelector('#stage');
const pending=new Map();let frame;
function injectedShim(config){return `<script>(()=>{let seq=0;const pending=new Map();addEventListener('message',e=>{const m=e.data||{};if(m.id&&pending.has(m.id)){const p=pending.get(m.id);pending.delete(m.id);m.error?p.reject(new Error(m.error)):p.resolve(m.value)}});const call=(type,payload={})=>new Promise((resolve,reject)=>{const id='nap-'+(++seq);pending.set(id,{resolve,reject});parent.postMessage({type,id,...payload},'*')});window.napplet={config:{get:()=>call('config.get'),subscribe:()=>({close(){}}),openSettings:()=>{}},storage:{getItem:key=>call('storage.getItem',{key}),setItem:(key,value)=>call('storage.setItem',{key,value}),removeItem:key=>call('storage.removeItem',{key})},notify:{send:input=>call('notify.send',{input})},shell:{supports:domain=>['config','storage','notify','shell'].includes(domain)}};const report=()=>parent.postMessage({type:'shell.resize',id:'resize-'+(++seq),height:Math.ceil(document.documentElement.scrollHeight)},'*');new ResizeObserver(report).observe(document.documentElement);addEventListener('load',report);parent.postMessage({type:'shell.ready'},'*')})()<\/script>`}
async function boot(){const config=await fetch(`${prefix}/api/table?token=${encodeURIComponent(token)}`,{cache:'no-store'}).then(async r=>{const j=await r.json();if(!r.ok)throw new Error(j.error||'configuration unavailable');return j});const artifact=await fetch(`${prefix}/napplet/order-to-table/index.html`,{cache:'no-store'}).then(r=>r.text());const srcdoc=artifact.replace(/<head([^>]*)>/i,`<head$1>${injectedShim(config)}`);frame=document.createElement('iframe');frame.sandbox='allow-scripts';frame.title='Order to Table napplet';frame.srcdoc=srcdoc;stage.replaceChildren(frame)}
addEventListener('message',async event=>{if(!frame||event.source!==frame.contentWindow)return;const m=event.data||{};if(!m.id)return;const answer=(value,error)=>frame.contentWindow.postMessage({type:`${m.type}.result`,id:m.id,value,error},'*');try{if(m.type==='config.get'){const value=await fetch(`${prefix}/api/table?token=${encodeURIComponent(token)}`,{cache:'no-store'}).then(r=>r.json());return answer({...value,tableToken:token})}if(m.type==='storage.setItem'){if(m.key.startsWith('order:')){const payload=typeof m.value==='string'?JSON.parse(m.value):m.value;const res=await fetch(`${prefix}/api/orders`,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({...payload,token})});const value=await res.json();if(!res.ok)throw new Error(value.error||'order failed');return answer(value)}if(m.key==='payment:create'){const payload=typeof m.value==='string'?JSON.parse(m.value):m.value;const res=await fetch(`${prefix}/api/payments`,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({...payload,token})});const value=await res.json();if(!res.ok)throw new Error(value.error||'invoice unavailable');sessionStorage.setItem(`napplet:payment-created:${payload.order_id}`,JSON.stringify(value));return answer()}sessionStorage.setItem(`napplet:${m.key}`,JSON.stringify(m.value));return answer()}if(m.type==='storage.getItem'){if(m.key.startsWith('order-status:')){const id=m.key.slice('order-status:'.length);const res=await fetch(`${prefix}/api/order?token=${encodeURIComponent(token)}&id=${encodeURIComponent(id)}`,{cache:'no-store'});const value=await res.json();if(!res.ok)throw new Error(value.error||'order status unavailable');return answer(value)}if(m.key.startsWith('payment-status:')){const id=m.key.slice('payment-status:'.length);const res=await fetch(`${prefix}/api/payment?token=${encodeURIComponent(token)}&id=${encodeURIComponent(id)}`,{cache:'no-store'});const value=await res.json();if(!res.ok)throw new Error(value.error||'payment status unavailable');return answer(value)}const raw=sessionStorage.getItem(`napplet:${m.key}`);return answer(raw?JSON.parse(raw):null)}if(m.type==='storage.removeItem'){sessionStorage.removeItem(`napplet:${m.key}`);return answer()}if(m.type==='notify.send'){return answer({shown:true})}answer(null,`unsupported capability: ${m.type}`)}catch(error){answer(null,String(error.message||error))}});
addEventListener('message',event=>{if(!frame||event.source!==frame.contentWindow||event.data?.type!=='shell.resize')return;const height=Math.max(320,Math.min(4000,Number(event.data.height)||0));frame.style.height=`${height}px`;stage.style.height=`${height}px`;if(window.frameElement instanceof HTMLIFrameElement)window.frameElement.style.height=`${height}px`;parent.postMessage({type:'napplet.resize',height},'*')});
boot().catch(error=>stage.innerHTML=`<div class="error">Napplet unavailable: ${String(error.message||error)}</div>`);