mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 03:01:09 +02:00
feat(matrix): add room invitations UI with accept/decline
- Add membership and inviter fields to SimpleRoom type - Track room membership status in store - Add invitedRooms derived property - Show invites section in RoomList with accept/decline buttons Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d37f5894dc
commit
33073ab8ac
3 changed files with 119 additions and 5 deletions
|
|
@ -1,7 +1,15 @@
|
|||
<script lang="ts">
|
||||
import { matrixStore } from '$lib/matrix';
|
||||
import RoomItem from './RoomItem.svelte';
|
||||
import { MagnifyingGlass, Plus, Users, ChatCircle } from '@manacore/shared-icons';
|
||||
import {
|
||||
MagnifyingGlass,
|
||||
Plus,
|
||||
Users,
|
||||
ChatCircle,
|
||||
Envelope,
|
||||
Check,
|
||||
X,
|
||||
} from '@manacore/shared-icons';
|
||||
|
||||
interface Props {
|
||||
onCreateRoom?: () => void;
|
||||
|
|
@ -22,6 +30,20 @@
|
|||
room.name.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
);
|
||||
|
||||
let filteredInvites = $derived(
|
||||
matrixStore.invitedRooms.filter((room) =>
|
||||
room.name.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
);
|
||||
|
||||
async function acceptInvite(roomId: string) {
|
||||
await matrixStore.joinRoom(roomId);
|
||||
}
|
||||
|
||||
async function declineInvite(roomId: string) {
|
||||
await matrixStore.leaveRoom(roomId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex h-full flex-col">
|
||||
|
|
@ -45,6 +67,64 @@
|
|||
|
||||
<!-- Room List with Sections -->
|
||||
<div class="chat-scrollbar flex-1 overflow-y-auto px-3">
|
||||
<!-- Invites Section -->
|
||||
{#if filteredInvites.length > 0}
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center gap-2 px-2 py-2 text-xs font-semibold uppercase text-muted-foreground">
|
||||
<Envelope class="h-3.5 w-3.5" />
|
||||
Einladungen
|
||||
<span class="px-1.5 py-0.5 rounded-full bg-gradient-to-r from-amber-500 to-orange-500 text-white text-[10px]">
|
||||
{filteredInvites.length}
|
||||
</span>
|
||||
</div>
|
||||
{#each filteredInvites as room (room.id)}
|
||||
<div
|
||||
class="flex items-center gap-3 px-3 py-2.5 mb-1 rounded-xl bg-amber-50 dark:bg-amber-500/10 border border-amber-200 dark:border-amber-500/20"
|
||||
>
|
||||
<!-- Avatar -->
|
||||
<div
|
||||
class="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 bg-gradient-to-br from-amber-500 to-orange-500 text-white shadow-sm"
|
||||
>
|
||||
<span class="text-sm font-semibold">
|
||||
{room.name
|
||||
.split(' ')
|
||||
.map((w) => w[0])
|
||||
.join('')
|
||||
.substring(0, 2)
|
||||
.toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<!-- Info -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-medium text-foreground truncate">{room.name}</p>
|
||||
{#if room.inviter}
|
||||
<p class="text-xs text-muted-foreground truncate">
|
||||
Eingeladen von {room.inviter}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- Actions -->
|
||||
<div class="flex gap-1.5">
|
||||
<button
|
||||
class="p-2 rounded-lg bg-green-500 hover:bg-green-600 text-white transition-colors"
|
||||
title="Annehmen"
|
||||
onclick={() => acceptInvite(room.id)}
|
||||
>
|
||||
<Check class="h-4 w-4" weight="bold" />
|
||||
</button>
|
||||
<button
|
||||
class="p-2 rounded-lg bg-red-500 hover:bg-red-600 text-white transition-colors"
|
||||
title="Ablehnen"
|
||||
onclick={() => declineInvite(room.id)}
|
||||
>
|
||||
<X class="h-4 w-4" weight="bold" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Direct Messages Section -->
|
||||
{#if filteredDirectRooms.length > 0 || !search}
|
||||
<div class="mb-2">
|
||||
|
|
|
|||
|
|
@ -96,11 +96,17 @@ class MatrixStore {
|
|||
.sort((a, b) => (b.lastMessageTime || 0) - (a.lastMessageTime || 0))
|
||||
);
|
||||
|
||||
/** Direct message rooms */
|
||||
directRooms = $derived(this.rooms.filter((r) => r.isDirect));
|
||||
/** Joined rooms only */
|
||||
joinedRooms = $derived(this.rooms.filter((r) => r.membership === 'join'));
|
||||
|
||||
/** Group rooms (non-DM) */
|
||||
groupRooms = $derived(this.rooms.filter((r) => !r.isDirect));
|
||||
/** Invited rooms */
|
||||
invitedRooms = $derived(this.rooms.filter((r) => r.membership === 'invite'));
|
||||
|
||||
/** Direct message rooms (joined only) */
|
||||
directRooms = $derived(this.joinedRooms.filter((r) => r.isDirect));
|
||||
|
||||
/** Group rooms (non-DM, joined only) */
|
||||
groupRooms = $derived(this.joinedRooms.filter((r) => !r.isDirect));
|
||||
|
||||
/** Current selected room */
|
||||
currentRoom = $derived(
|
||||
|
|
@ -1173,6 +1179,25 @@ class MatrixStore {
|
|||
const topicEvent = room.currentState.getStateEvents('m.room.topic', '');
|
||||
const topic = (topicEvent as MatrixEvent | null)?.getContent()?.topic;
|
||||
|
||||
// Get membership status
|
||||
const myUserId = this._client?.getUserId();
|
||||
const myMember = myUserId ? room.getMember(myUserId) : null;
|
||||
const membership = (myMember?.membership || 'leave') as SimpleRoom['membership'];
|
||||
|
||||
// Get inviter if this is an invite
|
||||
let inviter: string | undefined;
|
||||
if (membership === 'invite' && myMember) {
|
||||
// The events array contains the invite event
|
||||
const inviteEvent = room.currentState.getStateEvents('m.room.member', myUserId || '');
|
||||
if (inviteEvent) {
|
||||
const sender = (inviteEvent as MatrixEvent).getSender();
|
||||
if (sender) {
|
||||
const senderMember = room.getMember(sender);
|
||||
inviter = senderMember?.name || sender;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: room.roomId,
|
||||
name: room.name || 'Unnamed Room',
|
||||
|
|
@ -1186,6 +1211,8 @@ class MatrixStore {
|
|||
isDirect: this.isDirectRoom(room),
|
||||
isEncrypted: room.hasEncryptionStateEvent(),
|
||||
memberCount: room.getJoinedMemberCount(),
|
||||
membership,
|
||||
inviter,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ export type MessageType =
|
|||
| 'm.emote'
|
||||
| 'm.notice';
|
||||
|
||||
/**
|
||||
* Room membership status
|
||||
*/
|
||||
export type RoomMembership = 'join' | 'invite' | 'leave' | 'ban' | 'knock';
|
||||
|
||||
/**
|
||||
* Simplified room for UI rendering
|
||||
*/
|
||||
|
|
@ -73,6 +78,8 @@ export interface SimpleRoom {
|
|||
isDirect: boolean;
|
||||
isEncrypted: boolean;
|
||||
memberCount: number;
|
||||
membership: RoomMembership;
|
||||
inviter?: string; // User who sent the invite
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue