dm homepage placeholder

This commit is contained in:
2026-04-08 14:25:41 -04:00
parent 250daa012c
commit de805fb208
5 changed files with 77 additions and 36 deletions
+34 -34
View File
@@ -187,39 +187,50 @@
bind:this={scrollContainer}
onscroll={handleScroll}
>
{#if chat.activeChannelId && !chat.hasMoreMessages}
{@const dm = chat.activeDms.find(d => d.channelId === chat.activeChannelId)}
{#if chat.activeServer}
<div class="history-start-marker-wrapper">
<div class="history-start-marker">
<div class="hash-icon">#</div>
<div class="text-content">
<h1>Welcome to #{chat.activeChannel?.name || 'this channel'}!</h1>
<p>This is the start of the #{chat.activeChannel?.name || 'this channel'} channel.</p>
</div>
</div>
<div class="divider"></div>
</div>
{:else if dm}
{@const myIdHex = chat.identity?.toHexString()}
{@const otherIdentity = dm.sender.toHexString() === myIdHex ? dm.recipient : dm.sender}
{@const recipient = chat.users.find(u => u.identity.toHexString() === otherIdentity.toHexString())}
{#if recipient}
{#if chat.activeChannelId}
{#if !chat.hasMoreMessages}
{@const dm = chat.activeDms.find(d => d.channelId === chat.activeChannelId)}
{#if chat.activeServer}
<div class="history-start-marker-wrapper">
<div class="history-start-marker">
<Avatar user={recipient} size={64} />
<div class="hash-icon">#</div>
<div class="text-content">
<h1>{recipient.name || 'Unknown User'}</h1>
<p>This is the beginning of your direct message history with <strong>{recipient.name || 'this user'}</strong>.</p>
<h1>Welcome to #{chat.activeChannel?.name || 'this channel'}!</h1>
<p>This is the start of the #{chat.activeChannel?.name || 'this channel'} channel.</p>
</div>
</div>
<div class="divider"></div>
</div>
{:else if dm}
{@const myIdHex = chat.identity?.toHexString()}
{@const otherIdentity = dm.sender.toHexString() === myIdHex ? dm.recipient : dm.sender}
{@const recipient = chat.users.find(u => u.identity.toHexString() === otherIdentity.toHexString())}
{#if recipient}
<div class="history-start-marker-wrapper">
<div class="history-start-marker">
<Avatar user={recipient} size={64} />
<div class="text-content">
<h1>{recipient.name || 'Unknown User'}</h1>
<p>This is the beginning of your direct message history with <strong>{recipient.name || 'this user'}</strong>.</p>
</div>
</div>
<div class="divider"></div>
</div>
{/if}
{/if}
{/if}
{/if}
{#if !chat.activeChannelId}
{#each visibleMessages as msg, i (msg.id.toString())}
{@const isHighlighted = (chat.pendingThreadParentMessageId === msg.id) || (chat.activeThread?.parentMessageId === msg.id)}
<MessageItem
{msg}
prevMsg={i > 0 ? visibleMessages[i-1] : null}
{isHighlighted}
onContentLoad={handleContentLoad}
/>
{/each}
<div bind:this={messagesEndRef} style="height: 1px; flex-shrink: 0;"></div>
{:else}
<div class="empty-state">
<div class="empty-state-icon">
<i class="fas fa-comments"></i>
@@ -228,17 +239,6 @@
<p>Choose a channel from a server or a direct message to start chatting.</p>
</div>
{/if}
{#each visibleMessages as msg, i (msg.id.toString())}
{@const isHighlighted = (chat.pendingThreadParentMessageId === msg.id) || (chat.activeThread?.parentMessageId === msg.id)}
<MessageItem
{msg}
prevMsg={i > 0 ? visibleMessages[i-1] : null}
{isHighlighted}
onContentLoad={handleContentLoad}
/>
{/each}
<div bind:this={messagesEndRef} style="height: 1px; flex-shrink: 0;"></div>
</div>
</div>
+1 -1
View File
@@ -44,7 +44,7 @@ export class ChatService {
this.#voice = new VoiceService(this.#db, this.#nav, () => this.identity);
this.#account = new AccountService();
this.#server = new ServerManagementService();
this.#dm = new DirectMessagingService();
this.#dm = new DirectMessagingService(this.#db, () => this.identity);
// Session-only image processing: creates Blob URLs directly from Database data.
// This ditched the persistent IndexedDB cache to prevent stale data between reloads.
+28 -1
View File
@@ -1,13 +1,40 @@
import { useReducer } from "spacetimedb/svelte";
import { reducers } from "../../module_bindings";
import type { Identity } from "spacetimedb";
import type { DatabaseService } from "./database.svelte";
export class DirectMessagingService {
#openDirectMessageReducer = useReducer(reducers.openDirectMessage);
#closeDirectMessageReducer = useReducer(reducers.closeDirectMessage);
#db: DatabaseService;
#identity: () => Identity | null;
constructor(db: DatabaseService, identity: () => Identity | null) {
this.#db = db;
this.#identity = identity;
}
handleOpenDirectMessage = (recipient: Identity) => {
this.#openDirectMessageReducer({ recipient });
const identity = this.#identity();
if (!identity) return;
// Check if DM session already exists
const existing = this.#db.directMessages.find(dm =>
(dm.sender.isEqual(identity) && dm.recipient.isEqual(recipient)) ||
(dm.sender.isEqual(recipient) && dm.recipient.isEqual(identity))
);
if (existing) {
// Session exists, open it if closed
if (existing.sender.isEqual(identity) && !existing.isOpenSender) {
this.#openDirectMessageReducer({ recipient });
} else if (existing.recipient.isEqual(identity) && !existing.isOpenRecipient) {
this.#openDirectMessageReducer({ recipient });
}
} else {
// No session, create it
this.#openDirectMessageReducer({ recipient });
}
};
handleCloseDirectMessage = (channelId: bigint) => {
+3
View File
@@ -118,9 +118,12 @@ export class MessagingService {
}
#updateBuckets(newMessages: readonly Types.VisibleMessageRow[]) {
const activeChannelId = this.#nav.activeChannelId;
const tempBuckets = new Map<bigint, Map<bigint, Types.Message & { seqId?: bigint, reactions: Types.Reaction[], imageIds: bigint[] }>>();
for (const m of newMessages) {
if (activeChannelId && m.channelId !== activeChannelId) continue;
let bucketMap = tempBuckets.get(m.channelId);
if (!bucketMap) {
bucketMap = new Map();
+11
View File
@@ -60,6 +60,17 @@ export class NavigationService {
});
}
}
} else {
const activeDms = this.activeDms;
const isCurrentChannelValid =
this.activeChannelId &&
activeDms.some((dm) => dm.channelId === this.activeChannelId);
if (!isCurrentChannelValid) {
untrack(() => {
this.activeChannelId = null;
});
}
}
});
}