forked from coracle/flotilla
Add notification badges
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {page} from "$app/stores"
|
||||
import {remove} from "@welshman/lib"
|
||||
import {remove, assoc} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {pubkey, loadInboxRelaySelections} from "@welshman/app"
|
||||
import {fade} from "@lib/transition"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import ProfileName from "@app/components/ProfileName.svelte"
|
||||
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
||||
import ProfileCircles from "@app/components/ProfileCircles.svelte"
|
||||
import {makeChatPath} from "@app/routes"
|
||||
import {CHAT_FILTERS, deriveNotification} from "@app/notifications"
|
||||
|
||||
export let id: string
|
||||
export let pubkeys: string[]
|
||||
@@ -17,6 +19,8 @@
|
||||
const message = messages[0]
|
||||
const others = remove($pubkey!, pubkeys)
|
||||
const active = $page.params.chat === id
|
||||
const path = makeChatPath(pubkeys)
|
||||
const notification = deriveNotification(path, CHAT_FILTERS.map(assoc("authors", pubkeys)))
|
||||
|
||||
onMount(() => {
|
||||
for (const pk of others) {
|
||||
@@ -30,7 +34,7 @@
|
||||
class="cursor-pointer border-t border-solid border-base-100 px-6 py-2 transition-colors hover:bg-base-100 {$$props.class}"
|
||||
class:bg-base-100={active}>
|
||||
<div class="flex flex-col justify-start gap-1">
|
||||
<div class="flex justify-between gap-2">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
{#if others.length === 1}
|
||||
<ProfileCircle pubkey={others[0]} size={5} />
|
||||
@@ -44,6 +48,9 @@
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if !active && $notification}
|
||||
<div class="h-2 w-2 rounded-full bg-primary" transition:fade />
|
||||
{/if}
|
||||
</div>
|
||||
<p class="overflow-hidden text-ellipsis whitespace-nowrap text-sm">
|
||||
{message.content}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import {PLATFORM_NAME} from "@app/state"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {loadUserData} from "@app/commands"
|
||||
import {setChecked} from "@app/notifications"
|
||||
|
||||
const signUp = () => pushModal(SignUp)
|
||||
|
||||
@@ -32,6 +33,7 @@
|
||||
await loadUserData(session.pubkey, {relays})
|
||||
|
||||
pushToast({message: "Successfully logged in!"})
|
||||
setChecked("*")
|
||||
clearModals()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import InfoBunker from "@app/components/InfoBunker.svelte"
|
||||
import {loginWithNip46, loadUserData} from "@app/commands"
|
||||
import {pushModal, clearModals} from "@app/modal"
|
||||
import {setChecked} from "@app/notifications"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {PLATFORM_URL, PLATFORM_NAME, PLATFORM_LOGO, SIGNER_RELAYS} from "@app/state"
|
||||
|
||||
@@ -79,6 +80,7 @@
|
||||
|
||||
await loadUserData(pubkey)
|
||||
|
||||
setChecked("*")
|
||||
clearModals()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import SpaceJoin from "@app/components/SpaceJoin.svelte"
|
||||
import ProfileList from "@app/components/ProfileList.svelte"
|
||||
import RoomCreate from "@app/components/RoomCreate.svelte"
|
||||
import MenuSpaceRoomItem from "@app/components/MenuSpaceRoomItem.svelte"
|
||||
import {
|
||||
getMembershipRoomsByUrl,
|
||||
getMembershipUrls,
|
||||
@@ -22,11 +23,15 @@
|
||||
roomsByUrl,
|
||||
GENERAL,
|
||||
} from "@app/state"
|
||||
import {deriveNotification, THREAD_FILTERS} from "@app/notifications"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {makeSpacePath} from "@app/routes"
|
||||
|
||||
export let url
|
||||
|
||||
const threadsPath = makeSpacePath(url, "threads")
|
||||
const threadsNotification = deriveNotification(threadsPath, THREAD_FILTERS, url)
|
||||
|
||||
const openMenu = () => {
|
||||
showMenu = true
|
||||
}
|
||||
@@ -121,7 +126,7 @@
|
||||
</SecondaryNavItem>
|
||||
</div>
|
||||
<div in:fly={{delay: getDelay()}}>
|
||||
<SecondaryNavItem href={makeSpacePath(url, "threads")}>
|
||||
<SecondaryNavItem href={threadsPath} notification={$threadsNotification}>
|
||||
<Icon icon="notes-minimalistic" /> Threads
|
||||
</SecondaryNavItem>
|
||||
</div>
|
||||
@@ -130,17 +135,11 @@
|
||||
<SecondaryNavHeader>Your Rooms</SecondaryNavHeader>
|
||||
</div>
|
||||
<div transition:slide={{delay: getDelay()}}>
|
||||
<SecondaryNavItem href={makeSpacePath(url, GENERAL)}>
|
||||
<Icon icon="hashtag" />
|
||||
{GENERAL}
|
||||
</SecondaryNavItem>
|
||||
<MenuSpaceRoomItem {url} room={GENERAL} />
|
||||
</div>
|
||||
{#each rooms as room, i (room)}
|
||||
<div transition:slide={{delay: getDelay()}}>
|
||||
<SecondaryNavItem href={makeSpacePath(url, room)}>
|
||||
<Icon icon="hashtag" />
|
||||
{room}
|
||||
</SecondaryNavItem>
|
||||
<MenuSpaceRoomItem {url} {room} />
|
||||
</div>
|
||||
{/each}
|
||||
{#if otherRooms.length > 0}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
|
||||
import {makeSpacePath} from "@app/routes"
|
||||
import {deriveNotification, getRoomFilters} from "@app/notifications"
|
||||
|
||||
export let url
|
||||
export let room
|
||||
|
||||
const path = makeSpacePath(url, room)
|
||||
const notification = deriveNotification(path, getRoomFilters(room), url)
|
||||
</script>
|
||||
|
||||
<SecondaryNavItem href={path} notification={$notification}>
|
||||
<Icon icon="hashtag" />
|
||||
{room}
|
||||
</SecondaryNavItem>
|
||||
@@ -1,16 +1,17 @@
|
||||
<script lang="ts">
|
||||
import {displayRelayUrl} from "@welshman/util"
|
||||
import {userProfile} from "@welshman/app"
|
||||
import Avatar from "@lib/components/Avatar.svelte"
|
||||
import Divider from "@lib/components/Divider.svelte"
|
||||
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
|
||||
import SpaceAdd from "@app/components/SpaceAdd.svelte"
|
||||
import SpaceAvatar from "@app/components/SpaceAvatar.svelte"
|
||||
import MenuSpaces from "@app/components/MenuSpaces.svelte"
|
||||
import MenuSettings from "@app/components/MenuSettings.svelte"
|
||||
import PrimaryNavItemSpace from "@app/components/PrimaryNavItemSpace.svelte"
|
||||
import {userMembership, getMembershipUrls, PLATFORM_RELAY, PLATFORM_LOGO} from "@app/state"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {makeSpacePath} from "@app/routes"
|
||||
import {deriveNotification, CHAT_FILTERS} from "@app/notifications"
|
||||
|
||||
const chatNotification = deriveNotification("/chat", CHAT_FILTERS)
|
||||
|
||||
const addSpace = () => pushModal(SpaceAdd)
|
||||
|
||||
@@ -24,24 +25,14 @@
|
||||
<div class="flex h-full flex-col justify-between">
|
||||
<div>
|
||||
{#if PLATFORM_RELAY}
|
||||
<PrimaryNavItem
|
||||
title={displayRelayUrl(PLATFORM_RELAY)}
|
||||
href={makeSpacePath(PLATFORM_RELAY)}
|
||||
class="tooltip-right">
|
||||
<SpaceAvatar url={PLATFORM_RELAY} />
|
||||
</PrimaryNavItem>
|
||||
<PrimaryNavItemSpace url={PLATFORM_RELAY} />
|
||||
{:else}
|
||||
<PrimaryNavItem title="Home" href="/home" class="tooltip-right">
|
||||
<Avatar src={PLATFORM_LOGO} class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
<Divider />
|
||||
{#each getMembershipUrls($userMembership) as url (url)}
|
||||
<PrimaryNavItem
|
||||
title={displayRelayUrl(url)}
|
||||
href={makeSpacePath(url)}
|
||||
class="tooltip-right">
|
||||
<SpaceAvatar {url} />
|
||||
</PrimaryNavItem>
|
||||
<PrimaryNavItemSpace {url} />
|
||||
{/each}
|
||||
<PrimaryNavItem title="Add Space" on:click={addSpace} class="tooltip-right">
|
||||
<Avatar icon="settings-minimalistic" class="!h-10 !w-10" />
|
||||
@@ -59,7 +50,11 @@
|
||||
<PrimaryNavItem title="Notes" href="/notes" class="tooltip-right">
|
||||
<Avatar icon="notes-minimalistic" class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
<PrimaryNavItem title="Messages" href="/chat" class="tooltip-right">
|
||||
<PrimaryNavItem
|
||||
title="Messages"
|
||||
href="/chat"
|
||||
class="tooltip-right"
|
||||
notification={$chatNotification}>
|
||||
<Avatar icon="letter" class="!h-10 !w-10" />
|
||||
</PrimaryNavItem>
|
||||
<PrimaryNavItem title="Search" href="/people" class="tooltip-right">
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import {displayRelayUrl} from "@welshman/util"
|
||||
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
|
||||
import SpaceAvatar from "@app/components/SpaceAvatar.svelte"
|
||||
import {makeSpacePath} from "@app/routes"
|
||||
import {deriveNotification, SPACE_FILTERS} from "@app/notifications"
|
||||
|
||||
export let url
|
||||
|
||||
const path = makeSpacePath(url)
|
||||
const notification = deriveNotification(path, SPACE_FILTERS, url)
|
||||
</script>
|
||||
|
||||
<PrimaryNavItem
|
||||
title={displayRelayUrl(url)}
|
||||
href={path}
|
||||
class="tooltip-right"
|
||||
notification={$notification}>
|
||||
<SpaceAvatar {url} />
|
||||
</PrimaryNavItem>
|
||||
@@ -10,6 +10,7 @@
|
||||
import LogIn from "@app/components/LogIn.svelte"
|
||||
import InfoNostr from "@app/components/InfoNostr.svelte"
|
||||
import {pushModal, clearModals} from "@app/modal"
|
||||
import {setChecked} from "@app/notifications"
|
||||
import {PLATFORM_NAME} from "@app/state"
|
||||
import {pushToast} from "@app/toast"
|
||||
|
||||
@@ -42,6 +43,7 @@
|
||||
if (await loginBroker.connect("", nip46Perms)) {
|
||||
addSession({method: "nip46", pubkey, secret, handler: {...handler, pubkey}})
|
||||
pushToast({message: "Successfully logged in!"})
|
||||
setChecked("*")
|
||||
clearModals()
|
||||
} else {
|
||||
pushToast({
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
import ThunkStatus from "@app/components/ThunkStatus.svelte"
|
||||
import ThreadMenu from "@app/components/ThreadMenu.svelte"
|
||||
import {publishDelete, publishReaction} from "@app/commands"
|
||||
import {deriveNotification} from "@app/notifications"
|
||||
import {makeSpacePath} from "@app/routes"
|
||||
import {COMMENT} from "@app/state"
|
||||
|
||||
export let url
|
||||
@@ -21,7 +23,10 @@
|
||||
|
||||
const thunk = $thunks[event.id]
|
||||
const deleted = deriveIsDeleted(repository, event)
|
||||
const replies = deriveEvents(repository, {filters: [{kinds: [COMMENT], "#E": [event.id]}]})
|
||||
const path = makeSpacePath(url, "threads", event.id)
|
||||
const filters = [{kinds: [COMMENT], "#E": [event.id]}]
|
||||
const notification = deriveNotification(path, filters, url)
|
||||
const replies = deriveEvents(repository, {filters})
|
||||
|
||||
const showPopover = () => popover.show()
|
||||
|
||||
@@ -58,7 +63,10 @@
|
||||
<Icon icon="reply" />
|
||||
<span>{$replies.length} {$replies.length === 1 ? "reply" : "replies"}</span>
|
||||
</div>
|
||||
<div class="btn btn-neutral btn-xs hidden rounded-full sm:flex">
|
||||
<div class="btn btn-neutral btn-xs relative hidden rounded-full sm:flex">
|
||||
{#if $notification}
|
||||
<div class="h-2 w-2 rounded-full bg-primary" />
|
||||
{/if}
|
||||
Active {formatTimestampRelative(lastActive)}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user