significantly improve unread system

This commit is contained in:
hippoz 2022-09-02 22:07:08 +03:00
parent 81e9de6cd8
commit 7b9379732e
Signed by: hippoz
GPG key ID: 7C52899193467641
3 changed files with 74 additions and 31 deletions

View file

@ -348,7 +348,7 @@ body {
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.sidebar-button div { .sidebar-button .sidebar-button-icon {
display: inline; display: inline;
flex-shrink: 0; flex-shrink: 0;

View file

@ -2,7 +2,7 @@
import { HashIcon, PlusIcon, MoreVerticalIcon, SettingsIcon, CloudIcon } from "svelte-feather-icons"; import { HashIcon, PlusIcon, MoreVerticalIcon, SettingsIcon, CloudIcon } from "svelte-feather-icons";
import { quadInOut } from "svelte/easing"; import { quadInOut } from "svelte/easing";
import { maybeFly, maybeFlyIf } from "../animations"; import { maybeFly, maybeFlyIf } from "../animations";
import { channels, gatewayStatus, overlayStore, selectedChannel, showSidebar, smallViewport, userInfoStore } from "../stores"; import { channels, gatewayStatus, overlayStore, selectedChannel, showSidebar, smallViewport, userInfoStore, unreadStore } from "../stores";
import UserTopBar from "./UserTopBar.svelte"; import UserTopBar from "./UserTopBar.svelte";
const selectChannel = (channel) => { const selectChannel = (channel) => {
@ -19,30 +19,32 @@
<div class="sidebar"> <div class="sidebar">
{#each $channels as channel (channel.id)} {#each $channels as channel (channel.id)}
<button on:click="{ selectChannel(channel) }" class="sidebar-button" class:selected={ channel.id === $selectedChannel.id }> <button on:click="{ selectChannel(channel) }" class="sidebar-button" class:selected={ channel.id === $selectedChannel.id }>
<div> <div class="sidebar-button-icon">
<HashIcon /> <HashIcon />
</div> </div>
<span>{ channel.name }</span> <span>{ channel.name }</span>
{#if channel._hasUnreads} <div class="sidebar-channel-buttons">
<span class="unread-indicator"></span> {#if $unreadStore.get(channel.id)}
{/if} <div class="unread-indicator">{ $unreadStore.get(channel.id) }</div>
{#if $userInfoStore && (channel.owner_id === $userInfoStore.id || $userInfoStore.is_superuser)} {/if}
<button class="icon-button icon-button-auto" on:click|stopPropagation="{ () => overlayStore.open('editChannel', { channel }) }" aria-label="Edit Channel"> {#if $userInfoStore && (channel.owner_id === $userInfoStore.id || $userInfoStore.is_superuser)}
<MoreVerticalIcon /> <button class="icon-button" on:click|stopPropagation="{ () => overlayStore.open('editChannel', { channel }) }" aria-label="Edit Channel">
</button> <MoreVerticalIcon />
{/if} </button>
{/if}
</div>
</button> </button>
{/each} {/each}
{#if $userInfoStore && $userInfoStore.permissions.create_channel} {#if $userInfoStore && $userInfoStore.permissions.create_channel}
<button on:click="{ () => overlayStore.open('createChannel') }" class="sidebar-button"> <button on:click="{ () => overlayStore.open('createChannel') }" class="sidebar-button">
<div> <div class="sidebar-button-icon">
<PlusIcon /> <PlusIcon />
</div> </div>
<span>Create Channel</span> <span>Create Channel</span>
</button> </button>
{/if} {/if}
<button on:click="{ () => overlayStore.open('settings') }" class="sidebar-button"> <button on:click="{ () => overlayStore.open('settings') }" class="sidebar-button">
<div> <div class="sidebar-button-icon">
<SettingsIcon /> <SettingsIcon />
</div> </div>
<span>Settings</span> <span>Settings</span>
@ -58,7 +60,24 @@
<style> <style>
.unread-indicator { .unread-indicator {
color: var(--purple-2); display: inline-flex;
justify-content: center;
align-items: center;
background-color: var(--red-2);
padding-top: 1px;
padding-bottom: 1px;
padding-left: 0.375rem;
padding-right: 0.375rem;
border-radius: 9999px;
font-size: x-small;
}
.sidebar-channel-buttons {
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
flex-shrink: 0;
margin-left: auto; margin-left: auto;
} }
</style> </style>

View file

@ -103,22 +103,6 @@ class ChannelsStore extends Store {
this.value[index] = data; this.value[index] = data;
this.updated(); this.updated();
}); });
gateway.subscribe(GatewayEventType.MessageCreate, ({ channel_id }) => {
const index = this.value.findIndex(e => e.id === channel_id);
if (index === -1 || !this.value[index] || selectedChannel.value.id === channel_id)
return;
this.value[index]._hasUnreads = true;
this.updated();
});
selectedChannel.subscribe(({ id }) => {
const index = this.value.findIndex(e => e.id === id);
if (index === -1 || !this.value[index] || !this.value[index]._hasUnreads)
return;
this.value[index]._hasUnreads = false;
this.updated();
});
} }
} }
@ -448,6 +432,31 @@ class PresenceStore extends Store {
} }
} }
class UnreadStore extends Store {
constructor() {
super(new Map(), "UnreadStore");
gateway.subscribe(GatewayEventType.MessageCreate, ({ channel_id: channelId }) => {
if (selectedChannel.value.id !== channelId || window.document.visibilityState !== "visible") {
this.value.set(channelId, (this.value.get(channelId) || 0) + 1);
this.updated();
}
});
selectedChannel.subscribe(({ id }) => {
this.value.delete(id);
this.updated();
});
window.document.addEventListener("visibilitychange", () => {
if (window.document.visibilityState === "visible" && selectedChannel.value) {
this.value.delete(selectedChannel.value.id);
this.updated();
}
});
}
}
export const selectedChannel = new Store({ id: -1, name: "none", creator_id: -1 }, "selectedChannel"); export const selectedChannel = new Store({ id: -1, name: "none", creator_id: -1 }, "selectedChannel");
export const showSidebar = new Store(true, "showSidebar"); export const showSidebar = new Store(true, "showSidebar");
export const showPresenceSidebar = new Store(false, "showPresenceSidebar"); export const showPresenceSidebar = new Store(false, "showPresenceSidebar");
@ -462,14 +471,15 @@ export const userInfoStore = new UserInfoStore();
export const overlayStore = new OverlayStore(); export const overlayStore = new OverlayStore();
export const typingStore = new TypingStore(); export const typingStore = new TypingStore();
export const presenceStore = new PresenceStore(); export const presenceStore = new PresenceStore();
export const unreadStore = new UnreadStore();
export const setMessageInputEvent = new Store(null, "event:setMessageInput"); export const setMessageInputEvent = new Store(null, "event:setMessageInput");
export const allStores = { export const allStores = {
selectedChannel, selectedChannel,
showSidebar, showSidebar,
showPresenceSidebar, showPresenceSidebar,
showChannelView,
smallViewport, smallViewport,
showChannelView,
theme, theme,
doAnimations, doAnimations,
channels, channels,
@ -478,6 +488,9 @@ export const allStores = {
userInfoStore, userInfoStore,
overlayStore, overlayStore,
typingStore, typingStore,
presenceStore,
unreadStore,
setMessageInputEvent,
}; };
selectedChannel.watch((newSelectedChannel) => { selectedChannel.watch((newSelectedChannel) => {
@ -485,3 +498,14 @@ selectedChannel.watch((newSelectedChannel) => {
setItem("state:openChannelId", newSelectedChannel.id); setItem("state:openChannelId", newSelectedChannel.id);
} }
}); });
unreadStore.subscribe(() => {
let totalUnreads = 0;
unreadStore.value.forEach(count => totalUnreads += count);
if (totalUnreads > 0) {
window.document.title = `(${totalUnreads}) waffle`;
} else {
window.document.title = "waffle";
}
});