Bring back some notification badges

This commit is contained in:
Jon Staab
2026-02-27 12:25:16 -08:00
parent dfedf4e879
commit ccfe1bded5
5 changed files with 52 additions and 7 deletions
+1 -1
View File
@@ -256,7 +256,7 @@
<SecondaryNavHeader>Your Rooms</SecondaryNavHeader> <SecondaryNavHeader>Your Rooms</SecondaryNavHeader>
{/if} {/if}
{#each $userRooms as h, i (h)} {#each $userRooms as h, i (h)}
<SpaceMenuRoomItem {replaceState} {url} {h} /> <SpaceMenuRoomItem notify {replaceState} {url} {h} />
{/each} {/each}
{#if $otherRooms.length > 0} {#if $otherRooms.length > 0}
<div class="h-2"></div> <div class="h-2"></div>
@@ -3,11 +3,15 @@
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import SpaceMenu from "@app/components/SpaceMenu.svelte" import SpaceMenu from "@app/components/SpaceMenu.svelte"
import {notifications} from "@app/util/notifications"
import {makeSpacePath} from "@app/util/routes"
import {pushDrawer} from "@app/util/modal" import {pushDrawer} from "@app/util/modal"
import {deriveSocketStatus} from "@app/core/state" import {deriveSocketStatus} from "@app/core/state"
const {url} = $props() const {url} = $props()
const path = makeSpacePath(url) + ":mobile"
const status = deriveSocketStatus(url) const status = deriveSocketStatus(url)
const openMenu = () => pushDrawer(SpaceMenu, {url}) const openMenu = () => pushDrawer(SpaceMenu, {url})
@@ -17,5 +21,7 @@
<Icon icon={MenuDots} /> <Icon icon={MenuDots} />
{#if $status.theme !== "success"} {#if $status.theme !== "success"}
<div class="absolute right-0 top-0 -mr-1 -mt-1 h-2 w-2 rounded-full bg-{$status.theme}"></div> <div class="absolute right-0 top-0 -mr-1 -mt-1 h-2 w-2 rounded-full bg-{$status.theme}"></div>
{:else if $notifications.has(path)}
<div class="absolute right-0 top-0 -mr-1 -mt-1 h-2 w-2 rounded-full bg-primary"></div>
{/if} {/if}
</Button> </Button>
+7 -2
View File
@@ -4,16 +4,18 @@
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte" import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
import RoomNameWithImage from "@app/components/RoomNameWithImage.svelte" import RoomNameWithImage from "@app/components/RoomNameWithImage.svelte"
import {notifications} from "@app/util/notifications"
import {makeRoomPath} from "@app/util/routes" import {makeRoomPath} from "@app/util/routes"
import {deriveShouldNotify} from "@app/core/state" import {deriveShouldNotify} from "@app/core/state"
interface Props { interface Props {
url: any url: any
h: any h: any
notify?: boolean
replaceState?: boolean replaceState?: boolean
} }
const {url, h, replaceState = false}: Props = $props() const {url, h, notify = false, replaceState = false}: Props = $props()
const path = makeRoomPath(url, h) const path = makeRoomPath(url, h)
const shouldNotifyForSpace = deriveShouldNotify(url) const shouldNotifyForSpace = deriveShouldNotify(url)
@@ -21,7 +23,10 @@
const showDifferenceIcon = $derived($shouldNotifyForRoom !== $shouldNotifyForSpace) const showDifferenceIcon = $derived($shouldNotifyForRoom !== $shouldNotifyForSpace)
</script> </script>
<SecondaryNavItem href={path} {replaceState}> <SecondaryNavItem
href={path}
{replaceState}
notification={notify ? $notifications.has(path) : false}>
<RoomNameWithImage {url} {h} /> <RoomNameWithImage {url} {h} />
{#if showDifferenceIcon} {#if showDifferenceIcon}
<Icon icon={$shouldNotifyForRoom ? VolumeLoud : VolumeCross} size={4} class="opacity-50" /> <Icon icon={$shouldNotifyForRoom ? VolumeLoud : VolumeCross} size={4} class="opacity-50" />
+36 -2
View File
@@ -12,12 +12,14 @@ import {
repository, repository,
publishThunk, publishThunk,
loadRelay, loadRelay,
relaysByUrl,
waitForThunkError, waitForThunkError,
userMessagingRelayList, userMessagingRelayList,
} from "@welshman/app" } from "@welshman/app"
import { import {
on, on,
call, call,
find,
assoc, assoc,
poll, poll,
prop, prop,
@@ -44,7 +46,14 @@ import {
Address, Address,
} from "@welshman/util" } from "@welshman/util"
import {buildUrl} from "@lib/util" import {buildUrl} from "@lib/util"
import {makeSpacePath, makeChatPath, getEventPath, goToEvent} from "@app/util/routes" import {
makeSpacePath,
makeRoomPath,
makeSpaceChatPath,
makeChatPath,
getEventPath,
goToEvent,
} from "@app/util/routes"
import { import {
DM_KINDS, DM_KINDS,
CONTENT_KINDS, CONTENT_KINDS,
@@ -57,9 +66,11 @@ import {
userSettingsValues, userSettingsValues,
userGroupList, userGroupList,
getSpaceUrlsFromGroupList, getSpaceUrlsFromGroupList,
getSpaceRoomsFromGroupList,
makeCommentFilter, makeCommentFilter,
userSpaceUrls, userSpaceUrls,
shouldNotify, shouldNotify,
hasNip29,
device, device,
} from "@app/core/state" } from "@app/core/state"
import {kv} from "@app/core/storage" import {kv} from "@app/core/storage"
@@ -125,6 +136,7 @@ export const allNotifications = derived(
pubkey, pubkey,
checked, checked,
chatsById, chatsById,
relaysByUrl,
userGroupList, userGroupList,
deriveEventsByIdByUrl({ deriveEventsByIdByUrl({
tracker, tracker,
@@ -135,7 +147,7 @@ export const allNotifications = derived(
identity, identity,
), ),
), ),
([$pubkey, $checked, $chatsById, $userGroupList, eventsByIdByUrl]) => { ([$pubkey, $checked, $chatsById, $relaysByUrl, $userGroupList, eventsByIdByUrl]) => {
const hasNotification = (path: string, latestEvent?: TrustedEvent) => { const hasNotification = (path: string, latestEvent?: TrustedEvent) => {
if (!latestEvent || latestEvent.pubkey === $pubkey) { if (!latestEvent || latestEvent.pubkey === $pubkey) {
return false return false
@@ -168,12 +180,34 @@ export const allNotifications = derived(
for (const url of getSpaceUrlsFromGroupList($userGroupList)) { for (const url of getSpaceUrlsFromGroupList($userGroupList)) {
const spacePath = makeSpacePath(url) const spacePath = makeSpacePath(url)
const spacePathMobile = spacePath + ":mobile"
const eventsById = eventsByIdByUrl.get(url) || new Map() const eventsById = eventsByIdByUrl.get(url) || new Map()
const latestEvent = first(sortEventsDesc(eventsById.values())) const latestEvent = first(sortEventsDesc(eventsById.values()))
if (hasNotification(spacePath, latestEvent)) { if (hasNotification(spacePath, latestEvent)) {
paths.add(spacePath) paths.add(spacePath)
} }
if (hasNip29($relaysByUrl.get(url))) {
for (const h of getSpaceRoomsFromGroupList(url, $userGroupList)) {
const roomPath = makeRoomPath(url, h)
const latestEvent = find(e => e.tags.some(spec(["h", h])), eventsById.values())
if (hasNotification(roomPath, latestEvent)) {
paths.add(spacePathMobile)
paths.add(spacePath)
paths.add(roomPath)
}
}
} else {
const messagesPath = makeSpaceChatPath(url)
if (hasNotification(messagesPath, first(eventsById.values()))) {
paths.add(spacePathMobile)
paths.add(spacePath)
paths.add(messagesPath)
}
}
} }
return paths return paths
+2 -2
View File
@@ -38,7 +38,7 @@
class:text-base-content={active} class:text-base-content={active}
class:bg-base-100={active}> class:bg-base-100={active}>
{@render children?.()} {@render children?.()}
{#if !active && notification} {#if notification}
<div class="absolute right-2 top-5 h-2 w-2 rounded-full bg-primary" transition:fade></div> <div class="absolute right-2 top-5 h-2 w-2 rounded-full bg-primary" transition:fade></div>
{/if} {/if}
</a> </a>
@@ -48,7 +48,7 @@
class="{restProps.class} relative flex w-full items-center gap-3 text-left transition-all hover:bg-base-100 hover:text-base-content" class="{restProps.class} relative flex w-full items-center gap-3 text-left transition-all hover:bg-base-100 hover:text-base-content"
class:text-base-content={active} class:text-base-content={active}
class:bg-base-100={active}> class:bg-base-100={active}>
{#if !active && notification} {#if notification}
<div class="absolute right-2 top-5 h-2 w-2 rounded-full bg-primary" transition:fade></div> <div class="absolute right-2 top-5 h-2 w-2 rounded-full bg-primary" transition:fade></div>
{/if} {/if}
{@render children?.()} {@render children?.()}