diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-12-02 22:51:10 -0500 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-12-02 22:51:10 -0500 |
| commit | 4e3ad13aca163e733724b205c250bdb67cc56c29 (patch) | |
| tree | 216842b15b55181a495af1643102718bbe041a38 /ui/routes/(app) | |
| parent | 5ce6c9f6277c43caf7413cce255af7bdc947e74c (diff) | |
| parent | 0276d22c9ed49b035129cac99f68ada7fc1015d2 (diff) | |
Merge remote-tracking branch 'origin/wip/unreadish'
Diffstat (limited to 'ui/routes/(app)')
| -rw-r--r-- | ui/routes/(app)/+layout.svelte | 32 | ||||
| -rw-r--r-- | ui/routes/(app)/ch/[channel]/+page.svelte | 60 |
2 files changed, 89 insertions, 3 deletions
diff --git a/ui/routes/(app)/+layout.svelte b/ui/routes/(app)/+layout.svelte index ad8227e..81324fd 100644 --- a/ui/routes/(app)/+layout.svelte +++ b/ui/routes/(app)/+layout.svelte @@ -19,6 +19,35 @@ let loading = $state(true); let channel = $derived($page.params.channel); + let rawChannels; + channelsList.subscribe((val) => { + rawChannels = val.channels; + }); + let rawMessages; + messages.subscribe((val) => { + rawMessages = val; + }); + + let enrichedChannels = $derived.by(() => { + const channels = rawChannels; + const messages = rawMessages; + const enrichedChannels = []; + if (channels && messages) { + for (let ch of channels) { + let runs = messages.inChannel(ch.id); + let lastRun = runs?.slice(-1)[0]; + let lastMessage = lastRun?.messages.slice(-1)[0]; + let lastMessageAt = lastMessage?.at; + let hasUnreads = lastMessageAt > ch.lastReadAt; + enrichedChannels.push({ + ...ch, + hasUnreads, + }); + } + } + return enrichedChannels; + }); + function onBooted(boot) { currentUser.set({ id: boot.login.id, @@ -78,6 +107,7 @@ </script> <svelte:head> + <!-- TODO: unread count? --> <title>pilcrow</title> </svelte:head> @@ -87,7 +117,7 @@ <div id="interface" class="p-2"> <nav id="sidebar" data-expanded={pageContext.showMenu}> <div class="channel-list"> - <ChannelList active={channel} channels={$channelsList.channels} /> + <ChannelList active={channel} channels={enrichedChannels} /> </div> <div class="create-channel"> <CreateChannelForm /> diff --git a/ui/routes/(app)/ch/[channel]/+page.svelte b/ui/routes/(app)/ch/[channel]/+page.svelte index 0961665..7d5b8cf 100644 --- a/ui/routes/(app)/ch/[channel]/+page.svelte +++ b/ui/routes/(app)/ch/[channel]/+page.svelte @@ -1,14 +1,70 @@ <script> + import { DateTime } from 'luxon'; import { page } from '$app/stores'; import ActiveChannel from '$lib/components/ActiveChannel.svelte'; import MessageInput from '$lib/components/MessageInput.svelte'; - import { messages } from '$lib/store'; + import { channelsList, messages } from '$lib/store'; let channel = $derived($page.params.channel); let messageRuns = $derived($messages.inChannel(channel)); + let activeChannel; + + function inView(parentElement, element) { + const parRect = parentElement.getBoundingClientRect(); + const parentTop = parRect.top; + const parentBottom = parRect.bottom; + + const elRect = element.getBoundingClientRect(); + const elementTop = elRect.top; + const elementBottom = elRect.bottom; + + return ((parentTop < elementTop) && (parentBottom > elementBottom)); + } + + function getLastVisibleMessage() { + const parentElement = activeChannel; + const childElements = parentElement.getElementsByClassName('message'); + const lastInView = Array.from(childElements).reverse().find((el) => { + return inView(parentElement, el); + }); + return lastInView; + } + + function setLastRead() { + const channelObject = $channelsList.getChannel(channel); + const lastInView = getLastVisibleMessage(); + if (!channelObject || !lastInView) { + return; + } + const at = DateTime.fromISO(lastInView.dataset.at); + // Do it this way, rather than with Math.max tricks, to avoid assignment + // when we don't need it, to minimize reactive changes: + if (at > channelObject.lastReadAt) { + channelObject.lastReadAt = at; + } + } + + $effect(() => { + const _ = $messages.inChannel(channel); + setLastRead(); + }); + + function handleKeydown(event) { + if (event.key === 'Escape') { + setLastRead(); // TODO: pass in "last message DT"? + } + } + + let lastReadCallback = null; + function handleScroll() { + clearTimeout(lastReadCallback); // Fine if lastReadCallback is null still. + lastReadCallback = setTimeout(setLastRead, 2 * 1000); + } </script> -<div class="active-channel"> +<svelte:window onkeydown={handleKeydown} /> + +<div class="active-channel" on:scroll={handleScroll} bind:this={activeChannel}> <ActiveChannel {messageRuns} /> </div> <div class="create-message max-h-full"> |
