@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
|
+
* OasisViewLeagues — view game leagues popup widget
|
|
3
|
+
* Usage: const w = new OasisViewLeagues({}); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisViewLeagues {
|
|
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:600px;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}.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-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}`;
|
|
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">View Leagues</span><button class="oasis-popup-close">×</button></div><div class="oasis-popup-body" id="oasis-vl-body"><div class="oasis-cs"><div class="oasis-cs-icon">⏳</div><p>Loading leagues…</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-vl-body');
|
|
29
|
+
try {
|
|
30
|
+
const r = await fetch(`${this._apiUrl}/api/game/leagues`);
|
|
31
|
+
const data = await r.json();
|
|
32
|
+
const leagues = Array.isArray(data) ? data : (data.result || []);
|
|
33
|
+
if (!leagues.length) { body.innerHTML = '<div class="oasis-cs"><div class="oasis-cs-icon">🏆</div><h3>No Leagues</h3><p>No active leagues found.</p></div>'; return; }
|
|
34
|
+
body.innerHTML = `<table class="oasis-table"><thead><tr><th>League</th><th>Members</th><th>Season</th><th>Status</th></tr></thead><tbody>${leagues.map(l=>`<tr><td>${l.name||'—'}</td><td>${l.memberCount??0}</td><td>${l.season||'—'}</td><td><span class="oasis-badge ${l.isActive?'oasis-badge-green':'oasis-badge-yellow'}">${l.isActive?'Active':'Ended'}</span></td></tr>`).join('')}</tbody></table>`;
|
|
35
|
+
} catch { body.innerHTML = '<div class="oasis-msg-error">Failed to load leagues.</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.OasisViewLeagues = OasisViewLeagues;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisViewMission — view mission details popup widget
|
|
3
|
+
* Usage: const w = new OasisViewMission({ missionId: '...' }); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisViewMission {
|
|
6
|
+
constructor({ container, missionId = '', apiUrl = 'https://api.web4.oasisomniverse.one', onClose = null } = {}) {
|
|
7
|
+
this._missionId = missionId; 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:520px;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}.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-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-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-mission-hero{text-align:center;padding:20px 0 16px;border-bottom:1px solid rgba(0,200,255,.1);margin-bottom:20px}.oasis-mission-icon{font-size:48px}.oasis-mission-name{font-size:18px;font-weight:700;color:#fff;margin-top:8px}`;
|
|
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">Mission Details</span><button class="oasis-popup-close">×</button></div><div class="oasis-popup-body" id="oasis-vm-body"><div class="oasis-cs"><div class="oasis-cs-icon">⏳</div><p>Loading mission…</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-vm-body');
|
|
29
|
+
try {
|
|
30
|
+
const r = await fetch(`${this._apiUrl}/api/mission/${this._missionId}`);
|
|
31
|
+
const data = await r.json();
|
|
32
|
+
const m = data.result || data;
|
|
33
|
+
body.innerHTML = `<div class="oasis-mission-hero"><div class="oasis-mission-icon">🎯</div><div class="oasis-mission-name">${m.name||'Mission'}</div><div style="margin-top:8px"><span class="oasis-badge oasis-badge-blue">${m.missionType||'Mission'}</span> <span class="oasis-badge ${m.status==='active'?'oasis-badge-green':'oasis-badge-yellow'}">${m.status||'—'}</span></div></div>
|
|
34
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Description</div><div class="oasis-info-value">${m.description||'No description.'}</div></div>
|
|
35
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Karma Reward</div><div class="oasis-info-value">+${m.karmaReward??0} ⚡</div></div>
|
|
36
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Difficulty</div><div class="oasis-info-value">${m.difficulty||'—'}</div></div>
|
|
37
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Deadline</div><div class="oasis-info-value">${m.endDate?new Date(m.endDate).toLocaleDateString():'No deadline'}</div></div>`;
|
|
38
|
+
} catch { body.innerHTML = '<div class="oasis-msg-error">Failed to load mission.</div>'; }
|
|
39
|
+
}
|
|
40
|
+
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(); }
|
|
41
|
+
_render(c) { c.innerHTML = '<div class="oasis-popup-body"><p style="color:#7a9bbf">Loading…</p></div>'; }
|
|
42
|
+
destroy() { this.close(); }
|
|
43
|
+
}
|
|
44
|
+
window.OasisViewMission = OasisViewMission;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisViewOasisNft — view NFT details popup widget
|
|
3
|
+
* Usage: const w = new OasisViewOasisNft({ nftId: '...' }); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisViewOasisNft {
|
|
6
|
+
constructor({ container, nftId = '', apiUrl = 'https://api.web4.oasisomniverse.one', onBuy = null, onClose = null } = {}) {
|
|
7
|
+
this._nftId = nftId; this._apiUrl = apiUrl; this._onBuy = onBuy; 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:520px;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}.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-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-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-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-nft-hero{text-align:center;padding:24px;background:rgba(0,200,255,.04);border-radius:12px;margin-bottom:20px;border:1px solid rgba(0,200,255,.1)}.oasis-nft-hero-icon{font-size:64px;margin-bottom:10px}.oasis-nft-hero-name{font-size:20px;font-weight:700;color:#fff}.oasis-price-big{font-family:'Orbitron',sans-serif;font-size:24px;font-weight:700;color:#00c8ff;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">NFT Details</span><button class="oasis-popup-close">×</button></div><div class="oasis-popup-body" id="oasis-vnft-body"><div class="oasis-cs"><div class="oasis-cs-icon">⏳</div><p>Loading NFT…</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-vnft-body');
|
|
29
|
+
try {
|
|
30
|
+
const r = await fetch(`${this._apiUrl}/api/nft/${this._nftId}`);
|
|
31
|
+
const data = await r.json();
|
|
32
|
+
const n = data.result || data;
|
|
33
|
+
body.innerHTML = `<div class="oasis-nft-hero"><div class="oasis-nft-hero-icon">🖼️</div><div class="oasis-nft-hero-name">${n.name||'NFT'}</div>${n.price?`<div class="oasis-price-big">${n.price} ${n.currency||'ETH'}</div>`:''}</div>
|
|
34
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Collection</div><div class="oasis-info-value">${n.collection||'—'}</div></div>
|
|
35
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Creator</div><div class="oasis-info-value">${n.creator||'—'}</div></div>
|
|
36
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Blockchain</div><div class="oasis-info-value"><span class="oasis-badge oasis-badge-blue">${n.chain||'ETH'}</span></div></div>
|
|
37
|
+
<div class="oasis-info-block"><div class="oasis-info-label">Description</div><div class="oasis-info-value">${n.description||'No description.'}</div></div>
|
|
38
|
+
${n.isListed ? `<button class="oasis-btn" id="oasis-vnft-buy">Purchase NFT</button>` : ''}`;
|
|
39
|
+
if (n.isListed) this._overlay.querySelector('#oasis-vnft-buy').addEventListener('click', () => { if (this._onBuy) this._onBuy(n); this.close(); });
|
|
40
|
+
} catch { body.innerHTML = '<div class="oasis-msg-error">Failed to load NFT.</div>'; }
|
|
41
|
+
}
|
|
42
|
+
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(); }
|
|
43
|
+
_render(c) { c.innerHTML = '<div class="oasis-popup-body"><p style="color:#7a9bbf">Loading…</p></div>'; }
|
|
44
|
+
destroy() { this.close(); }
|
|
45
|
+
}
|
|
46
|
+
window.OasisViewOasisNft = OasisViewOasisNft;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisViewTournaments — view game tournaments popup widget
|
|
3
|
+
* Usage: const w = new OasisViewTournaments({}); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisViewTournaments {
|
|
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}.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}`;
|
|
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">Tournaments</span><button class="oasis-popup-close">×</button></div><div class="oasis-popup-body" id="oasis-vt-body"><div class="oasis-cs"><div class="oasis-cs-icon">⏳</div><p>Loading tournaments…</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-vt-body');
|
|
29
|
+
try {
|
|
30
|
+
const r = await fetch(`${this._apiUrl}/api/game/tournaments`);
|
|
31
|
+
const data = await r.json();
|
|
32
|
+
const ts = Array.isArray(data) ? data : (data.result || []);
|
|
33
|
+
if (!ts.length) { body.innerHTML = '<div class="oasis-cs"><div class="oasis-cs-icon">🎮</div><h3>No Tournaments</h3><p>No tournaments are currently running.</p></div>'; return; }
|
|
34
|
+
body.innerHTML = `<table class="oasis-table"><thead><tr><th>Tournament</th><th>Players</th><th>Prize</th><th>Status</th></tr></thead><tbody>${ts.map(t=>`<tr><td>${t.name||'—'}</td><td>${t.playerCount??0}/${t.maxPlayers??'∞'}</td><td>${t.prizePool||'—'}</td><td><span class="oasis-badge ${t.status==='active'?'oasis-badge-green':t.status==='upcoming'?'oasis-badge-yellow':'oasis-badge-red'}">${t.status||'unknown'}</span></td></tr>`).join('')}</tbody></table>`;
|
|
35
|
+
} catch { body.innerHTML = '<div class="oasis-msg-error">Failed to load tournaments.</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.OasisViewTournaments = OasisViewTournaments;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OasisVoteKarma — vote karma for an avatar popup widget
|
|
3
|
+
* Usage: const w = new OasisVoteKarma({ targetAvatarId: '...', onVote: (result) => {} }); w.open();
|
|
4
|
+
*/
|
|
5
|
+
class OasisVoteKarma {
|
|
6
|
+
constructor({ container, targetAvatarId = '', apiUrl = 'https://api.web4.oasisomniverse.one', onVote = null, onClose = null } = {}) {
|
|
7
|
+
this._targetAvatarId = targetAvatarId; this._apiUrl = apiUrl; this._onVote = onVote; 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-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,.oasis-select:focus,.oasis-textarea:focus{border-color:rgba(0,200,255,.5)}.oasis-textarea{resize:vertical;min-height:80px}.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-karma-selector{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:16px}.oasis-karma-opt{background:rgba(0,200,255,.07);border:1px solid rgba(0,200,255,.15);border-radius:8px;padding:10px 4px;text-align:center;cursor:pointer;transition:all .2s;font-size:11px;color:#7a9bbf}.oasis-karma-opt:hover,.oasis-karma-opt.selected{background:rgba(0,200,255,.18);border-color:rgba(0,200,255,.5);color:#00c8ff}`;
|
|
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">Vote Karma</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._overlay.querySelectorAll('.oasis-karma-opt').forEach(el => el.addEventListener('click', () => { this._overlay.querySelectorAll('.oasis-karma-opt').forEach(o => o.classList.remove('selected')); el.classList.add('selected'); }));
|
|
26
|
+
this._overlay.querySelector('#oasis-vk-submit').addEventListener('click', () => this._doVote());
|
|
27
|
+
}
|
|
28
|
+
_buildBody() {
|
|
29
|
+
const categories = [{ v: 'health', l: '❤️ Health' }, { v: 'food', l: '🌿 Food' }, { v: 'environment', l: '🌍 Env' }, { v: 'community', l: '🤝 Community' }, { v: 'education', l: '📚 Education' }, { v: 'tech', l: '💡 Tech' }, { v: 'arts', l: '🎨 Arts' }, { v: 'other', l: '✨ Other' }];
|
|
30
|
+
return `<div id="oasis-vk-msg"></div>
|
|
31
|
+
${!this._targetAvatarId ? `<div class="oasis-field"><label class="oasis-label">Avatar Username</label><input class="oasis-input" id="oasis-vk-user" type="text" placeholder="username" /></div>` : `<div style="color:#7a9bbf;font-size:13px;margin-bottom:16px">Voting karma for avatar ID: <strong style="color:#fff">${this._targetAvatarId}</strong></div>`}
|
|
32
|
+
<div class="oasis-label" style="margin-bottom:8px">Karma Category</div>
|
|
33
|
+
<div class="oasis-karma-selector">${categories.map(c=>`<div class="oasis-karma-opt" data-val="${c.v}">${c.l}</div>`).join('')}</div>
|
|
34
|
+
<div class="oasis-field"><label class="oasis-label">Amount</label><input class="oasis-input" id="oasis-vk-amt" type="number" min="1" max="100" value="10" /></div>
|
|
35
|
+
<div class="oasis-field"><label class="oasis-label">Reason (optional)</label><textarea class="oasis-textarea" id="oasis-vk-reason" placeholder="Why are you awarding this karma?"></textarea></div>
|
|
36
|
+
<button class="oasis-btn" id="oasis-vk-submit">Award Karma ⚡</button>`;
|
|
37
|
+
}
|
|
38
|
+
async _doVote() {
|
|
39
|
+
const msgEl = this._overlay.querySelector('#oasis-vk-msg');
|
|
40
|
+
const btn = this._overlay.querySelector('#oasis-vk-submit');
|
|
41
|
+
const selected = this._overlay.querySelector('.oasis-karma-opt.selected');
|
|
42
|
+
const amount = +this._overlay.querySelector('#oasis-vk-amt').value;
|
|
43
|
+
const reason = this._overlay.querySelector('#oasis-vk-reason').value.trim();
|
|
44
|
+
const avatarId = this._targetAvatarId || (this._overlay.querySelector('#oasis-vk-user')?.value.trim());
|
|
45
|
+
if (!selected) { msgEl.innerHTML = '<div class="oasis-msg oasis-msg-error">Please select a karma category.</div>'; return; }
|
|
46
|
+
if (!avatarId) { msgEl.innerHTML = '<div class="oasis-msg oasis-msg-error">Please enter an avatar username.</div>'; return; }
|
|
47
|
+
btn.textContent = 'Submitting…'; btn.disabled = true;
|
|
48
|
+
try {
|
|
49
|
+
const res = await fetch(`${this._apiUrl}/api/karma/vote`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ avatarId, karmaType: selected.dataset.val, karmaAmount: amount, reason }) });
|
|
50
|
+
const data = await res.json();
|
|
51
|
+
if (!res.ok || data.isError) throw new Error(data.message || 'Vote failed');
|
|
52
|
+
msgEl.innerHTML = `<div class="oasis-msg oasis-msg-success">+${amount} karma awarded!</div>`;
|
|
53
|
+
if (this._onVote) this._onVote(data);
|
|
54
|
+
btn.textContent = 'Submitted!';
|
|
55
|
+
} catch (e) { msgEl.innerHTML = `<div class="oasis-msg oasis-msg-error">${e.message}</div>`; btn.textContent = 'Award Karma ⚡'; btn.disabled = false; }
|
|
56
|
+
}
|
|
57
|
+
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(); }
|
|
58
|
+
_render(c) { c.innerHTML = `<div class="oasis-popup-body">${this._buildBody()}</div>`; }
|
|
59
|
+
destroy() { this.close(); }
|
|
60
|
+
}
|
|
61
|
+
window.OasisVoteKarma = OasisVoteKarma;
|