significantly improve unread system
This commit is contained in:
parent
81e9de6cd8
commit
7b9379732e
3 changed files with 74 additions and 31 deletions
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)}
|
||||||
|
<div class="unread-indicator">{ $unreadStore.get(channel.id) }</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $userInfoStore && (channel.owner_id === $userInfoStore.id || $userInfoStore.is_superuser)}
|
{#if $userInfoStore && (channel.owner_id === $userInfoStore.id || $userInfoStore.is_superuser)}
|
||||||
<button class="icon-button icon-button-auto" on:click|stopPropagation="{ () => overlayStore.open('editChannel', { channel }) }" aria-label="Edit Channel">
|
<button class="icon-button" on:click|stopPropagation="{ () => overlayStore.open('editChannel', { channel }) }" aria-label="Edit Channel">
|
||||||
<MoreVerticalIcon />
|
<MoreVerticalIcon />
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/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>
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue