@nibgate/sdk 0.2.10 → 0.2.12
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/dist/nibgate.js +1456 -921
- package/dist/nibgate.js.map +4 -4
- package/dist/nibgate.min.js +67 -2
- package/dist/nibgate.min.js.map +4 -4
- package/package.json +1 -1
- package/src/browser/default-ui.js +418 -0
- package/src/browser/index.js +1 -0
- package/src/browser/reputation.js +2 -2
- package/src/index.d.ts +4 -1
- package/src/index.js +1 -0
- package/src/server/gateway.js +9 -0
- package/src/server/index.js +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { createEvmGatewayUnlock } from './evm-gateway.js';
|
|
2
|
+
|
|
3
|
+
const SID = 'nibgate-ui-styles';
|
|
4
|
+
|
|
5
|
+
const theme = {
|
|
6
|
+
bg: 'var(--bg, #f4f4f0)',
|
|
7
|
+
fg: 'var(--fg, #0a0a0a)',
|
|
8
|
+
muted: 'var(--muted, #6b6862)',
|
|
9
|
+
border: 'var(--border, #cecdc3)',
|
|
10
|
+
accent: 'var(--accent, #7c9a6d)',
|
|
11
|
+
accentSoft: 'var(--accent-soft, #d8e8d3)',
|
|
12
|
+
cardHover: 'var(--card-hover, #e0ddd3)',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const css = (s) => Object.entries(s).map(([k, v]) => `${k.replace(/[A-Z]/g, m => '-' + m.toLowerCase())}:${v}`).join(';');
|
|
16
|
+
|
|
17
|
+
function h(tag, attrs, children) {
|
|
18
|
+
const e = document.createElement(tag);
|
|
19
|
+
for (const [k, v] of Object.entries(attrs || {})) {
|
|
20
|
+
if (k === 'dataset') Object.assign(e.dataset, v);
|
|
21
|
+
else if (k.startsWith('on')) e.addEventListener(k.slice(2).toLowerCase(), v);
|
|
22
|
+
else if (k === 'style' && typeof v === 'object') e.style.cssText = css(v);
|
|
23
|
+
else if (k === 'cls') e.className = v;
|
|
24
|
+
else e.setAttribute(k, String(v));
|
|
25
|
+
}
|
|
26
|
+
if (typeof children === 'string') e.innerHTML = children;
|
|
27
|
+
else if (children) (Array.isArray(children) ? children : [children]).forEach(c => { if (c != null) e.appendChild(typeof c === 'string' ? document.createTextNode(c) : c); });
|
|
28
|
+
return e;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function inject() {
|
|
32
|
+
if (document.getElementById(SID)) return;
|
|
33
|
+
const s = h('style', { id: SID }, `
|
|
34
|
+
@keyframes nfade { from { opacity:0;transform:translateY(6px) } to { opacity:1;transform:translateY(0) } }
|
|
35
|
+
@keyframes nscale { from { opacity:0;transform:scale(0.96) } to { opacity:1;transform:scale(1) } }
|
|
36
|
+
@keyframes nshimmer { 0% { transform:translateX(-100%) } 100% { transform:translateX(100%) } }
|
|
37
|
+
|
|
38
|
+
.nui { font-family:var(--font-content,'Kumbh Sans','ABC Favorit',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif);color:${theme.fg};line-height:1.5;-webkit-font-smoothing:antialiased;font-size:19px }
|
|
39
|
+
.nui *,.nui *::before,.nui *::after { box-sizing:border-box }
|
|
40
|
+
.nui-btn { display:flex;align-items:center;justify-content:center;gap:8px;font-size:18px;font-weight:600;padding:14px 28px;border-radius:12px;cursor:pointer;transition:all.12s;font-family:inherit;line-height:1;border:none }
|
|
41
|
+
.nui-btn:disabled { opacity:0.35;cursor:default;pointer-events:none }
|
|
42
|
+
.nui-btn:focus-visible { outline:2px solid ${theme.accent};outline-offset:2px }
|
|
43
|
+
.nui-btn-primary { background:${theme.accent};color:${theme.bg} }
|
|
44
|
+
.nui-btn-primary:hover:not(:disabled) { opacity:0.9 }
|
|
45
|
+
.nui-btn-primary:active:not(:disabled) { transform:scale(.98) }
|
|
46
|
+
.nui-btn-outline { background:transparent;border:1px solid ${theme.border};color:${theme.fg} }
|
|
47
|
+
.nui-btn-outline:hover:not(:disabled) { border-color:${theme.accent};color:${theme.accent} }
|
|
48
|
+
.nui-input { padding:14px 16px;font-size:18px;border-radius:12px;border:1px solid ${theme.border};background:transparent;color:${theme.fg};width:100%;font-family:inherit;outline:none;transition:border-color.12s }
|
|
49
|
+
.nui-input:focus { border-color:${theme.accent} }
|
|
50
|
+
.nui-input::placeholder { color:${theme.muted} }
|
|
51
|
+
.nui-label { font-size:17px;font-weight:600;color:${theme.muted};margin-bottom:8px;display:block }
|
|
52
|
+
.nui-mono { font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace }
|
|
53
|
+
.nui-stat { font-size:18px;color:${theme.muted};line-height:1.4;min-height:28px }
|
|
54
|
+
.nui-stat-err { color:#dc2626 }
|
|
55
|
+
.nui-stat-ok { color:#16a34a }
|
|
56
|
+
`);
|
|
57
|
+
document.head.appendChild(s);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function el(tag, a, c) { return h(tag, a, c); }
|
|
61
|
+
|
|
62
|
+
function status(el, msg) {
|
|
63
|
+
if (!el) return;
|
|
64
|
+
el.textContent = msg || '';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function esc(s) {
|
|
68
|
+
const d = document.createElement('div');
|
|
69
|
+
d.textContent = String(s ?? '');
|
|
70
|
+
return d.innerHTML;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function renderDefaultUnlockUI(container, resource, options = {}) {
|
|
74
|
+
inject();
|
|
75
|
+
|
|
76
|
+
const card = el('div', { cls: 'nui', style: { animation: 'nfade .2s ease-out' } });
|
|
77
|
+
|
|
78
|
+
const lockSVG = '<svg width="24" height="24" viewBox="0 0 32 32" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle"><path d="M6 16v8a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-8"/><path d="M10 16v-5a6 6 0 0 1 12 0v5"/><circle cx="16" cy="21" r="2"/></svg>';
|
|
79
|
+
|
|
80
|
+
card.innerHTML = `
|
|
81
|
+
<div style="display:flex;flex-direction:column;align-items:center;text-align:center;max-width:580px;margin:0 auto;padding:40px 52px">
|
|
82
|
+
<div id="nibgate-lottie" style="width:165px;height:168px;margin-bottom:24px"></div>
|
|
83
|
+
<div style="font-size:50px;font-weight:700;letter-spacing:-.03em;color:${theme.fg};margin-bottom:12px">${esc(resource.price)} USDC</div>
|
|
84
|
+
<div style="font-size:21px;color:${theme.muted};margin-bottom:48px">Pay to unlock this content</div>
|
|
85
|
+
<div data-nibgate-wallet-label class="nui-mono" style="font-size:18px;color:${theme.muted};margin-bottom:40px;min-height:28px">Connect wallet</div>
|
|
86
|
+
<div data-nibgate-unlock-wrap style="width:100%;position:relative;border-radius:12px;overflow:hidden;cursor:pointer">
|
|
87
|
+
<div data-nibgate-unlock-progress style="position:absolute;inset:0;width:0%;background:linear-gradient(90deg,rgba(255,255,255,0.1),rgba(255,255,255,0.45));border-radius:12px;transition:width .05s linear;z-index:2"></div>
|
|
88
|
+
<div data-nibgate-shimmer style="position:absolute;inset:0;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,0.12),transparent);border-radius:12px;transform:translateX(-100%);z-index:3;pointer-events:none"></div>
|
|
89
|
+
<button type="button" data-nibgate-unlock disabled class="nui-btn nui-btn-primary" style="width:100%;padding:24px 32px;font-size:24px;position:relative;z-index:4;background:${theme.accent};transition:transform .1s,opacity .15s;display:flex">${lockSVG}Hold to pay ${esc(resource.price)} USDC</button></div>
|
|
90
|
+
<div class="nui-stat" style="text-align:center;margin-top:16px" data-nibgate-status></div>
|
|
91
|
+
</div>
|
|
92
|
+
<div data-nibgate-premium hidden style="margin-top:32px;border-top:1px solid ${theme.border};padding-top:32px"></div>
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
(typeof container === 'string' ? document.querySelector(container) : container)?.appendChild(card);
|
|
96
|
+
// Load Lottie animation
|
|
97
|
+
(function loadLottie() {
|
|
98
|
+
if (!document.getElementById('nibgate-lottie')) return;
|
|
99
|
+
|
|
100
|
+
function startAnim(data) {
|
|
101
|
+
var d = document.getElementById('nibgate-lottie');
|
|
102
|
+
if (d && window.lottie) {
|
|
103
|
+
window.lottie.loadAnimation({ container: d, animationData: data, loop: true, autoplay: true });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (window.lottie) {
|
|
108
|
+
if (window._lottieData) {
|
|
109
|
+
startAnim(window._lottieData);
|
|
110
|
+
} else {
|
|
111
|
+
fetch('/nibgate-unlock-key.json?t=1').then(function(r) { if (!r.ok) throw new Error(); return r.json(); }).then(function(d) { window._lottieData = d; startAnim(d); }).catch(function() {});
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (window._lottieLoading) return;
|
|
117
|
+
window._lottieLoading = true;
|
|
118
|
+
var s = document.createElement('script');
|
|
119
|
+
s.src = 'https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js';
|
|
120
|
+
s.onload = function() {
|
|
121
|
+
fetch('/nibgate-unlock-key.json?t=1').then(function(r) { if (!r.ok) throw new Error(); return r.json(); }).then(function(d) { window._lottieData = d; startAnim(d); }).catch(function() {});
|
|
122
|
+
};
|
|
123
|
+
document.head.appendChild(s);
|
|
124
|
+
})();
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
const st = card.querySelector('[data-nibgate-status]');
|
|
128
|
+
const label = card.querySelector('[data-nibgate-wallet-label]');
|
|
129
|
+
const wrap = card.querySelector('[data-nibgate-unlock-wrap]');
|
|
130
|
+
const prog = card.querySelector('[data-nibgate-unlock-progress]');
|
|
131
|
+
const shimmer = card.querySelector('[data-nibgate-shimmer]');
|
|
132
|
+
const btn = card.querySelector('[data-nibgate-unlock]');
|
|
133
|
+
|
|
134
|
+
const HOLD_MS = 1500;
|
|
135
|
+
let holdTimer = null, holdActive = false, holdComplete = false;
|
|
136
|
+
|
|
137
|
+
const ctrl = createEvmGatewayUnlock(resource, {
|
|
138
|
+
...options,
|
|
139
|
+
connectButton: null,
|
|
140
|
+
unlockButton: null,
|
|
141
|
+
walletLabel: null,
|
|
142
|
+
status: '[data-nibgate-status]',
|
|
143
|
+
unlockedTarget: '[data-nibgate-premium]',
|
|
144
|
+
onStatus: (msg) => status(st, msg),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
function shortAddress(a) { return a ? a.slice(0, 6) + '...' + a.slice(-4) : ''; }
|
|
148
|
+
|
|
149
|
+
function updateLabel() {
|
|
150
|
+
const addr = ctrl.getWalletAddress();
|
|
151
|
+
if (addr) {
|
|
152
|
+
label.innerHTML = shortAddress(addr) + ' <span data-nibgate-disconnect style="cursor:pointer">· Disconnect</span>';
|
|
153
|
+
btn.disabled = false;
|
|
154
|
+
btn.style.cursor = 'pointer';
|
|
155
|
+
} else {
|
|
156
|
+
label.textContent = 'Connect wallet';
|
|
157
|
+
btn.disabled = true;
|
|
158
|
+
btn.style.cursor = 'default';
|
|
159
|
+
btn.innerHTML = lockSVG + 'Hold to pay ' + esc(resource.price) + ' USDC';
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function setBtnText(t) { btn.innerHTML = lockSVG + t; }
|
|
164
|
+
|
|
165
|
+
function resetHold() {
|
|
166
|
+
holdActive = false;
|
|
167
|
+
holdComplete = false;
|
|
168
|
+
holdTimer = null;
|
|
169
|
+
btn.style.transform = 'scale(1)';
|
|
170
|
+
prog.style.width = '0%';
|
|
171
|
+
shimmer.style.animation = 'none';
|
|
172
|
+
shimmer.style.transform = 'translateX(-100%)';
|
|
173
|
+
if (!btn.disabled) setBtnText('Hold to pay ' + esc(resource.price) + ' USDC');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function startHold(e) {
|
|
177
|
+
if (btn.disabled || holdActive) return;
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
holdActive = true;
|
|
180
|
+
holdComplete = false;
|
|
181
|
+
btn.style.transform = 'scale(.97)';
|
|
182
|
+
prog.style.transition = 'none';
|
|
183
|
+
prog.style.width = '0%';
|
|
184
|
+
shimmer.style.animation = 'nshimmer 1s ease-in-out infinite';
|
|
185
|
+
shimmer.style.transform = 'none';
|
|
186
|
+
setBtnText('Hold\u2026');
|
|
187
|
+
requestAnimationFrame(() => {
|
|
188
|
+
prog.style.transition = 'width ' + HOLD_MS + 'ms linear';
|
|
189
|
+
prog.style.width = '100%';
|
|
190
|
+
});
|
|
191
|
+
const t1 = setTimeout(() => { if (holdActive && !holdComplete) setBtnText('Keep holding\u2026'); }, HOLD_MS * 0.4);
|
|
192
|
+
const t2 = setTimeout(() => { if (holdActive && !holdComplete) setBtnText('Almost there\u2026'); }, HOLD_MS * 0.75);
|
|
193
|
+
holdTimer = setTimeout(() => {
|
|
194
|
+
holdComplete = true;
|
|
195
|
+
holdActive = false;
|
|
196
|
+
clearTimeout(t1); clearTimeout(t2);
|
|
197
|
+
btn.style.transform = 'scale(1)';
|
|
198
|
+
shimmer.style.animation = 'none';
|
|
199
|
+
prog.style.transition = 'width .05s linear';
|
|
200
|
+
setBtnText('Processing\u2026');
|
|
201
|
+
btn.disabled = true;
|
|
202
|
+
setTimeout(() => ctrl.unlock().then(updateLabel).catch(updateLabel), 300);
|
|
203
|
+
}, HOLD_MS);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function cancelHold() {
|
|
207
|
+
if (!holdActive || holdComplete) return;
|
|
208
|
+
clearTimeout(holdTimer);
|
|
209
|
+
resetHold();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
label.addEventListener('click', (e) => {
|
|
213
|
+
if (e.target.dataset.nibgateDisconnect !== undefined) {
|
|
214
|
+
ctrl.disconnect().then(updateLabel);
|
|
215
|
+
} else if (!ctrl.getWalletAddress()) {
|
|
216
|
+
ctrl.connect().then(updateLabel).catch(() => updateLabel());
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
wrap.addEventListener('mousedown', startHold);
|
|
221
|
+
wrap.addEventListener('touchstart', startHold, { passive: false });
|
|
222
|
+
document.addEventListener('mouseup', cancelHold);
|
|
223
|
+
document.addEventListener('touchend', cancelHold);
|
|
224
|
+
wrap.addEventListener('mouseleave', cancelHold);
|
|
225
|
+
|
|
226
|
+
setTimeout(updateLabel, 200);
|
|
227
|
+
|
|
228
|
+
const cleanup = () => { document.removeEventListener('mouseup', cancelHold); document.removeEventListener('touchend', cancelHold); };
|
|
229
|
+
card.addEventListener('remove', cleanup);
|
|
230
|
+
|
|
231
|
+
return { ...ctrl, element: card, destroy: () => { cleanup(); card.remove(); } };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function renderDefaultRatingUI(container, resource, options = {}) {
|
|
235
|
+
inject();
|
|
236
|
+
|
|
237
|
+
let sel = 0, busy = false, ctrl = null, statusTimer = null;
|
|
238
|
+
|
|
239
|
+
const wrap = el('div', { cls: 'nui', style: { animation: 'nfade .2s ease-out', textAlign: 'center', padding: '28px 0' } });
|
|
240
|
+
|
|
241
|
+
const starRow = el('div', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '4px' } });
|
|
242
|
+
const stars = [];
|
|
243
|
+
for (let i = 1; i <= 5; i++) {
|
|
244
|
+
const b = el('button', {
|
|
245
|
+
type: 'button', 'aria-label': `${i} star${i > 1 ? 's' : ''}`,
|
|
246
|
+
'data-star': i,
|
|
247
|
+
style: {
|
|
248
|
+
background: 'none', border: 'none', cursor: 'pointer',
|
|
249
|
+
padding: '4px', fontSize: '31px', lineHeight: '1',
|
|
250
|
+
color: theme.border, transition: 'color .12s, transform .12s',
|
|
251
|
+
borderRadius: '4px',
|
|
252
|
+
fontFamily: 'inherit',
|
|
253
|
+
},
|
|
254
|
+
}, '☆');
|
|
255
|
+
stars.push(b);
|
|
256
|
+
starRow.appendChild(b);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const avgEl = el('span', { style: { fontSize: '17px', color: theme.muted, marginLeft: '12px' } });
|
|
260
|
+
avgEl.textContent = 'No ratings yet';
|
|
261
|
+
starRow.appendChild(avgEl);
|
|
262
|
+
|
|
263
|
+
const st = el('div', { style: { fontSize: '17px', color: theme.muted, marginTop: '8px', minHeight: '1.4em' } });
|
|
264
|
+
st.textContent = 'Tap stars to rate';
|
|
265
|
+
|
|
266
|
+
wrap.append(starRow, st);
|
|
267
|
+
|
|
268
|
+
(typeof container === 'string' ? document.querySelector(container) : container)?.appendChild(wrap);
|
|
269
|
+
|
|
270
|
+
function clearStatus() {
|
|
271
|
+
if (statusTimer) { clearTimeout(statusTimer); statusTimer = null; }
|
|
272
|
+
st.textContent = sel > 0 ? '' : 'Tap stars to rate';
|
|
273
|
+
st.style.color = theme.muted;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function setStatus(msg, color, autoClear) {
|
|
277
|
+
st.textContent = msg || '';
|
|
278
|
+
st.style.color = color || '';
|
|
279
|
+
if (autoClear) statusTimer = setTimeout(clearStatus, autoClear);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function fill(h) {
|
|
283
|
+
stars.forEach((b, i) => {
|
|
284
|
+
const active = i < h;
|
|
285
|
+
b.style.color = active ? theme.accent : theme.border;
|
|
286
|
+
b.textContent = active ? '★' : '☆';
|
|
287
|
+
b.style.transform = active ? 'scale(1.08)' : 'scale(1)';
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function setPrompt(v) {
|
|
292
|
+
const labels = ['', 'Poor', 'Below average', 'Average', 'Good', 'Excellent'];
|
|
293
|
+
setStatus(labels[v]);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function rate(v) {
|
|
297
|
+
if (busy || !ctrl) return;
|
|
298
|
+
busy = true;
|
|
299
|
+
stars.forEach(b => { b.disabled = true; b.style.cursor = 'default'; });
|
|
300
|
+
sel = v;
|
|
301
|
+
fill(v);
|
|
302
|
+
setStatus('Submitting\u2026');
|
|
303
|
+
try {
|
|
304
|
+
await ctrl.rate({ rating: v });
|
|
305
|
+
fill(v);
|
|
306
|
+
setStatus('You rated ' + v + ' \u2605'.repeat(v), theme.accent, 3000);
|
|
307
|
+
refresh();
|
|
308
|
+
} catch (e) {
|
|
309
|
+
setStatus(e?.message || 'Could not save rating. Try again.', '#dc2626');
|
|
310
|
+
fill(sel);
|
|
311
|
+
} finally {
|
|
312
|
+
busy = false;
|
|
313
|
+
stars.forEach(b => { b.disabled = false; b.style.cursor = 'pointer'; });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function refresh() {
|
|
318
|
+
const u = options.statsUrl || (resource.id ? `${options.apiBase || '/api'}/rating/${resource.id}` : null);
|
|
319
|
+
if (!u) return;
|
|
320
|
+
fetch(u).then(r => r.json()).then(d => {
|
|
321
|
+
if (d.count > 0) {
|
|
322
|
+
avgEl.textContent = `${d.average.toFixed(1)} \u2014 ${d.count} rating${d.count !== 1 ? 's' : ''}`;
|
|
323
|
+
} else {
|
|
324
|
+
avgEl.textContent = 'No ratings yet';
|
|
325
|
+
}
|
|
326
|
+
}).catch(() => {});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
stars.forEach(b => {
|
|
330
|
+
const v = parseInt(b.dataset.star);
|
|
331
|
+
b.addEventListener('mouseenter', () => {
|
|
332
|
+
if (!busy) { fill(v); setPrompt(v); }
|
|
333
|
+
});
|
|
334
|
+
b.addEventListener('mouseleave', () => {
|
|
335
|
+
if (!busy) { fill(sel); clearStatus(); }
|
|
336
|
+
});
|
|
337
|
+
b.addEventListener('click', () => rate(v));
|
|
338
|
+
b.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); rate(v); } });
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
import('./rating-ui.js').then(m => {
|
|
342
|
+
ctrl = m.createOnchainRating(resource, {
|
|
343
|
+
autoMount: false,
|
|
344
|
+
contentId: options.contentId || '0x' + (resource.id || '').replace(/-/g, ''),
|
|
345
|
+
onRated: (r) => { setStatus('Rating saved', theme.accent, 3000); refresh(); if (typeof options.onRated === 'function') options.onRated(r); },
|
|
346
|
+
onError: (e) => { setStatus(e?.message || 'Could not save rating. Try again.', '#dc2626'); if (typeof options.onError === 'function') options.onError(e); },
|
|
347
|
+
});
|
|
348
|
+
refresh();
|
|
349
|
+
}).catch(() => { setStatus('Could not load rating', '#dc2626'); });
|
|
350
|
+
|
|
351
|
+
return { element: wrap, destroy: () => wrap.remove(), refresh, setRating: (v) => { sel = v; fill(v); } };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function renderDefaultGatewayWalletUI(container, options = {}) {
|
|
355
|
+
inject();
|
|
356
|
+
let tab = 'deposit';
|
|
357
|
+
|
|
358
|
+
const wrap = el('div', { cls: 'nui', style: { animation: 'nfade .2s ease-out', border: '1px solid ' + theme.border, borderRadius: '16px', padding: '28px' } });
|
|
359
|
+
|
|
360
|
+
wrap.innerHTML = `
|
|
361
|
+
<div style="display:flex;gap:12px;margin-bottom:20px">
|
|
362
|
+
<div data-gw-wallet-card style="flex:1;background:${theme.bg};border:1px solid ${theme.border};border-radius:12px;padding:16px">
|
|
363
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
|
|
364
|
+
<div style="font-size:12px;font-weight:600;color:${theme.muted};letter-spacing:.02em">Wallet</div>
|
|
365
|
+
<button data-gw-connect style="font-size:12px;font-weight:600;cursor:pointer;border:1px solid ${theme.accent};background:transparent;color:${theme.accent};border-radius:8px;padding:4px 12px;font-family:inherit">Connect</button>
|
|
366
|
+
</div>
|
|
367
|
+
<div data-gw-wallet-balance class="nui-mono" style="font-size:24px;font-weight:700;color:${theme.fg}">—</div>
|
|
368
|
+
</div>
|
|
369
|
+
<div style="flex:1;background:${theme.bg};border:1px solid ${theme.border};border-radius:12px;padding:16px">
|
|
370
|
+
<div style="font-size:12px;font-weight:600;color:${theme.muted};margin-bottom:4px;letter-spacing:.02em">Gateway</div>
|
|
371
|
+
<div data-gw-balance class="nui-mono" style="font-size:24px;font-weight:700;color:${theme.fg}">—</div>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
<div style="display:flex;gap:0;margin-bottom:20px;border-bottom:1px solid ${theme.border}">
|
|
375
|
+
<button data-tab="deposit" style="flex:1;padding:10px 0;font-size:15px;font-weight:600;cursor:pointer;border:none;background:transparent;color:${theme.fg};border-bottom:2px solid ${theme.accent};font-family:inherit">Deposit</button>
|
|
376
|
+
<button data-tab="withdraw" style="flex:1;padding:10px 0;font-size:15px;font-weight:600;cursor:pointer;border:none;background:transparent;color:${theme.muted};border-bottom:2px solid transparent;font-family:inherit">Withdraw</button>
|
|
377
|
+
</div>
|
|
378
|
+
<div data-gw-form></div>
|
|
379
|
+
`;
|
|
380
|
+
|
|
381
|
+
(typeof container === 'string' ? document.querySelector(container) : container)?.appendChild(wrap);
|
|
382
|
+
|
|
383
|
+
const formEl = wrap.querySelector('[data-gw-form]');
|
|
384
|
+
const tabs = wrap.querySelectorAll('[data-tab]');
|
|
385
|
+
|
|
386
|
+
function select(btns, t) {
|
|
387
|
+
btns.forEach(b => {
|
|
388
|
+
const on = b.dataset.tab === t;
|
|
389
|
+
b.style.color = on ? theme.fg : theme.muted;
|
|
390
|
+
b.style.borderBottomColor = on ? theme.accent : 'transparent';
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function render(t) {
|
|
395
|
+
tab = t;
|
|
396
|
+
select(tabs, t);
|
|
397
|
+
if (t === 'deposit') {
|
|
398
|
+
formEl.innerHTML = `
|
|
399
|
+
<label class="nui-label">Amount (USDC)</label>
|
|
400
|
+
<input type="number" step="0.01" min="0" placeholder="0.00" data-gw-deposit-amount class="nui-input" style="margin-bottom:16px">
|
|
401
|
+
<button type="button" data-gw-deposit class="nui-btn nui-btn-primary" style="width:100%;padding:16px 28px;font-size:20px">Deposit</button>
|
|
402
|
+
<div data-gw-tx class="nui-mono" style="font-size:12px;color:${theme.muted};word-break:break-all;margin-top:12px;display:none"></div>
|
|
403
|
+
`;
|
|
404
|
+
} else {
|
|
405
|
+
formEl.innerHTML = `
|
|
406
|
+
<label class="nui-label">Amount (USDC)</label>
|
|
407
|
+
<input type="number" step="0.01" min="0" placeholder="0.00" data-gw-withdraw-amount class="nui-input" style="margin-bottom:16px">
|
|
408
|
+
<button type="button" data-gw-withdraw class="nui-btn nui-btn-primary" style="width:100%;padding:16px 28px;font-size:20px">Withdraw to your wallet</button>
|
|
409
|
+
<div data-gw-tx class="nui-mono" style="font-size:12px;color:${theme.muted};word-break:break-all;margin-top:12px;display:none"></div>
|
|
410
|
+
`;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
render('deposit');
|
|
415
|
+
tabs.forEach(b => b.addEventListener('click', () => render(b.dataset.tab)));
|
|
416
|
+
|
|
417
|
+
return { element: wrap, destroy: () => wrap.remove(), switchTab: render };
|
|
418
|
+
}
|
package/src/browser/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { rateResource, createOnchainRating, mountRatingUI } from './rating-ui.js
|
|
|
7
7
|
export { trackResourcePage, setupResourcePage } from './track.js';
|
|
8
8
|
export { createTransferCheckout, payWithTransfer } from './transfer.js';
|
|
9
9
|
export { contentRatingHash, NIBGATE_CONTENT_HASH_NAMESPACE, NIBGATE_REPUTATION_ABI, NIBGATE_REPUTATION_CHAIN_ID, NIBGATE_REPUTATION_CHAIN_NAME, NIBGATE_REPUTATION_CONTRACT, NIBGATE_REPUTATION_RPC_URL, rateContentOnchain, reviewTextHash } from './reputation.js';
|
|
10
|
+
export { renderDefaultUnlockUI, renderDefaultRatingUI, renderDefaultGatewayWalletUI } from './default-ui.js';
|
|
10
11
|
export { CONTENT_TYPES, ACCESS_MODES, UNLOCK_MODES, normalizeContentType, normalizeResource, normalizeAccessPolicy, normalizeUnlockPolicy, validateResourceMetadata } from '../core/resource.js';
|
|
11
12
|
export { NIBGATE_CONTENT_SETTING_FIELDS, createNibgateContentSettings, settingsToAccessPolicy, settingsToUnlockPolicy } from '../core/settings.js';
|
|
12
13
|
export { PAYMENT_RAILS, normalizePaymentRail } from '../core/payment.js';
|
|
@@ -8,7 +8,7 @@ const ZERO_HASH = `0x${'0'.repeat(64)}`;
|
|
|
8
8
|
export const NIBGATE_CONTENT_HASH_NAMESPACE = 'nibgate:content:v1';
|
|
9
9
|
export const NIBGATE_REPUTATION_CHAIN_ID = 5042002;
|
|
10
10
|
export const NIBGATE_REPUTATION_CHAIN_NAME = 'Arc Testnet';
|
|
11
|
-
export const NIBGATE_REPUTATION_RPC_URL = 'https://rpc.testnet.arc.
|
|
11
|
+
export const NIBGATE_REPUTATION_RPC_URL = 'https://rpc.testnet.arc.io';
|
|
12
12
|
export const NIBGATE_REPUTATION_CONTRACT = '0x9f27fd62e75f86a3c7addfdba443aab1f930e281';
|
|
13
13
|
|
|
14
14
|
export const NIBGATE_REPUTATION_ABI = [
|
|
@@ -27,7 +27,7 @@ export const NIBGATE_REPUTATION_ABI = [
|
|
|
27
27
|
];
|
|
28
28
|
|
|
29
29
|
function stripHex(value = '') {
|
|
30
|
-
return String(value || '').replace(/^0x/i, '').toLowerCase();
|
|
30
|
+
return String(value || '').replace(/^0x/i, '').replace(/[^0-9a-fA-F]/g, '').toLowerCase();
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function word(hex = '') {
|
package/src/index.d.ts
CHANGED
|
@@ -428,7 +428,7 @@ export declare const NIBGATE_REPUTATION_ABI: readonly unknown[];
|
|
|
428
428
|
export declare const NIBGATE_CONTENT_HASH_NAMESPACE: 'nibgate:content:v1';
|
|
429
429
|
export declare const NIBGATE_REPUTATION_CHAIN_ID: 5042002;
|
|
430
430
|
export declare const NIBGATE_REPUTATION_CHAIN_NAME: 'Arc Testnet';
|
|
431
|
-
export declare const NIBGATE_REPUTATION_RPC_URL: 'https://rpc.testnet.arc.
|
|
431
|
+
export declare const NIBGATE_REPUTATION_RPC_URL: 'https://rpc.testnet.arc.io';
|
|
432
432
|
export declare const NIBGATE_REPUTATION_CONTRACT: '0x9f27fd62e75f86a3c7addfdba443aab1f930e281';
|
|
433
433
|
export declare function contentRatingHash(resource: NibgateResource | string, options?: Record<string, unknown>): string;
|
|
434
434
|
export declare function reviewTextHash(review?: string): string;
|
|
@@ -453,3 +453,6 @@ export declare function mountRatingUI(resource: NibgateResource | string, option
|
|
|
453
453
|
|
|
454
454
|
export declare function createTransferCheckout(resource: NibgateResource | string, options: NibgateTransferCheckoutOptions): NibgateTransferCheckout;
|
|
455
455
|
export declare function payWithTransfer(resource: NibgateResource | string, options: NibgateTransferCheckoutOptions & NibgateAccessCheckOptions): Promise<NibgateAccessCheckResult>;
|
|
456
|
+
export declare function renderDefaultUnlockUI(container: HTMLElement | string, resource: NibgateResource, options?: NibgateEvmGatewayUnlockOptions): NibgateEvmGatewayUnlockController & { element: HTMLElement; destroy: () => void };
|
|
457
|
+
export declare function renderDefaultRatingUI(container: HTMLElement | string, resource: NibgateResource, options?: Record<string, unknown>): { element: HTMLElement; destroy: () => void };
|
|
458
|
+
export declare function renderDefaultGatewayWalletUI(container: HTMLElement | string, options?: Record<string, unknown>): { element: HTMLElement; destroy: () => void; switchTab: (tab: string) => void };
|
package/src/index.js
CHANGED
package/src/server/gateway.js
CHANGED
|
@@ -120,6 +120,15 @@ export async function runCircleGatewayRequirement(request, resourceInput, option
|
|
|
120
120
|
|
|
121
121
|
const resource = normalizeResource(resourceInput);
|
|
122
122
|
const recipient = resource.recipient || resource.payTo || options.recipient || serverEnv('NIBGATE_SELLER_ADDRESS') || '';
|
|
123
|
+
if (!recipient) {
|
|
124
|
+
return {
|
|
125
|
+
handled: true,
|
|
126
|
+
response: jsonResponse({
|
|
127
|
+
error: 'Gateway seller address is not configured',
|
|
128
|
+
detail: 'Set NIBGATE_SELLER_ADDRESS or pass recipient in the resource.'
|
|
129
|
+
}, { status: 400 })
|
|
130
|
+
};
|
|
131
|
+
}
|
|
123
132
|
const middleware = createGatewayMiddleware({
|
|
124
133
|
sellerAddress: recipient,
|
|
125
134
|
facilitatorUrl: options.facilitatorUrl || serverEnv('NIBGATE_FACILITATOR_URL') || serverEnv('CIRCLE_GATEWAY_FACILITATOR_URL') || 'https://gateway-api-testnet.circle.com',
|
package/src/server/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { createPaymentChallenge } from './challenge.js';
|
|
|
5
5
|
export { createManifest, manifestResponse } from './manifest.js';
|
|
6
6
|
export { createUnlockToken, verifyUnlockToken } from './proof.js';
|
|
7
7
|
export { emitHubEvent } from './hub.js';
|
|
8
|
-
export { payWithGateway, createGatewayBuyer, getGatewayBalances, depositToGateway, withdrawFromGateway } from './gateway.js';
|
|
8
|
+
export { payWithGateway, createGatewayBuyer, getGatewayBalances, depositToGateway, withdrawFromGateway, runCircleGatewayRequirement } from './gateway.js';
|
|
9
9
|
export { createNibgateServer, protect, verifyPayment } from './access.js';
|
|
10
10
|
export { circleGatewayOptions, createCircleGatewayServer } from './presets.js';
|
|
11
11
|
export { normalizeServerResource as normalizeResource, normalizeAccessPolicy, normalizeUnlockPolicy, validateResourceMetadata, UNLOCK_MODES } from '../core/resource.js';
|