Rework BlueMap claiming and expand ranks guilds chat and warps
This commit is contained in:
1 parent
434ed5338a
commit
6a2944a88e
68 files changed
+1453
-2500
No files matched your search
@@ -17,7 +17,16 @@
|
||||
let allGuilds = $state<Guild[]>([]);
|
||||
let members = $state<GuildMember[]>([]);
|
||||
let posts = $state<GuildPost[]>([]);
|
||||
let activeTab = $state<'territory' | 'members' | 'feed' | 'wallet'>('territory');
|
||||
let activeTab = $state<'territory' | 'members' | 'feed' | 'wallet' | 'settings'>('territory');
|
||||
type GuildRole = { id: number; name: string; can_invite: boolean; can_kick: boolean; can_claim: boolean; can_post: boolean; can_manage: boolean };
|
||||
type JoinRequest = { uuid: string; name: string; message: string; created_at: string };
|
||||
let roles = $state<GuildRole[]>([]);
|
||||
let joinRequests = $state<JoinRequest[]>([]);
|
||||
let newRoleName = $state('');
|
||||
let roleFlags = $state({ can_invite: false, can_kick: false, can_claim: false, can_post: true, can_manage: false });
|
||||
let guildEdit = $state({ description: '', motd: '', icon_url: '', banner_url: '' });
|
||||
const myRole = $derived(members.find((m) => m.uuid === activeAccount()?.uuid)?.role ?? '');
|
||||
const canManageDetails = $derived(myRole === 'leader' || myRole === 'officer' || !!roles.find((r) => r.name === myRole)?.can_manage);
|
||||
let loading = $state(false);
|
||||
let walletServers = $state<Array<{ id: number; name: string; instance_id: string }>>([]);
|
||||
|
||||
@@ -58,9 +67,14 @@
|
||||
]);
|
||||
members = mList;
|
||||
posts = pList;
|
||||
roles = await invoke<GuildRole[]>('get_guild_roles', { guildId: mine.id }).catch(() => []);
|
||||
joinRequests = await invoke<JoinRequest[]>('get_guild_join_requests', { guildId: mine.id }).catch(() => []);
|
||||
guildEdit = { description: mine.description ?? '', motd: mine.motd ?? '', icon_url: mine.icon_url ?? '', banner_url: mine.banner_url ?? '' };
|
||||
} else {
|
||||
members = [];
|
||||
posts = [];
|
||||
roles = [];
|
||||
joinRequests = [];
|
||||
}
|
||||
} catch (e: any) {
|
||||
toast(e?.message ?? 'Failed to load guild data', 'error');
|
||||
@@ -85,6 +99,59 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function requestJoin(guild: Guild) {
|
||||
try {
|
||||
await invoke('request_guild_join', { guildId: guild.id, message: '' });
|
||||
toast(`Request sent to ${guild.name}`, 'ok');
|
||||
} catch (e: any) { toast(String(e), 'error'); }
|
||||
}
|
||||
|
||||
async function answerJoin(req: JoinRequest, accept: boolean) {
|
||||
if (!myGuild) return;
|
||||
try {
|
||||
await invoke('respond_guild_join_request', { guildId: myGuild.id, uuid: req.uuid, accept });
|
||||
toast(accept ? `${req.name} joined the guild` : 'Request declined', 'ok');
|
||||
await loadInstanceGuildData();
|
||||
} catch (e: any) { toast(String(e), 'error'); }
|
||||
}
|
||||
|
||||
async function createRole() {
|
||||
if (!myGuild || !newRoleName.trim()) return;
|
||||
try {
|
||||
await invoke('create_guild_role', { guildId: myGuild.id, role: { name: newRoleName.trim(), ...roleFlags } });
|
||||
newRoleName = '';
|
||||
roles = await invoke<GuildRole[]>('get_guild_roles', { guildId: myGuild.id });
|
||||
toast('Guild role created', 'ok');
|
||||
} catch (e: any) { toast(String(e), 'error'); }
|
||||
}
|
||||
|
||||
async function assignRole(member: GuildMember, role: string) {
|
||||
if (!myGuild) return;
|
||||
try {
|
||||
await invoke('assign_guild_role', { guildId: myGuild.id, uuid: member.uuid, role });
|
||||
member.role = role as GuildMember['role'];
|
||||
toast('Guild role assigned', 'ok');
|
||||
} catch (e: any) { toast(String(e), 'error'); }
|
||||
}
|
||||
|
||||
async function deleteRole(role: GuildRole) {
|
||||
if (!myGuild) return;
|
||||
try {
|
||||
await invoke('delete_guild_role', { guildId: myGuild.id, roleId: role.id });
|
||||
await loadInstanceGuildData();
|
||||
toast('Guild role deleted', 'ok');
|
||||
} catch (e: any) { toast(String(e), 'error'); }
|
||||
}
|
||||
|
||||
async function saveGuild() {
|
||||
if (!myGuild) return;
|
||||
try {
|
||||
await invoke('update_guild', { guildId: myGuild.id, description: guildEdit.description, motd: guildEdit.motd, iconUrl: guildEdit.icon_url, bannerUrl: guildEdit.banner_url });
|
||||
await loadInstanceGuildData();
|
||||
toast('Guild details saved', 'ok');
|
||||
} catch (e: any) { toast(String(e), 'error'); }
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
loadInvites();
|
||||
try { walletServers = (await invoke<{ servers: typeof walletServers }>('get_public_servers')).servers; }
|
||||
@@ -242,6 +309,7 @@
|
||||
<button class:active={activeTab === 'wallet'} onclick={() => (activeTab = 'wallet')}>
|
||||
<Landmark size={15} /> Treasury
|
||||
</button>
|
||||
{#if canManageDetails}<button class:active={activeTab === 'settings'} onclick={() => (activeTab = 'settings')}>Guild settings</button>{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -270,6 +338,13 @@
|
||||
<GuildWallet guild={myGuild} {members} servers={walletServers} instanceId={selectedInstId} />
|
||||
{:else if activeTab === 'members'}
|
||||
<div class="members-tab">
|
||||
{#if joinRequests.length}
|
||||
<div class="card"><h3>Join requests</h3>
|
||||
{#each joinRequests as req}
|
||||
<div class="row"><strong>{req.name}</strong><span>{req.message}</span><span class="spacer"></span><button class="sm" onclick={() => answerJoin(req, false)}>Decline</button><button class="sm primary" onclick={() => answerJoin(req, true)}>Accept</button></div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="members-grid">
|
||||
{#each members as m (m.uuid)}
|
||||
<div
|
||||
@@ -292,16 +367,42 @@
|
||||
<span class="badge leader-badge"><Crown size={11} /> Leader</span>
|
||||
{:else if m.role === 'officer'}
|
||||
<span class="badge officer-badge"><UserCheck size={11} /> Officer</span>
|
||||
{:else}
|
||||
{:else if m.role === 'member'}
|
||||
<span class="badge member-role-badge">Member</span>
|
||||
{:else}
|
||||
<span class="badge member-role-badge">{m.role}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<span class="tiny muted">Joined {new Date(m.joined_at).toLocaleDateString()}</span>
|
||||
{#if myGuild.leader_uuid === activeAccount()?.uuid && m.uuid !== activeAccount()?.uuid}
|
||||
<select value={m.role} aria-label="Role for {m.name}" onchange={(e) => assignRole(m, e.currentTarget.value)}>
|
||||
<option value="member">Member</option><option value="officer">Officer</option>
|
||||
{#each roles as role}<option value={role.name}>{role.name}</option>{/each}
|
||||
</select>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else if activeTab === 'settings'}
|
||||
<div class="card col">
|
||||
<h3>Guild appearance</h3>
|
||||
<label>Description<textarea bind:value={guildEdit.description} maxlength="500"></textarea></label>
|
||||
<label>Message of the day<input bind:value={guildEdit.motd} maxlength="200" /></label>
|
||||
<label>Icon URL<input bind:value={guildEdit.icon_url} type="url" /></label>
|
||||
<label>Banner URL<input bind:value={guildEdit.banner_url} type="url" /></label>
|
||||
<button class="primary" onclick={saveGuild}>Save guild details</button>
|
||||
</div>
|
||||
{#if myRole === 'leader'}<div class="card col">
|
||||
<h3>Guild roles</h3>
|
||||
{#each roles as role}
|
||||
<div class="row"><strong>{role.name}</strong><span class="tiny muted">{[role.can_invite && 'Invite', role.can_kick && 'Kick', role.can_claim && 'Claim', role.can_post && 'Post', role.can_manage && 'Manage'].filter(Boolean).join(', ') || 'No extra permissions'}</span><span class="spacer"></span><button class="sm danger" onclick={() => deleteRole(role)}>Delete</button></div>
|
||||
{/each}
|
||||
<label>New role name<input bind:value={newRoleName} maxlength="24" /></label>
|
||||
<div class="row"><label><input type="checkbox" bind:checked={roleFlags.can_invite} /> Invite</label><label><input type="checkbox" bind:checked={roleFlags.can_kick} /> Kick</label><label><input type="checkbox" bind:checked={roleFlags.can_claim} /> Claim</label><label><input type="checkbox" bind:checked={roleFlags.can_post} /> Post</label><label><input type="checkbox" bind:checked={roleFlags.can_manage} /> Manage</label></div>
|
||||
<button class="primary" onclick={createRole}>Create role</button>
|
||||
</div>{/if}
|
||||
{:else if activeTab === 'feed'}
|
||||
<div class="feed-tab">
|
||||
<!-- Post Announcement Form -->
|
||||
@@ -407,6 +508,7 @@
|
||||
{#if g.description}
|
||||
<p class="dir-desc">{g.description}</p>
|
||||
{/if}
|
||||
<button class="sm primary" onclick={() => requestJoin(g)}>Request to join</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -436,6 +538,10 @@
|
||||
<span class="field-label">Banner Image URL (Optional)</span>
|
||||
<input type="url" placeholder="https://example.com/banner.png" bind:value={newGuildBanner} />
|
||||
</label>
|
||||
<label>
|
||||
<span class="field-label">Icon Image URL (Optional)</span>
|
||||
<input type="url" placeholder="https://example.com/guild-icon.png" bind:value={newGuildIcon} />
|
||||
</label>
|
||||
</div>
|
||||
{#snippet footer()}
|
||||
<button class="ghost" onclick={() => (createModalOpen = false)}>Cancel</button>
|
||||
|
||||
Reference in new issue
Block a user