@oasisomniverse/js 1.0.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.
- package/package.json +36 -0
- package/src/add-data.js +54 -0
- package/src/avatar-connect.js +126 -0
- package/src/avatar-wallet.js +60 -0
- package/src/coming-soon.js +35 -0
- package/src/confirmation.js +53 -0
- package/src/contact.js +84 -0
- package/src/cross-chain-management.js +36 -0
- package/src/edit-avatar.js +79 -0
- package/src/eggs.js +88 -0
- package/src/forgot-password.js +73 -0
- package/src/index.esm.js +15 -0
- package/src/index.js +15 -0
- package/src/karma-toast.js +50 -0
- package/src/load-data.js +56 -0
- package/src/login.js +154 -0
- package/src/manage-data.js +42 -0
- package/src/manage-eggs.js +41 -0
- package/src/manage-mission.js +42 -0
- package/src/manage-oasis-nft.js +41 -0
- package/src/message.js +57 -0
- package/src/navbar.js +80 -0
- package/src/oasis-modal.js +79 -0
- package/src/off-chain-management.js +36 -0
- package/src/provider-dropdown.js +87 -0
- package/src/purchase-oasis-nft.js +54 -0
- package/src/purchase-oasis-virtual-land-nft.js +54 -0
- package/src/reset-password.js +75 -0
- package/src/search-avatar.js +78 -0
- package/src/search-data.js +59 -0
- package/src/search-eggs.js +49 -0
- package/src/search-karma.js +48 -0
- package/src/search-mission.js +50 -0
- package/src/search-oasis-nft.js +49 -0
- package/src/search-profiles.js +50 -0
- package/src/seeds.js +83 -0
- package/src/signup.js +154 -0
- package/src/star-field.js +68 -0
- package/src/verify-email.js +77 -0
- package/src/view-achievements.js +42 -0
- package/src/view-avatar-karma.js +50 -0
- package/src/view-avatar.js +69 -0
- package/src/view-eggs.js +44 -0
- package/src/view-karma.js +41 -0
- package/src/view-leagues.js +41 -0
- package/src/view-mission.js +44 -0
- package/src/view-oasis-nft.js +46 -0
- package/src/view-tournaments.js +41 -0
- package/src/vote-karma.js +61 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisManageOasisNft — manage OASIS NFTs popup widget
|
|
3
|
+
* Usage: const w = new OasisManageOasisNft({}); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisManageOasisNft {
|
|
6
|
+
constructor({ container, apiUrl = 'https://api.web4.oasisomniverse.one', onClose = null } = {}) {
|
|
7
|
+
this._apiUrl = apiUrl; this._onClose = onClose; this._overlay = null;
|
|
8
|
+
this._injectStyles(); if (container) this._render(container);
|
|
9
|
+
}
|
|
10
|
+
_injectStyles() {
|
|
11
|
+
if (document.getElementById('oasis-popup-styles')) return;
|
|
12
|
+
const s = document.createElement('style'); s.id = 'oasis-popup-styles';
|
|
13
|
+
s.textContent = `.oasis-popup-overlay{position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,.65);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .25s}.oasis-popup-overlay.is-open{opacity:1}.oasis-popup-modal{background:#0a1628;border:1px solid rgba(0,200,255,.2);border-radius:16px;box-shadow:0 24px 64px rgba(0,0,0,.6);width:90%;max-width:640px;max-height:90vh;overflow-y:auto;transform:translateY(16px);transition:transform .25s}.oasis-popup-overlay.is-open .oasis-popup-modal{transform:none}.oasis-popup-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid rgba(0,200,255,.1)}.oasis-popup-title{font-family:'Orbitron',sans-serif;font-size:14px;font-weight:700;color:#00c8ff;letter-spacing:.08em}.oasis-popup-close{background:none;border:none;color:#7a9bbf;font-size:22px;cursor:pointer;padding:0;line-height:1}.oasis-popup-close:hover{color:#fff}.oasis-popup-body{padding:24px}.oasis-badge{display:inline-block;padding:3px 8px;border-radius:999px;font-size:10px;font-weight:700;cursor:pointer}.oasis-badge-blue{background:rgba(0,200,255,.15);color:#00c8ff}.oasis-badge-green{background:rgba(0,200,100,.15);color:#00c864}.oasis-badge-yellow{background:rgba(255,200,0,.15);color:#ffc800}.oasis-badge-red{background:rgba(255,80,80,.15);color:#ff6b6b}.oasis-table{width:100%;border-collapse:collapse;font-size:13px}.oasis-table th{text-align:left;padding:10px 12px;font-size:10px;font-weight:700;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase;border-bottom:1px solid rgba(0,200,255,.1)}.oasis-table td{padding:10px 12px;color:#fff;border-bottom:1px solid rgba(255,255,255,.05)}.oasis-table tr:hover td{background:rgba(0,200,255,.04)}.oasis-cs{text-align:center;padding:32px 16px}.oasis-cs-icon{font-size:48px;margin-bottom:12px}.oasis-cs h3{font-family:'Orbitron',sans-serif;font-size:15px;color:#00c8ff;margin:0 0 8px}.oasis-cs p{color:#7a9bbf;font-size:13px;margin:0}.oasis-msg-error{background:rgba(255,80,80,.12);border:1px solid rgba(255,80,80,.3);color:#ff6b6b;border-radius:8px;padding:10px 14px;font-size:13px}.oasis-stat-row{display:flex;gap:12px;margin-bottom:20px}.oasis-stat{flex:1;background:rgba(0,200,255,.05);border:1px solid rgba(0,200,255,.1);border-radius:10px;padding:14px;text-align:center}.oasis-stat-val{font-family:'Orbitron',sans-serif;font-size:20px;font-weight:700;color:#00c8ff}.oasis-stat-lbl{font-size:10px;color:#7a9bbf;margin-top:4px}`;
|
|
14
|
+
document.head.appendChild(s);
|
|
15
|
+
}
|
|
16
|
+
open() {
|
|
17
|
+
if (this._overlay) return;
|
|
18
|
+
this._overlay = document.createElement('div');
|
|
19
|
+
this._overlay.className = 'oasis-popup-overlay';
|
|
20
|
+
this._overlay.innerHTML = `<div class="oasis-popup-modal"><div class="oasis-popup-header"><span class="oasis-popup-title">Manage NFTs</span><button class="oasis-popup-close">×</button></div><div class="oasis-popup-body" id="oasis-mnft-body"><div class="oasis-cs"><div class="oasis-cs-icon">⏳</div><p>Loading NFTs…</p></div></div></div>`;
|
|
21
|
+
this._overlay.querySelector('.oasis-popup-close').addEventListener('click', () => this.close());
|
|
22
|
+
this._overlay.addEventListener('click', e => { if (e.target === this._overlay) this.close(); });
|
|
23
|
+
document.body.appendChild(this._overlay);
|
|
24
|
+
requestAnimationFrame(() => this._overlay.classList.add('is-open'));
|
|
25
|
+
this._load();
|
|
26
|
+
}
|
|
27
|
+
async _load() {
|
|
28
|
+
const body = this._overlay.querySelector('#oasis-mnft-body');
|
|
29
|
+
try {
|
|
30
|
+
const r = await fetch(`${this._apiUrl}/api/nft`);
|
|
31
|
+
const data = await r.json();
|
|
32
|
+
const nfts = Array.isArray(data) ? data : (data.result || []);
|
|
33
|
+
if (!nfts.length) { body.innerHTML = '<div class="oasis-cs"><div class="oasis-cs-icon">🖼️</div><h3>No NFTs</h3><p>No NFTs in your collection.</p></div>'; return; }
|
|
34
|
+
body.innerHTML = `<div class="oasis-stat-row"><div class="oasis-stat"><div class="oasis-stat-val">${nfts.length}</div><div class="oasis-stat-lbl">Total NFTs</div></div><div class="oasis-stat"><div class="oasis-stat-val">${nfts.filter(n=>n.isListed).length}</div><div class="oasis-stat-lbl">Listed</div></div></div><table class="oasis-table"><thead><tr><th>Name</th><th>Collection</th><th>Chain</th><th>Status</th></tr></thead><tbody>${nfts.map(n=>`<tr><td>${n.name||'—'}</td><td>${n.collection||'—'}</td><td><span class="oasis-badge oasis-badge-blue">${n.chain||'ETH'}</span></td><td><span class="oasis-badge ${n.isListed?'oasis-badge-green':'oasis-badge-yellow'}">${n.isListed?'Listed':'Held'}</span></td></tr>`).join('')}</tbody></table>`;
|
|
35
|
+
} catch { body.innerHTML = '<div class="oasis-msg-error">Failed to load NFTs.</div>'; }
|
|
36
|
+
}
|
|
37
|
+
close() { if (!this._overlay) return; const el = this._overlay; this._overlay = null; el.classList.remove('is-open'); el.addEventListener('transitionend', () => el.remove(), { once: true }); if (this._onClose) this._onClose(); }
|
|
38
|
+
_render(c) { c.innerHTML = '<div class="oasis-popup-body"><p style="color:#7a9bbf">Loading…</p></div>'; }
|
|
39
|
+
destroy() { this.close(); }
|
|
40
|
+
}
|
|
41
|
+
window.OasisManageOasisNft = OasisManageOasisNft;
|
package/src/message.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisMessage — messaging UI popup widget
|
|
3
|
+
* Usage: const w = new OasisMessage({ recipientId: '...', recipientName: 'Alice' }); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisMessage {
|
|
6
|
+
constructor({ container, recipientId = '', recipientName = 'Avatar', apiUrl = 'https://api.web4.oasisomniverse.one', onClose = null } = {}) {
|
|
7
|
+
this._recipientId = recipientId; this._recipientName = recipientName; this._apiUrl = apiUrl; this._onClose = onClose; this._overlay = null;
|
|
8
|
+
this._injectStyles(); if (container) this._render(container);
|
|
9
|
+
}
|
|
10
|
+
_injectStyles() {
|
|
11
|
+
if (document.getElementById('oasis-popup-styles')) return;
|
|
12
|
+
const s = document.createElement('style'); s.id = 'oasis-popup-styles';
|
|
13
|
+
s.textContent = `.oasis-popup-overlay{position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,.65);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .25s}.oasis-popup-overlay.is-open{opacity:1}.oasis-popup-modal{background:#0a1628;border:1px solid rgba(0,200,255,.2);border-radius:16px;box-shadow:0 24px 64px rgba(0,0,0,.6);width:90%;max-width:480px;max-height:90vh;overflow-y:auto;transform:translateY(16px);transition:transform .25s}.oasis-popup-overlay.is-open .oasis-popup-modal{transform:none}.oasis-popup-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid rgba(0,200,255,.1)}.oasis-popup-title{font-family:'Orbitron',sans-serif;font-size:14px;font-weight:700;color:#00c8ff;letter-spacing:.08em}.oasis-popup-close{background:none;border:none;color:#7a9bbf;font-size:22px;cursor:pointer;padding:0;line-height:1}.oasis-popup-close:hover{color:#fff}.oasis-popup-body{padding:24px}.oasis-input{background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.2);border-radius:8px;padding:10px 14px;color:#fff;font-size:14px;outline:none;transition:border-color .2s;width:100%;box-sizing:border-box}.oasis-input:focus{border-color:rgba(0,200,255,.5)}.oasis-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:11px 20px;cursor:pointer;transition:opacity .2s}.oasis-btn:hover{opacity:.85}.oasis-btn:disabled{opacity:.5;cursor:not-allowed}.oasis-chat-area{background:rgba(0,0,0,.2);border:1px solid rgba(0,200,255,.1);border-radius:10px;height:240px;overflow-y:auto;padding:12px;margin-bottom:12px;display:flex;flex-direction:column;gap:8px}.oasis-chat-bubble{border-radius:10px;padding:8px 12px;font-size:13px;color:#fff;max-width:75%;background:rgba(0,200,255,.1)}.oasis-chat-bubble.mine{background:rgba(0,128,255,.2);align-self:flex-end;margin-left:auto}.oasis-chat-meta{font-size:10px;color:#7a9bbf;margin-top:3px}.oasis-chat-input-row{display:flex;gap:8px}.oasis-chat-input-row .oasis-input{flex:1}.oasis-msg{border-radius:8px;padding:10px 14px;font-size:13px;margin-bottom:12px}.oasis-msg-error{background:rgba(255,80,80,.12);border:1px solid rgba(255,80,80,.3);color:#ff6b6b}`;
|
|
14
|
+
document.head.appendChild(s);
|
|
15
|
+
}
|
|
16
|
+
open() {
|
|
17
|
+
if (this._overlay) return;
|
|
18
|
+
this._overlay = document.createElement('div');
|
|
19
|
+
this._overlay.className = 'oasis-popup-overlay';
|
|
20
|
+
this._overlay.innerHTML = `<div class="oasis-popup-modal"><div class="oasis-popup-header"><span class="oasis-popup-title">Message: ${this._recipientName}</span><button class="oasis-popup-close">×</button></div><div class="oasis-popup-body">${this._buildBody()}</div></div>`;
|
|
21
|
+
this._overlay.querySelector('.oasis-popup-close').addEventListener('click', () => this.close());
|
|
22
|
+
this._overlay.addEventListener('click', e => { if (e.target === this._overlay) this.close(); });
|
|
23
|
+
document.body.appendChild(this._overlay);
|
|
24
|
+
requestAnimationFrame(() => this._overlay.classList.add('is-open'));
|
|
25
|
+
this._attachEvents();
|
|
26
|
+
}
|
|
27
|
+
_buildBody() {
|
|
28
|
+
return `<div id="oasis-msg-err"></div>
|
|
29
|
+
<div class="oasis-chat-area" id="oasis-chat-area">
|
|
30
|
+
<div style="color:#7a9bbf;font-size:12px;text-align:center;padding:8px">Start of your conversation with ${this._recipientName}</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="oasis-chat-input-row">
|
|
33
|
+
<input class="oasis-input" id="oasis-msg-input" type="text" placeholder="Type a message…" />
|
|
34
|
+
<button class="oasis-btn" id="oasis-msg-send" style="width:auto;padding:10px 16px">Send</button>
|
|
35
|
+
</div>`;
|
|
36
|
+
}
|
|
37
|
+
_attachEvents() {
|
|
38
|
+
const send = () => {
|
|
39
|
+
const input = this._overlay.querySelector('#oasis-msg-input');
|
|
40
|
+
const area = this._overlay.querySelector('#oasis-chat-area');
|
|
41
|
+
const text = input.value.trim();
|
|
42
|
+
if (!text) return;
|
|
43
|
+
const bubble = document.createElement('div');
|
|
44
|
+
bubble.innerHTML = `<div class="oasis-chat-bubble mine">${text}<div class="oasis-chat-meta">${new Date().toLocaleTimeString()}</div></div>`;
|
|
45
|
+
area.appendChild(bubble);
|
|
46
|
+
area.scrollTop = area.scrollHeight;
|
|
47
|
+
input.value = '';
|
|
48
|
+
fetch(`${this._apiUrl}/api/message`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ recipientId: this._recipientId, message: text }) }).catch(() => {});
|
|
49
|
+
};
|
|
50
|
+
this._overlay.querySelector('#oasis-msg-send').addEventListener('click', send);
|
|
51
|
+
this._overlay.querySelector('#oasis-msg-input').addEventListener('keydown', e => { if (e.key === 'Enter') send(); });
|
|
52
|
+
}
|
|
53
|
+
close() { if (!this._overlay) return; const el = this._overlay; this._overlay = null; el.classList.remove('is-open'); el.addEventListener('transitionend', () => el.remove(), { once: true }); if (this._onClose) this._onClose(); }
|
|
54
|
+
_render(c) { c.innerHTML = `<div class="oasis-popup-body">${this._buildBody()}</div>`; this._overlay = { querySelector: (s) => c.querySelector(s) }; this._attachEvents(); }
|
|
55
|
+
destroy() { this.close(); }
|
|
56
|
+
}
|
|
57
|
+
window.OasisMessage = OasisMessage;
|
package/src/navbar.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisNavBar — standalone responsive navigation bar widget
|
|
3
|
+
* Usage: new OasisNavBar({ container, brand, links, onLogin })
|
|
4
|
+
*/
|
|
5
|
+
(function () {
|
|
6
|
+
const CSS = `
|
|
7
|
+
.oasis-nav{position:relative;z-index:100;padding:16px 32px;display:flex;align-items:center;justify-content:space-between;background:rgba(3,7,20,.92);backdrop-filter:blur(12px);border-bottom:1px solid rgba(0,200,255,.1);font-family:'Rajdhani',sans-serif;box-sizing:border-box}
|
|
8
|
+
.oasis-nav-brand{font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.16em;color:#fff;text-decoration:none;display:flex;align-items:center;gap:10px;white-space:nowrap}
|
|
9
|
+
.oasis-nav-brand-icon{color:#00c8ff;font-size:16px}
|
|
10
|
+
.oasis-nav-links{display:flex;gap:16px;list-style:none;margin:0;padding:0}
|
|
11
|
+
.oasis-nav-links a{font-family:'Share Tech Mono',monospace;font-size:11px;letter-spacing:.08em;color:#a8bfd8;text-decoration:none;transition:color .2s;text-transform:uppercase}
|
|
12
|
+
.oasis-nav-links a:hover{color:#00c8ff}
|
|
13
|
+
.oasis-nav-right{display:flex;align-items:center;gap:10px;flex-shrink:0}
|
|
14
|
+
.oasis-nav-login{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:7px;color:#fff;font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:.08em;padding:8px 16px;cursor:pointer;white-space:nowrap;transition:opacity .2s}
|
|
15
|
+
.oasis-nav-login:hover{opacity:.85}
|
|
16
|
+
.oasis-nav-toggle{display:none;flex-direction:column;justify-content:center;gap:5px;width:32px;height:32px;background:none;border:none;cursor:pointer;padding:0}
|
|
17
|
+
.oasis-nav-toggle span{display:block;width:100%;height:2px;background:#a8bfd8;transition:transform .25s,opacity .25s;border-radius:1px}
|
|
18
|
+
.oasis-nav-toggle.open span:nth-child(1){transform:translateY(7px) rotate(45deg)}
|
|
19
|
+
.oasis-nav-toggle.open span:nth-child(2){opacity:0}
|
|
20
|
+
.oasis-nav-toggle.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
|
|
21
|
+
@media(max-width:900px){
|
|
22
|
+
.oasis-nav-toggle{display:flex}
|
|
23
|
+
.oasis-nav-links{position:fixed;top:0;right:0;height:100vh;width:min(78vw,300px);flex-direction:column;justify-content:flex-start;align-items:flex-start;gap:14px;padding:72px 28px 24px;overflow-y:auto;background:rgba(3,7,20,.98);border-left:1px solid rgba(0,200,255,.12);transform:translateX(100%);transition:transform .3s ease;z-index:200}
|
|
24
|
+
.oasis-nav-links.open{transform:translateX(0)}
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
class OasisNavBar {
|
|
29
|
+
constructor({
|
|
30
|
+
container,
|
|
31
|
+
brand = 'OASIS',
|
|
32
|
+
brandIcon = '✦',
|
|
33
|
+
links = [],
|
|
34
|
+
loginLabel = 'Login',
|
|
35
|
+
onLogin = null,
|
|
36
|
+
} = {}) {
|
|
37
|
+
this._container = typeof container === 'string' ? document.querySelector(container) : container;
|
|
38
|
+
if (!this._container) throw new Error('OasisNavBar: container not found');
|
|
39
|
+
if (!document.getElementById('oasis-nav-styles')) {
|
|
40
|
+
const s = document.createElement('style'); s.id = 'oasis-nav-styles'; s.textContent = CSS;
|
|
41
|
+
document.head.appendChild(s);
|
|
42
|
+
}
|
|
43
|
+
this._brand = brand;
|
|
44
|
+
this._brandIcon = brandIcon;
|
|
45
|
+
this._links = links;
|
|
46
|
+
this._loginLabel = loginLabel;
|
|
47
|
+
this._onLogin = onLogin;
|
|
48
|
+
this._open = false;
|
|
49
|
+
this._render();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_render() {
|
|
53
|
+
this._container.innerHTML = `
|
|
54
|
+
<nav class="oasis-nav">
|
|
55
|
+
<a class="oasis-nav-brand" href="#">
|
|
56
|
+
<span class="oasis-nav-brand-icon">${this._brandIcon}</span>
|
|
57
|
+
<span>${this._brand}</span>
|
|
58
|
+
</a>
|
|
59
|
+
<button class="oasis-nav-toggle${this._open ? ' open' : ''}" id="oasis-nav-toggle" aria-label="Menu">
|
|
60
|
+
<span></span><span></span><span></span>
|
|
61
|
+
</button>
|
|
62
|
+
<ul class="oasis-nav-links${this._open ? ' open' : ''}">
|
|
63
|
+
${this._links.map(l => `<li><a href="${l.href || '#'}">${l.label}</a></li>`).join('')}
|
|
64
|
+
</ul>
|
|
65
|
+
${this._onLogin ? `<div class="oasis-nav-right"><button class="oasis-nav-login" id="oasis-nav-login">${this._loginLabel}</button></div>` : ''}
|
|
66
|
+
</nav>`;
|
|
67
|
+
document.getElementById('oasis-nav-toggle')?.addEventListener('click', () => {
|
|
68
|
+
this._open = !this._open; this._render();
|
|
69
|
+
});
|
|
70
|
+
document.getElementById('oasis-nav-login')?.addEventListener('click', this._onLogin);
|
|
71
|
+
this._container.querySelectorAll('.oasis-nav-links a').forEach(a => {
|
|
72
|
+
a.addEventListener('click', () => { this._open = false; this._render(); });
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
destroy() { this._container.innerHTML = ''; }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
window.OasisNavBar = OasisNavBar;
|
|
80
|
+
})();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisModal — reusable modal wrapper
|
|
3
|
+
* Usage:
|
|
4
|
+
* const modal = new OasisModal({ title: 'My Modal', content: '<p>Hello</p>' })
|
|
5
|
+
* modal.open() / modal.close() / modal.destroy()
|
|
6
|
+
*/
|
|
7
|
+
class OasisModal {
|
|
8
|
+
constructor({ title = '', content = '', accentColor = '#00c8ff', onClose = null } = {}) {
|
|
9
|
+
this._onClose = onClose;
|
|
10
|
+
this._el = null;
|
|
11
|
+
this._title = title;
|
|
12
|
+
this._content = content;
|
|
13
|
+
this._accent = accentColor;
|
|
14
|
+
this._build();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_build() {
|
|
18
|
+
const el = document.createElement('div');
|
|
19
|
+
el.className = 'oasis-modal-backdrop';
|
|
20
|
+
el.innerHTML = `
|
|
21
|
+
<div class="oasis-modal-box" role="dialog" aria-modal="true">
|
|
22
|
+
<div class="oasis-modal-header" style="border-bottom-color:${this._accent}33">
|
|
23
|
+
<span class="oasis-modal-title" style="color:${this._accent}">${this._title}</span>
|
|
24
|
+
<button class="oasis-modal-close" aria-label="Close">✕</button>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="oasis-modal-body">${this._content}</div>
|
|
27
|
+
</div>
|
|
28
|
+
`;
|
|
29
|
+
el.querySelector('.oasis-modal-close').addEventListener('click', () => this.close());
|
|
30
|
+
el.addEventListener('click', e => { if (e.target === el) this.close(); });
|
|
31
|
+
document.addEventListener('keydown', this._onKey = e => { if (e.key === 'Escape') this.close(); });
|
|
32
|
+
this._el = el;
|
|
33
|
+
this._injectStyles();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_injectStyles() {
|
|
37
|
+
if (document.getElementById('oasis-modal-styles')) return;
|
|
38
|
+
const s = document.createElement('style');
|
|
39
|
+
s.id = 'oasis-modal-styles';
|
|
40
|
+
s.textContent = `
|
|
41
|
+
.oasis-modal-backdrop{position:fixed;inset:0;background:rgba(3,7,20,.75);backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;z-index:9999;opacity:0;transition:opacity .25s}
|
|
42
|
+
.oasis-modal-backdrop.is-open{opacity:1}
|
|
43
|
+
.oasis-modal-box{background:#0d1829;border:1px solid rgba(0,200,255,.18);border-radius:16px;padding:28px;width:min(520px,92vw);max-height:88vh;overflow-y:auto;transform:translateY(16px);transition:transform .25s}
|
|
44
|
+
.oasis-modal-backdrop.is-open .oasis-modal-box{transform:none}
|
|
45
|
+
.oasis-modal-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:14px;border-bottom:1px solid;margin-bottom:20px}
|
|
46
|
+
.oasis-modal-title{font-family:'Orbitron',sans-serif;font-size:15px;font-weight:700;letter-spacing:.06em}
|
|
47
|
+
.oasis-modal-close{background:none;border:none;color:#7a9bbf;font-size:18px;cursor:pointer;padding:0;line-height:1}
|
|
48
|
+
.oasis-modal-close:hover{color:#fff}
|
|
49
|
+
.oasis-modal-body{color:#a8bfd8;font-size:14px;line-height:1.6}
|
|
50
|
+
`;
|
|
51
|
+
document.head.appendChild(s);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setContent(html) {
|
|
55
|
+
this._el.querySelector('.oasis-modal-body').innerHTML = html;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
open() {
|
|
59
|
+
document.body.appendChild(this._el);
|
|
60
|
+
requestAnimationFrame(() => this._el.classList.add('is-open'));
|
|
61
|
+
document.body.style.overflow = 'hidden';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
close() {
|
|
65
|
+
this._el.classList.remove('is-open');
|
|
66
|
+
this._el.addEventListener('transitionend', () => {
|
|
67
|
+
this._el.remove();
|
|
68
|
+
document.body.style.overflow = '';
|
|
69
|
+
}, { once: true });
|
|
70
|
+
if (this._onClose) this._onClose();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
destroy() {
|
|
74
|
+
document.removeEventListener('keydown', this._onKey);
|
|
75
|
+
this._el.remove();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
window.OasisModal = OasisModal;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisOffChainManagement — manage off-chain data providers popup widget
|
|
3
|
+
* Usage: const w = new OasisOffChainManagement({}); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisOffChainManagement {
|
|
6
|
+
constructor({ container, onClose = null } = {}) {
|
|
7
|
+
this._onClose = onClose; this._overlay = null;
|
|
8
|
+
this._injectStyles(); if (container) this._render(container);
|
|
9
|
+
}
|
|
10
|
+
_injectStyles() {
|
|
11
|
+
if (document.getElementById('oasis-popup-styles')) return;
|
|
12
|
+
const s = document.createElement('style'); s.id = 'oasis-popup-styles';
|
|
13
|
+
s.textContent = `.oasis-popup-overlay{position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,.65);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .25s}.oasis-popup-overlay.is-open{opacity:1}.oasis-popup-modal{background:#0a1628;border:1px solid rgba(0,200,255,.2);border-radius:16px;box-shadow:0 24px 64px rgba(0,0,0,.6);width:90%;max-width:680px;max-height:90vh;overflow-y:auto;transform:translateY(16px);transition:transform .25s}.oasis-popup-overlay.is-open .oasis-popup-modal{transform:none}.oasis-popup-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid rgba(0,200,255,.1)}.oasis-popup-title{font-family:'Orbitron',sans-serif;font-size:14px;font-weight:700;color:#00c8ff;letter-spacing:.08em}.oasis-popup-close{background:none;border:none;color:#7a9bbf;font-size:22px;cursor:pointer;padding:0;line-height:1}.oasis-popup-close:hover{color:#fff}.oasis-popup-body{padding:24px}.oasis-info-block{background:rgba(0,200,255,.05);border:1px solid rgba(0,200,255,.1);border-radius:10px;padding:16px;margin-bottom:12px}.oasis-info-label{font-size:10px;font-weight:600;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase;margin-bottom:4px}.oasis-info-value{color:#fff;font-size:14px;font-weight:500}.oasis-badge{display:inline-block;padding:3px 8px;border-radius:999px;font-size:10px;font-weight:700}.oasis-badge-blue{background:rgba(0,200,255,.15);color:#00c8ff}.oasis-badge-green{background:rgba(0,200,100,.15);color:#00c864}.oasis-badge-red{background:rgba(255,80,80,.15);color:#ff6b6b}.oasis-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:11px 20px;cursor:pointer;transition:opacity .2s}.oasis-btn:hover{opacity:.85}.oasis-section-title{font-size:11px;font-weight:700;color:#7a9bbf;letter-spacing:.08em;text-transform:uppercase;margin:0 0 12px}.oasis-cs{text-align:center;padding:32px 16px}.oasis-cs-icon{font-size:48px;margin-bottom:12px}.oasis-cs h3{font-family:'Orbitron',sans-serif;font-size:15px;color:#00c8ff;margin:0 0 8px}.oasis-cs p{color:#7a9bbf;font-size:13px;margin:0}`;
|
|
14
|
+
document.head.appendChild(s);
|
|
15
|
+
}
|
|
16
|
+
open() {
|
|
17
|
+
if (this._overlay) return;
|
|
18
|
+
this._overlay = document.createElement('div');
|
|
19
|
+
this._overlay.className = 'oasis-popup-overlay';
|
|
20
|
+
this._overlay.innerHTML = `<div class="oasis-popup-modal"><div class="oasis-popup-header"><span class="oasis-popup-title">Off-Chain Management</span><button class="oasis-popup-close">×</button></div><div class="oasis-popup-body">${this._buildBody()}</div></div>`;
|
|
21
|
+
this._overlay.querySelector('.oasis-popup-close').addEventListener('click', () => this.close());
|
|
22
|
+
this._overlay.addEventListener('click', e => { if (e.target === this._overlay) this.close(); });
|
|
23
|
+
document.body.appendChild(this._overlay);
|
|
24
|
+
requestAnimationFrame(() => this._overlay.classList.add('is-open'));
|
|
25
|
+
}
|
|
26
|
+
_buildBody() {
|
|
27
|
+
const providers = [{ name: 'MongoDB', type: 'Document DB', status: 'connected', records: 1243 }, { name: 'Neo4j', type: 'Graph DB', status: 'connected', records: 892 }, { name: 'SQLite', type: 'Relational DB', status: 'disconnected', records: 0 }, { name: 'IPFS', type: 'Distributed FS', status: 'connected', records: 56 }];
|
|
28
|
+
return `<p class="oasis-section-title">Off-Chain Providers</p>
|
|
29
|
+
${providers.map(p => `<div class="oasis-info-block" style="display:flex;align-items:center;justify-content:space-between"><div><div class="oasis-info-label">${p.name} — ${p.type}</div><div class="oasis-info-value">${p.records.toLocaleString()} records</div></div><span class="oasis-badge ${p.status === 'connected' ? 'oasis-badge-green' : 'oasis-badge-red'}">${p.status}</span></div>`).join('')}
|
|
30
|
+
<div style="text-align:right;margin-top:16px"><button class="oasis-btn" onclick="this.textContent='Refreshing…';setTimeout(()=>this.textContent='Refresh All',1500)">Refresh All</button></div>`;
|
|
31
|
+
}
|
|
32
|
+
close() { if (!this._overlay) return; const el = this._overlay; this._overlay = null; el.classList.remove('is-open'); el.addEventListener('transitionend', () => el.remove(), { once: true }); if (this._onClose) this._onClose(); }
|
|
33
|
+
_render(c) { c.innerHTML = `<div class="oasis-popup-body">${this._buildBody()}</div>`; }
|
|
34
|
+
destroy() { this.close(); }
|
|
35
|
+
}
|
|
36
|
+
window.OasisOffChainManagement = OasisOffChainManagement;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisProviderDropdown — blockchain provider selector widget
|
|
3
|
+
* Usage: new OasisProviderDropdown({ container, onChange })
|
|
4
|
+
*/
|
|
5
|
+
(function () {
|
|
6
|
+
const PROVIDERS = [
|
|
7
|
+
{ id: 'ethereum', name: 'Ethereum', icon: '⟠', color: '#627eea' },
|
|
8
|
+
{ id: 'solana', name: 'Solana', icon: '◎', color: '#9945ff' },
|
|
9
|
+
{ id: 'polygon', name: 'Polygon', icon: '⬡', color: '#8247e5' },
|
|
10
|
+
{ id: 'arbitrum', name: 'Arbitrum', icon: '🔵', color: '#2d9cdb' },
|
|
11
|
+
{ id: 'eosio', name: 'EOSIO', icon: '🔮', color: '#443f54' },
|
|
12
|
+
{ id: 'holochain', name: 'Holochain', icon: '⬡', color: '#00e5be' },
|
|
13
|
+
{ id: 'thegraph', name: 'The Graph', icon: '📊', color: '#6f4cff' },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const CSS = `
|
|
17
|
+
.oasis-pd-shell{position:relative;font-family:'Rajdhani',sans-serif}
|
|
18
|
+
.oasis-pd-trigger{display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.25);border-radius:10px;padding:10px 14px;cursor:pointer;transition:border-color .2s;color:#fff;width:100%;box-sizing:border-box}
|
|
19
|
+
.oasis-pd-trigger:hover,.oasis-pd-trigger.open{border-color:rgba(0,200,255,.5)}
|
|
20
|
+
.oasis-pd-icon{font-size:20px;width:28px;text-align:center}
|
|
21
|
+
.oasis-pd-label{flex:1;text-align:left}
|
|
22
|
+
.oasis-pd-name{font-family:'Orbitron',sans-serif;font-size:13px;font-weight:700}
|
|
23
|
+
.oasis-pd-sub{font-size:11px;color:#7a9bbf;margin-top:2px}
|
|
24
|
+
.oasis-pd-arrow{font-size:10px;color:#7a9bbf;transition:transform .2s}
|
|
25
|
+
.oasis-pd-arrow.open{transform:rotate(180deg)}
|
|
26
|
+
.oasis-pd-menu{position:absolute;top:calc(100% + 6px);left:0;right:0;background:#0d1b2e;border:1px solid rgba(0,200,255,.2);border-radius:10px;overflow:hidden;z-index:999;box-shadow:0 8px 32px rgba(0,0,0,.5)}
|
|
27
|
+
.oasis-pd-option{display:flex;align-items:center;gap:10px;padding:10px 14px;cursor:pointer;transition:background .15s;color:#e0f0ff}
|
|
28
|
+
.oasis-pd-option:hover{background:rgba(0,200,255,.1)}
|
|
29
|
+
.oasis-pd-option.selected{background:rgba(0,200,255,.12);border-left:3px solid #00c8ff}
|
|
30
|
+
.oasis-pd-opt-icon{font-size:18px;width:24px;text-align:center}
|
|
31
|
+
.oasis-pd-opt-name{font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700}
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
class OasisProviderDropdown {
|
|
35
|
+
constructor({ container, onChange, defaultProvider = 'ethereum' } = {}) {
|
|
36
|
+
this._container = typeof container === 'string' ? document.querySelector(container) : container;
|
|
37
|
+
if (!this._container) throw new Error('OasisProviderDropdown: container not found');
|
|
38
|
+
if (!document.getElementById('oasis-pd-styles')) {
|
|
39
|
+
const s = document.createElement('style'); s.id = 'oasis-pd-styles'; s.textContent = CSS;
|
|
40
|
+
document.head.appendChild(s);
|
|
41
|
+
}
|
|
42
|
+
this._selected = PROVIDERS.find(p => p.id === defaultProvider) || PROVIDERS[0];
|
|
43
|
+
this._open = false;
|
|
44
|
+
this._onChange = onChange || (() => {});
|
|
45
|
+
this._render();
|
|
46
|
+
document.addEventListener('click', this._onDocClick.bind(this));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_render() {
|
|
50
|
+
const p = this._selected;
|
|
51
|
+
this._container.innerHTML = `
|
|
52
|
+
<div class="oasis-pd-shell">
|
|
53
|
+
<div class="oasis-pd-trigger${this._open ? ' open' : ''}" id="oasis-pd-trigger">
|
|
54
|
+
<div class="oasis-pd-icon" style="color:${p.color}">${p.icon}</div>
|
|
55
|
+
<div class="oasis-pd-label">
|
|
56
|
+
<div class="oasis-pd-name">${p.name}</div>
|
|
57
|
+
<div class="oasis-pd-sub">Active Provider</div>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="oasis-pd-arrow${this._open ? ' open' : ''}">▼</div>
|
|
60
|
+
</div>
|
|
61
|
+
${this._open ? `<div class="oasis-pd-menu" id="oasis-pd-menu">
|
|
62
|
+
${PROVIDERS.map(pr => `
|
|
63
|
+
<div class="oasis-pd-option${pr.id === p.id ? ' selected' : ''}" data-id="${pr.id}">
|
|
64
|
+
<div class="oasis-pd-opt-icon" style="color:${pr.color}">${pr.icon}</div>
|
|
65
|
+
<div class="oasis-pd-opt-name">${pr.name}</div>
|
|
66
|
+
</div>`).join('')}
|
|
67
|
+
</div>` : ''}
|
|
68
|
+
</div>`;
|
|
69
|
+
document.getElementById('oasis-pd-trigger')?.addEventListener('click', e => { e.stopPropagation(); this._toggle(); });
|
|
70
|
+
this._container.querySelectorAll('[data-id]').forEach(el => {
|
|
71
|
+
el.addEventListener('click', e => { e.stopPropagation(); this._select(el.dataset.id); });
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_toggle() { this._open = !this._open; this._render(); }
|
|
76
|
+
_select(id) {
|
|
77
|
+
this._selected = PROVIDERS.find(p => p.id === id) || this._selected;
|
|
78
|
+
this._open = false; this._render(); this._onChange(this._selected);
|
|
79
|
+
}
|
|
80
|
+
_onDocClick() { if (this._open) { this._open = false; this._render(); } }
|
|
81
|
+
|
|
82
|
+
get value() { return this._selected; }
|
|
83
|
+
destroy() { document.removeEventListener('click', this._onDocClick.bind(this)); this._container.innerHTML = ''; }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
window.OasisProviderDropdown = OasisProviderDropdown;
|
|
87
|
+
})();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisPurchaseOasisNft — purchase an OASIS NFT popup widget
|
|
3
|
+
* Usage: const w = new OasisPurchaseOasisNft({ nftId: '...', onSuccess: (tx) => {} }); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisPurchaseOasisNft {
|
|
6
|
+
constructor({ container, nftId = '', nftName = 'OASIS NFT', price = '0.00', currency = 'ETH', apiUrl = 'https://api.web4.oasisomniverse.one', onSuccess = null, onClose = null } = {}) {
|
|
7
|
+
this._nftId = nftId; this._nftName = nftName; this._price = price; this._currency = currency;
|
|
8
|
+
this._apiUrl = apiUrl; this._onSuccess = onSuccess; this._onClose = onClose; this._overlay = null;
|
|
9
|
+
this._injectStyles(); if (container) this._render(container);
|
|
10
|
+
}
|
|
11
|
+
_injectStyles() {
|
|
12
|
+
if (document.getElementById('oasis-popup-styles')) return;
|
|
13
|
+
const s = document.createElement('style'); s.id = 'oasis-popup-styles';
|
|
14
|
+
s.textContent = `.oasis-popup-overlay{position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,.65);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .25s}.oasis-popup-overlay.is-open{opacity:1}.oasis-popup-modal{background:#0a1628;border:1px solid rgba(0,200,255,.2);border-radius:16px;box-shadow:0 24px 64px rgba(0,0,0,.6);width:90%;max-width:480px;max-height:90vh;overflow-y:auto;transform:translateY(16px);transition:transform .25s}.oasis-popup-overlay.is-open .oasis-popup-modal{transform:none}.oasis-popup-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid rgba(0,200,255,.1)}.oasis-popup-title{font-family:'Orbitron',sans-serif;font-size:14px;font-weight:700;color:#00c8ff;letter-spacing:.08em}.oasis-popup-close{background:none;border:none;color:#7a9bbf;font-size:22px;cursor:pointer;padding:0;line-height:1}.oasis-popup-close:hover{color:#fff}.oasis-popup-body{padding:24px}.oasis-field{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}.oasis-label{font-size:11px;font-weight:600;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase}.oasis-select{background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.2);border-radius:8px;padding:10px 14px;color:#fff;font-size:14px;outline:none;transition:border-color .2s;width:100%;box-sizing:border-box}.oasis-select:focus{border-color:rgba(0,200,255,.5)}.oasis-select option{background:#0a1628}.oasis-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:11px 20px;cursor:pointer;transition:opacity .2s;width:100%}.oasis-btn:hover{opacity:.85}.oasis-btn:disabled{opacity:.5;cursor:not-allowed}.oasis-msg{border-radius:8px;padding:10px 14px;font-size:13px;margin-bottom:16px}.oasis-msg-error{background:rgba(255,80,80,.12);border:1px solid rgba(255,80,80,.3);color:#ff6b6b}.oasis-msg-success{background:rgba(0,200,100,.12);border:1px solid rgba(0,200,100,.3);color:#00c864}.oasis-info-block{background:rgba(0,200,255,.05);border:1px solid rgba(0,200,255,.1);border-radius:10px;padding:16px;margin-bottom:16px}.oasis-info-label{font-size:10px;font-weight:600;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase;margin-bottom:4px}.oasis-info-value{color:#fff;font-size:14px;font-weight:500}.oasis-price-big{font-family:'Orbitron',sans-serif;font-size:28px;font-weight:700;color:#00c8ff;margin:4px 0}.oasis-nft-preview{text-align:center;padding:20px;background:rgba(0,200,255,.04);border-radius:10px;margin-bottom:20px;border:1px solid rgba(0,200,255,.1)}.oasis-nft-icon{font-size:56px;margin-bottom:8px}.oasis-nft-preview-name{font-size:15px;font-weight:700;color:#fff}.oasis-divider{border:none;border-top:1px solid rgba(0,200,255,.1);margin:16px 0}`;
|
|
15
|
+
document.head.appendChild(s);
|
|
16
|
+
}
|
|
17
|
+
open() {
|
|
18
|
+
if (this._overlay) return;
|
|
19
|
+
this._overlay = document.createElement('div');
|
|
20
|
+
this._overlay.className = 'oasis-popup-overlay';
|
|
21
|
+
this._overlay.innerHTML = `<div class="oasis-popup-modal"><div class="oasis-popup-header"><span class="oasis-popup-title">Purchase NFT</span><button class="oasis-popup-close">×</button></div><div class="oasis-popup-body">${this._buildBody()}</div></div>`;
|
|
22
|
+
this._overlay.querySelector('.oasis-popup-close').addEventListener('click', () => this.close());
|
|
23
|
+
this._overlay.addEventListener('click', e => { if (e.target === this._overlay) this.close(); });
|
|
24
|
+
document.body.appendChild(this._overlay);
|
|
25
|
+
requestAnimationFrame(() => this._overlay.classList.add('is-open'));
|
|
26
|
+
this._overlay.querySelector('#oasis-pnft-confirm').addEventListener('click', () => this._doPurchase());
|
|
27
|
+
}
|
|
28
|
+
_buildBody() {
|
|
29
|
+
return `<div class="oasis-nft-preview"><div class="oasis-nft-icon">🖼️</div><div class="oasis-nft-preview-name">${this._nftName}</div></div>
|
|
30
|
+
<div id="oasis-pnft-msg"></div>
|
|
31
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Price</div><div class="oasis-price-big">${this._price} ${this._currency}</div></div>
|
|
32
|
+
<div class="oasis-field"><label class="oasis-label">Pay with</label><select class="oasis-select" id="oasis-pnft-wallet"><option value="metamask">MetaMask</option><option value="walletconnect">WalletConnect</option><option value="seeds">SEEDS Token</option></select></div>
|
|
33
|
+
<hr class="oasis-divider" />
|
|
34
|
+
<p style="color:#7a9bbf;font-size:12px;margin:0 0 16px">By confirming, you agree to the OASIS NFT Terms of Service. Transaction fees may apply.</p>
|
|
35
|
+
<button class="oasis-btn" id="oasis-pnft-confirm">Confirm Purchase</button>`;
|
|
36
|
+
}
|
|
37
|
+
async _doPurchase() {
|
|
38
|
+
const msgEl = this._overlay.querySelector('#oasis-pnft-msg');
|
|
39
|
+
const btn = this._overlay.querySelector('#oasis-pnft-confirm');
|
|
40
|
+
btn.textContent = 'Processing…'; btn.disabled = true;
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(`${this._apiUrl}/api/nft/purchase`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ nftId: this._nftId, wallet: this._overlay.querySelector('#oasis-pnft-wallet').value }) });
|
|
43
|
+
const data = await res.json();
|
|
44
|
+
if (!res.ok || data.isError) throw new Error(data.message || 'Purchase failed');
|
|
45
|
+
msgEl.innerHTML = '<div class="oasis-msg oasis-msg-success">Purchase successful! NFT transferred to your wallet.</div>';
|
|
46
|
+
if (this._onSuccess) this._onSuccess(data);
|
|
47
|
+
btn.textContent = 'Purchased!';
|
|
48
|
+
} catch (e) { msgEl.innerHTML = `<div class="oasis-msg oasis-msg-error">${e.message}</div>`; btn.textContent = 'Confirm Purchase'; btn.disabled = false; }
|
|
49
|
+
}
|
|
50
|
+
close() { if (!this._overlay) return; const el = this._overlay; this._overlay = null; el.classList.remove('is-open'); el.addEventListener('transitionend', () => el.remove(), { once: true }); if (this._onClose) this._onClose(); }
|
|
51
|
+
_render(c) { c.innerHTML = `<div class="oasis-popup-body">${this._buildBody()}</div>`; }
|
|
52
|
+
destroy() { this.close(); }
|
|
53
|
+
}
|
|
54
|
+
window.OasisPurchaseOasisNft = OasisPurchaseOasisNft;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisPurchaseOasisVirtualLandNft — purchase OASIS virtual land NFT popup widget
|
|
3
|
+
* Usage: const w = new OasisPurchaseOasisVirtualLandNft({ landId: '...' }); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisPurchaseOasisVirtualLandNft {
|
|
6
|
+
constructor({ container, landId = '', landName = 'Virtual Land', price = '0.00', currency = 'ETH', apiUrl = 'https://api.web4.oasisomniverse.one', onSuccess = null, onClose = null } = {}) {
|
|
7
|
+
this._landId = landId; this._landName = landName; this._price = price; this._currency = currency;
|
|
8
|
+
this._apiUrl = apiUrl; this._onSuccess = onSuccess; this._onClose = onClose; this._overlay = null;
|
|
9
|
+
this._injectStyles(); if (container) this._render(container);
|
|
10
|
+
}
|
|
11
|
+
_injectStyles() {
|
|
12
|
+
if (document.getElementById('oasis-popup-styles')) return;
|
|
13
|
+
const s = document.createElement('style'); s.id = 'oasis-popup-styles';
|
|
14
|
+
s.textContent = `.oasis-popup-overlay{position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,.65);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .25s}.oasis-popup-overlay.is-open{opacity:1}.oasis-popup-modal{background:#0a1628;border:1px solid rgba(0,200,255,.2);border-radius:16px;box-shadow:0 24px 64px rgba(0,0,0,.6);width:90%;max-width:480px;max-height:90vh;overflow-y:auto;transform:translateY(16px);transition:transform .25s}.oasis-popup-overlay.is-open .oasis-popup-modal{transform:none}.oasis-popup-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid rgba(0,200,255,.1)}.oasis-popup-title{font-family:'Orbitron',sans-serif;font-size:14px;font-weight:700;color:#00c8ff;letter-spacing:.08em}.oasis-popup-close{background:none;border:none;color:#7a9bbf;font-size:22px;cursor:pointer;padding:0;line-height:1}.oasis-popup-close:hover{color:#fff}.oasis-popup-body{padding:24px}.oasis-field{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}.oasis-label{font-size:11px;font-weight:600;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase}.oasis-select{background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.2);border-radius:8px;padding:10px 14px;color:#fff;font-size:14px;outline:none;width:100%;box-sizing:border-box}.oasis-select option{background:#0a1628}.oasis-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:11px 20px;cursor:pointer;transition:opacity .2s;width:100%}.oasis-btn:hover{opacity:.85}.oasis-btn:disabled{opacity:.5;cursor:not-allowed}.oasis-msg{border-radius:8px;padding:10px 14px;font-size:13px;margin-bottom:16px}.oasis-msg-error{background:rgba(255,80,80,.12);border:1px solid rgba(255,80,80,.3);color:#ff6b6b}.oasis-msg-success{background:rgba(0,200,100,.12);border:1px solid rgba(0,200,100,.3);color:#00c864}.oasis-info-block{background:rgba(0,200,255,.05);border:1px solid rgba(0,200,255,.1);border-radius:10px;padding:16px;margin-bottom:16px}.oasis-info-label{font-size:10px;font-weight:600;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase;margin-bottom:4px}.oasis-info-value{color:#fff;font-size:14px;font-weight:500}.oasis-price-big{font-family:'Orbitron',sans-serif;font-size:28px;font-weight:700;color:#00c8ff}.oasis-land-preview{background:linear-gradient(135deg,rgba(0,200,80,.1),rgba(0,100,200,.1));border:1px solid rgba(0,200,80,.2);border-radius:12px;padding:24px;text-align:center;margin-bottom:20px}.oasis-land-icon{font-size:48px;margin-bottom:8px}.oasis-land-name{font-size:15px;font-weight:700;color:#fff}.oasis-divider{border:none;border-top:1px solid rgba(0,200,255,.1);margin:16px 0}`;
|
|
15
|
+
document.head.appendChild(s);
|
|
16
|
+
}
|
|
17
|
+
open() {
|
|
18
|
+
if (this._overlay) return;
|
|
19
|
+
this._overlay = document.createElement('div');
|
|
20
|
+
this._overlay.className = 'oasis-popup-overlay';
|
|
21
|
+
this._overlay.innerHTML = `<div class="oasis-popup-modal"><div class="oasis-popup-header"><span class="oasis-popup-title">Purchase Virtual Land</span><button class="oasis-popup-close">×</button></div><div class="oasis-popup-body">${this._buildBody()}</div></div>`;
|
|
22
|
+
this._overlay.querySelector('.oasis-popup-close').addEventListener('click', () => this.close());
|
|
23
|
+
this._overlay.addEventListener('click', e => { if (e.target === this._overlay) this.close(); });
|
|
24
|
+
document.body.appendChild(this._overlay);
|
|
25
|
+
requestAnimationFrame(() => this._overlay.classList.add('is-open'));
|
|
26
|
+
this._overlay.querySelector('#oasis-pvl-confirm').addEventListener('click', () => this._doPurchase());
|
|
27
|
+
}
|
|
28
|
+
_buildBody() {
|
|
29
|
+
return `<div class="oasis-land-preview"><div class="oasis-land-icon">🌍</div><div class="oasis-land-name">${this._landName}</div><div style="color:#7a9bbf;font-size:12px;margin-top:4px">Our World Virtual Land Parcel</div></div>
|
|
30
|
+
<div id="oasis-pvl-msg"></div>
|
|
31
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Price</div><div class="oasis-price-big">${this._price} ${this._currency}</div></div>
|
|
32
|
+
<div class="oasis-field"><label class="oasis-label">Pay with</label><select class="oasis-select" id="oasis-pvl-wallet"><option value="metamask">MetaMask (ETH)</option><option value="solana">Solana Wallet</option><option value="seeds">SEEDS Token</option></select></div>
|
|
33
|
+
<hr class="oasis-divider" />
|
|
34
|
+
<p style="color:#7a9bbf;font-size:12px;margin:0 0 16px">Virtual land NFTs grant you ownership of a parcel in Our World. Transaction fees apply.</p>
|
|
35
|
+
<button class="oasis-btn" id="oasis-pvl-confirm">Purchase Land NFT</button>`;
|
|
36
|
+
}
|
|
37
|
+
async _doPurchase() {
|
|
38
|
+
const msgEl = this._overlay.querySelector('#oasis-pvl-msg');
|
|
39
|
+
const btn = this._overlay.querySelector('#oasis-pvl-confirm');
|
|
40
|
+
btn.textContent = 'Processing…'; btn.disabled = true;
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(`${this._apiUrl}/api/nft/land/purchase`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ landId: this._landId, wallet: this._overlay.querySelector('#oasis-pvl-wallet').value }) });
|
|
43
|
+
const data = await res.json();
|
|
44
|
+
if (!res.ok || data.isError) throw new Error(data.message || 'Purchase failed');
|
|
45
|
+
msgEl.innerHTML = '<div class="oasis-msg oasis-msg-success">Land NFT purchased! Welcome to Our World.</div>';
|
|
46
|
+
if (this._onSuccess) this._onSuccess(data);
|
|
47
|
+
btn.textContent = 'Purchased!';
|
|
48
|
+
} catch (e) { msgEl.innerHTML = `<div class="oasis-msg oasis-msg-error">${e.message}</div>`; btn.textContent = 'Purchase Land NFT'; btn.disabled = false; }
|
|
49
|
+
}
|
|
50
|
+
close() { if (!this._overlay) return; const el = this._overlay; this._overlay = null; el.classList.remove('is-open'); el.addEventListener('transitionend', () => el.remove(), { once: true }); if (this._onClose) this._onClose(); }
|
|
51
|
+
_render(c) { c.innerHTML = `<div class="oasis-popup-body">${this._buildBody()}</div>`; }
|
|
52
|
+
destroy() { this.close(); }
|
|
53
|
+
}
|
|
54
|
+
window.OasisPurchaseOasisVirtualLandNft = OasisPurchaseOasisVirtualLandNft;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisResetPassword — set new password with token popup widget
|
|
3
|
+
* Usage: const w = new OasisResetPassword({ token: 'abc123', onSuccess: () => {} }); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisResetPassword {
|
|
6
|
+
constructor({ container, token = '', apiUrl = 'https://api.web4.oasisomniverse.one', onSuccess = null, onClose = null } = {}) {
|
|
7
|
+
this._token = token; this._apiUrl = apiUrl; this._onSuccess = onSuccess; this._onClose = onClose; this._overlay = null;
|
|
8
|
+
this._injectStyles();
|
|
9
|
+
if (container) this._render(container);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
_injectStyles() {
|
|
13
|
+
if (document.getElementById('oasis-popup-styles')) return;
|
|
14
|
+
const s = document.createElement('style'); s.id = 'oasis-popup-styles';
|
|
15
|
+
s.textContent = `.oasis-popup-overlay{position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,.65);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .25s}.oasis-popup-overlay.is-open{opacity:1}.oasis-popup-modal{background:#0a1628;border:1px solid rgba(0,200,255,.2);border-radius:16px;box-shadow:0 24px 64px rgba(0,0,0,.6);width:90%;max-width:480px;max-height:90vh;overflow-y:auto;transform:translateY(16px);transition:transform .25s}.oasis-popup-overlay.is-open .oasis-popup-modal{transform:none}.oasis-popup-modal.lg{max-width:900px}.oasis-popup-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid rgba(0,200,255,.1)}.oasis-popup-title{font-family:'Orbitron',sans-serif;font-size:14px;font-weight:700;color:#00c8ff;letter-spacing:.08em}.oasis-popup-close{background:none;border:none;color:#7a9bbf;font-size:22px;cursor:pointer;padding:0;line-height:1}.oasis-popup-close:hover{color:#fff}.oasis-popup-body{padding:24px}.oasis-field{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}.oasis-label{font-size:11px;font-weight:600;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase}.oasis-input,.oasis-select,.oasis-textarea{background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.2);border-radius:8px;padding:10px 14px;color:#fff;font-size:14px;outline:none;transition:border-color .2s;width:100%;box-sizing:border-box}.oasis-input:focus{border-color:rgba(0,200,255,.5)}.oasis-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:11px 20px;cursor:pointer;transition:opacity .2s;width:100%}.oasis-btn:hover{opacity:.85}.oasis-btn:disabled{opacity:.5;cursor:not-allowed}.oasis-msg{border-radius:8px;padding:10px 14px;font-size:13px;margin-bottom:16px}.oasis-msg-error{background:rgba(255,80,80,.12);border:1px solid rgba(255,80,80,.3);color:#ff6b6b}.oasis-msg-success{background:rgba(0,200,100,.12);border:1px solid rgba(0,200,100,.3);color:#00c864}.oasis-grid-2{display:grid;grid-template-columns:1fr 1fr;gap:16px}.oasis-info-block{background:rgba(0,200,255,.05);border:1px solid rgba(0,200,255,.1);border-radius:10px;padding:16px;margin-bottom:12px}.oasis-info-label{font-size:10px;font-weight:600;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase;margin-bottom:4px}.oasis-info-value{color:#fff;font-size:14px;font-weight:500}.oasis-cs{text-align:center;padding:32px 16px}.oasis-cs-icon{font-size:48px;margin-bottom:12px}.oasis-cs h3{font-family:'Orbitron',sans-serif;font-size:15px;color:#00c8ff;margin:0 0 8px}.oasis-cs p{color:#7a9bbf;font-size:13px;margin:0}.oasis-table{width:100%;border-collapse:collapse;font-size:13px}.oasis-table th{text-align:left;padding:10px 12px;font-size:10px;font-weight:700;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase;border-bottom:1px solid rgba(0,200,255,.1)}.oasis-table td{padding:10px 12px;color:#fff;border-bottom:1px solid rgba(255,255,255,.05)}.oasis-badge{display:inline-block;padding:3px 8px;border-radius:999px;font-size:10px;font-weight:700}.oasis-badge-blue{background:rgba(0,200,255,.15);color:#00c8ff}.oasis-badge-green{background:rgba(0,200,100,.15);color:#00c864}.oasis-badge-red{background:rgba(255,80,80,.15);color:#ff6b6b}.oasis-search-row{display:flex;gap:10px;margin-bottom:16px}.oasis-search-row .oasis-input{flex:1}.oasis-stat-row{display:flex;gap:12px;margin-bottom:20px}.oasis-stat{flex:1;background:rgba(0,200,255,.05);border:1px solid rgba(0,200,255,.1);border-radius:10px;padding:14px;text-align:center}.oasis-stat-val{font-family:'Orbitron',sans-serif;font-size:20px;font-weight:700;color:#00c8ff}.oasis-stat-lbl{font-size:10px;color:#7a9bbf;margin-top:4px}.oasis-divider{border:none;border-top:1px solid rgba(0,200,255,.1);margin:16px 0}.oasis-section-title{font-size:11px;font-weight:700;color:#7a9bbf;letter-spacing:.08em;text-transform:uppercase;margin:0 0 12px}.oasis-radio-group{display:flex;gap:16px;margin-bottom:16px}.oasis-radio-label{display:flex;align-items:center;gap:8px;font-size:13px;color:#fff;cursor:pointer}.oasis-radio-label input[type=radio]{accent-color:#00c8ff}.oasis-link{background:none;border:none;color:#00c8ff;font-size:13px;cursor:pointer;padding:0;text-decoration:underline}.oasis-chat-area{background:rgba(0,0,0,.2);border:1px solid rgba(0,200,255,.1);border-radius:10px;height:220px;overflow-y:auto;padding:12px;margin-bottom:12px;display:flex;flex-direction:column;gap:8px}.oasis-chat-bubble{background:rgba(0,200,255,.1);border-radius:10px;padding:8px 12px;font-size:13px;color:#fff;max-width:75%}.oasis-chat-bubble.mine{background:rgba(0,128,255,.2);align-self:flex-end}.oasis-chat-input-row{display:flex;gap:8px}.oasis-chat-input-row .oasis-input{flex:1}`;
|
|
16
|
+
document.head.appendChild(s);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
open() {
|
|
20
|
+
if (this._overlay) return;
|
|
21
|
+
this._overlay = document.createElement('div');
|
|
22
|
+
this._overlay.className = 'oasis-popup-overlay';
|
|
23
|
+
this._overlay.innerHTML = `<div class="oasis-popup-modal">
|
|
24
|
+
<div class="oasis-popup-header"><span class="oasis-popup-title">New Password</span><button class="oasis-popup-close">×</button></div>
|
|
25
|
+
<div class="oasis-popup-body">${this._buildBody()}</div>
|
|
26
|
+
</div>`;
|
|
27
|
+
this._overlay.querySelector('.oasis-popup-close').addEventListener('click', () => this.close());
|
|
28
|
+
this._overlay.addEventListener('click', e => { if (e.target === this._overlay) this.close(); });
|
|
29
|
+
document.body.appendChild(this._overlay);
|
|
30
|
+
requestAnimationFrame(() => this._overlay.classList.add('is-open'));
|
|
31
|
+
this._overlay.querySelector('#oasis-rp-submit').addEventListener('click', () => this._doReset());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_buildBody() {
|
|
35
|
+
return `
|
|
36
|
+
<div id="oasis-rp-msg"></div>
|
|
37
|
+
<div class="oasis-field"><label class="oasis-label">New Password</label><input class="oasis-input" id="oasis-rp-pass" type="password" placeholder="••••••••" /></div>
|
|
38
|
+
<div class="oasis-field"><label class="oasis-label">Confirm New Password</label><input class="oasis-input" id="oasis-rp-pass2" type="password" placeholder="••••••••" /></div>
|
|
39
|
+
<button class="oasis-btn" id="oasis-rp-submit">Set New Password</button>
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async _doReset() {
|
|
44
|
+
const pass = this._overlay.querySelector('#oasis-rp-pass').value;
|
|
45
|
+
const pass2 = this._overlay.querySelector('#oasis-rp-pass2').value;
|
|
46
|
+
const msgEl = this._overlay.querySelector('#oasis-rp-msg');
|
|
47
|
+
const btn = this._overlay.querySelector('#oasis-rp-submit');
|
|
48
|
+
if (!pass) { msgEl.innerHTML = '<div class="oasis-msg oasis-msg-error">Please enter a new password.</div>'; return; }
|
|
49
|
+
if (pass !== pass2) { msgEl.innerHTML = '<div class="oasis-msg oasis-msg-error">Passwords do not match.</div>'; return; }
|
|
50
|
+
btn.textContent = 'Saving…'; btn.disabled = true;
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(`${this._apiUrl}/api/avatar/reset-password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: this._token, password: pass }) });
|
|
53
|
+
const data = await res.json();
|
|
54
|
+
if (!res.ok || data.isError) throw new Error(data.message || 'Reset failed');
|
|
55
|
+
msgEl.innerHTML = '<div class="oasis-msg oasis-msg-success">Password updated! You can now log in.</div>';
|
|
56
|
+
if (this._onSuccess) this._onSuccess();
|
|
57
|
+
} catch (e) {
|
|
58
|
+
msgEl.innerHTML = `<div class="oasis-msg oasis-msg-error">${e.message}</div>`;
|
|
59
|
+
btn.textContent = 'Set New Password'; btn.disabled = false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
close() {
|
|
64
|
+
if (!this._overlay) return;
|
|
65
|
+
const el = this._overlay; this._overlay = null;
|
|
66
|
+
el.classList.remove('is-open');
|
|
67
|
+
el.addEventListener('transitionend', () => el.remove(), { once: true });
|
|
68
|
+
if (this._onClose) this._onClose();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
_render(container) { container.innerHTML = `<div class="oasis-popup-body">${this._buildBody()}</div>`; }
|
|
72
|
+
destroy() { this.close(); }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
window.OasisResetPassword = OasisResetPassword;
|