Add account connections, guild wallets and admin fixes [skip ci]
This commit is contained in:
1 parent
7076e847ea
commit
004862ea88
38 files changed
+1696
-458
No files matched your search
@@ -7,7 +7,7 @@
|
||||
import Avatar from '../components/Avatar.svelte';
|
||||
import ChunkMap from '../components/ChunkMap.svelte';
|
||||
import Modal from '../components/Modal.svelte';
|
||||
import { app, instances, selectedInstance, toast } from '../lib/store.svelte';
|
||||
import { activeAccount, app, instances, selectedInstance, toast } from '../lib/store.svelte';
|
||||
import { invoke } from '../lib/tauri';
|
||||
import type { Guild, GuildMember, GuildPost } from '../lib/types';
|
||||
|
||||
@@ -16,8 +16,34 @@
|
||||
let allGuilds = $state<Guild[]>([]);
|
||||
let members = $state<GuildMember[]>([]);
|
||||
let posts = $state<GuildPost[]>([]);
|
||||
let activeTab = $state<'territory' | 'members' | 'feed'>('territory');
|
||||
let activeTab = $state<'territory' | 'members' | 'feed' | 'wallet'>('territory');
|
||||
let loading = $state(false);
|
||||
let walletServers = $state<Array<{ id: number; name: string; instance_id: string }>>([]);
|
||||
let walletServerId = $state<number | null>(null);
|
||||
let wallet = $state<{ balance: number; transactions: Array<{ id: number; actor_uuid: string; kind: string; amount: number; created_at: string }> } | null>(null);
|
||||
let walletAmount = $state('');
|
||||
let walletBusy = $state(false);
|
||||
const canWithdraw = $derived(members.some(m => m.uuid === activeAccount()?.uuid && ['leader', 'officer'].includes(m.role)));
|
||||
|
||||
async function loadWallet() {
|
||||
if (!myGuild || !walletServerId) { wallet = null; return; }
|
||||
try { wallet = await invoke('get_guild_wallet', { guildId: myGuild.id, serverId: walletServerId }); }
|
||||
catch (e: any) { toast(e?.message ?? 'Unable to load guild wallet', 'error'); }
|
||||
}
|
||||
|
||||
async function transferWallet(withdraw: boolean) {
|
||||
if (!myGuild || !walletServerId || walletBusy) return;
|
||||
const amount = Number(walletAmount);
|
||||
if (!Number.isFinite(amount) || amount <= 0 || Math.round(amount * 100) !== amount * 100) { toast('Enter a valid amount with at most two decimals', 'error'); return; }
|
||||
walletBusy = true;
|
||||
try {
|
||||
await invoke('transfer_guild_wallet', { guildId: myGuild.id, serverId: walletServerId, amount, withdraw });
|
||||
walletAmount = '';
|
||||
await loadWallet();
|
||||
toast(withdraw ? 'Funds withdrawn' : 'Funds deposited', 'ok');
|
||||
} catch (e: any) { toast(e?.message ?? 'Transfer failed', 'error'); }
|
||||
finally { walletBusy = false; }
|
||||
}
|
||||
|
||||
// Create Guild Modal
|
||||
let createModalOpen = $state(false);
|
||||
@@ -67,6 +93,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
try { walletServers = (await invoke<{ servers: typeof walletServers }>('get_public_servers')).servers; }
|
||||
catch { walletServers = []; }
|
||||
});
|
||||
$effect(() => {
|
||||
const available = walletServers.filter(s => s.instance_id === selectedInstId);
|
||||
if (!available.some(s => s.id === walletServerId)) walletServerId = available[0]?.id ?? null;
|
||||
});
|
||||
$effect(() => { if (activeTab === 'wallet' && myGuild && walletServerId) loadWallet(); });
|
||||
|
||||
async function handleCreateGuild() {
|
||||
if (!newGuildName.trim() || !newGuildTag.trim()) {
|
||||
toast('Please enter both guild name and tag', 'error');
|
||||
@@ -201,6 +237,9 @@
|
||||
<button class:active={activeTab === 'feed'} onclick={() => (activeTab = 'feed')}>
|
||||
<MessageSquare size={15} /> Guild Feed ({posts.length})
|
||||
</button>
|
||||
<button class:active={activeTab === 'wallet'} onclick={() => (activeTab = 'wallet')}>
|
||||
Guild Wallet
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -225,6 +264,28 @@
|
||||
onunclaim={() => { if (myGuild) myGuild.claims_count = Math.max(0, myGuild.claims_count - 1); }}
|
||||
/>
|
||||
</div>
|
||||
{:else if activeTab === 'wallet'}
|
||||
<div class="wallet-tab glass">
|
||||
<h3>Guild Wallet</h3>
|
||||
<label>Game server
|
||||
<select bind:value={walletServerId}>
|
||||
{#each walletServers.filter(s => s.instance_id === selectedInstId) as server}<option value={server.id}>{server.name}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
{#if !walletServerId}<p>Link a game server to this instance to use the guild wallet.</p>{/if}
|
||||
{#if wallet}
|
||||
<p>Balance: ${wallet.balance.toFixed(2)}</p>
|
||||
<div class="wallet-actions">
|
||||
<input type="number" min="0.01" step="0.01" placeholder="Amount" bind:value={walletAmount} aria-label="Transfer amount" />
|
||||
<button class="primary sm" disabled={walletBusy || !walletServerId} onclick={() => transferWallet(false)}>Deposit</button>
|
||||
{#if canWithdraw}<button class="ghost sm" disabled={walletBusy || !walletServerId} onclick={() => transferWallet(true)}>Withdraw</button>{/if}
|
||||
</div>
|
||||
<h4>Recent transfers</h4>
|
||||
{#each wallet.transactions as tx (tx.id)}
|
||||
<p>{tx.kind === 'withdraw' ? '−' : '+'}${tx.amount.toFixed(2)} · {new Date(tx.created_at).toLocaleString()}</p>
|
||||
{:else}<p>No transfers yet.</p>{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{:else if activeTab === 'members'}
|
||||
<div class="members-tab">
|
||||
<div class="members-grid">
|
||||
|
||||
Reference in new issue
Block a user