diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx
index 6a1c8f268d..5b9223b5ee 100644
--- a/packages/app/src/app.tsx
+++ b/packages/app/src/app.tsx
@@ -38,7 +38,7 @@ import { HighlightsProvider } from "@/context/highlights"
import { LanguageProvider, type Locale, useLanguage } from "@/context/language"
import { LayoutProvider } from "@/context/layout"
import { ModelsProvider } from "@/context/models"
-import { NotificationProvider } from "@/context/notification"
+import { NotificationProvider, useNotification } from "@/context/notification"
import { PermissionProvider } from "@/context/permission"
import { PromptProvider } from "@/context/prompt"
import { ServerConnection, ServerProvider, serverName, useServer } from "@/context/server"
@@ -316,9 +316,7 @@ function ServerScopedProviders(props: ServerScopedShellProps) {
return (
-
- {props.children}
-
+ {props.children}
)
@@ -345,13 +343,23 @@ function NewAppLayout(props: ParentProps) {
function TargetServerScopedProviders(props: ServerScopedShellProps) {
return (
-
- {props.children}
-
+
+ {props.children}
)
}
+function MarkSessionNotificationsViewed(props: { sessionID?: () => string | undefined }) {
+ const notification = useNotification()
+ createEffect(() => {
+ const sessionID = props.sessionID?.()
+ if (!notification.ready() || !sessionID) return
+ if (notification.session.unseenCount(sessionID) === 0) return
+ notification.session.markViewed(sessionID)
+ })
+ return null
+}
+
function SessionProviders(props: ParentProps) {
return (
@@ -560,11 +568,13 @@ export function AppInterface(props: {
component={props.router ?? Router}
root={(routerProps) => (
-
-
- {routerProps.children}
-
-
+
+
+
+ {routerProps.children}
+
+
+
)}
>
diff --git a/packages/app/src/context/notification.tsx b/packages/app/src/context/notification.tsx
index 7318af084f..e12f8f1250 100644
--- a/packages/app/src/context/notification.tsx
+++ b/packages/app/src/context/notification.tsx
@@ -1,9 +1,9 @@
import { createStore, reconcile } from "solid-js/store"
-import { type Accessor, batch, createEffect, createMemo, onCleanup } from "solid-js"
-import { useParams } from "@solidjs/router"
+import { type Accessor, batch, createEffect, createMemo, createRoot, getOwner, onCleanup } from "solid-js"
+import { useParams, useSearchParams } from "@solidjs/router"
import { createSimpleContext } from "@opencode-ai/ui/context"
-import { useServerSDK } from "./server-sdk"
-import { useServerSync } from "./server-sync"
+import type { ServerSDK } from "./server-sdk"
+import type { ServerSync } from "./server-sync"
import { usePlatform } from "@/context/platform"
import { useLanguage } from "@/context/language"
import { useSettings } from "@/context/settings"
@@ -12,6 +12,11 @@ import { decode64 } from "@/utils/base64"
import { EventSessionError } from "@opencode-ai/sdk/v2"
import { Persist, persisted } from "@/utils/persist"
import { playSoundById } from "@/utils/sound"
+import { useGlobal } from "./global"
+import { ServerConnection, useServer } from "./server"
+import { type DraftTab, useTabs } from "./tabs"
+import { requireServerKey } from "@/utils/session-route"
+import type { ServerScope } from "@/utils/server-scope"
type NotificationBase = {
directory?: string
@@ -107,267 +112,360 @@ function buildNotificationIndex(list: Notification[]) {
export const { use: useNotification, provider: NotificationProvider } = createSimpleContext({
name: "Notification",
gate: false,
- init: (props: { directory?: Accessor; sessionID?: Accessor }) => {
- const params = useParams()
- const serverSDK = useServerSDK()
- const serverSync = useServerSync()
+ init: () => {
+ const params = useParams<{ serverKey?: string; dir?: string; id?: string }>()
+ const [search] = useSearchParams<{ draftId?: string }>()
+ const global = useGlobal()
+ const server = useServer()
+ const tabs = useTabs()
const platform = usePlatform()
const settings = useSettings()
const language = useLanguage()
+ const owner = getOwner()
+ const states = new Map void; state: NotificationState }>()
- const empty: Notification[] = []
-
- const currentDirectory = createMemo(() => {
- return props.directory?.() ?? decode64(params.dir)
+ const activeServer = createMemo(() => {
+ if (params.serverKey) return requireServerKey(params.serverKey)
+ if (search.draftId) {
+ const draft = tabs.store.find((tab): tab is DraftTab => tab.type === "draft" && tab.draftID === search.draftId)
+ if (draft) return draft.server
+ }
+ return server.key
})
+ const activeDirectory = createMemo(() => decode64(params.dir))
+ const activeSession = createMemo(() => params.id)
- const currentSession = createMemo(() => props.sessionID?.() ?? params.id)
-
- const [store, setStore, _, ready] = persisted(
- Persist.serverGlobal(serverSDK().scope, "notification", ["notification.v1"]),
- createStore({
- list: [] as Notification[],
- }),
- )
- const [index, setIndex] = createStore(buildNotificationIndex(store.list))
-
- const meta = { pruned: false, disposed: false }
-
- const updateUnseen = (scope: "session" | "project", key: string, unseen: Notification[]) => {
- setIndex(scope, "unseen", key, unseen)
- setIndex(scope, "unseenCount", key, unseen.length)
- setIndex(
- scope,
- "unseenHasError",
- key,
- unseen.some((notification) => notification.type === "error"),
+ const ensure = (key: ServerConnection.Key) => {
+ const conn = global.servers.list().find((item) => ServerConnection.key(item) === key)
+ if (!conn) throw new Error(`Notification server not found: ${key}`)
+ const ctx = global.ensureServerCtx(conn)
+ const existing = states.get(ctx.sdk.scope)
+ if (existing) return existing.state
+ const root = createRoot(
+ (dispose) => ({
+ dispose,
+ state: createServerNotificationState({
+ sdk: ctx.sdk,
+ sync: ctx.sync,
+ active: () => server.scope(activeServer()) === ctx.sdk.scope,
+ directory: activeDirectory,
+ sessionID: activeSession,
+ platform,
+ settings,
+ language,
+ }),
+ }),
+ owner ?? undefined,
)
- }
-
- const appendToIndex = (notification: Notification) => {
- if (notification.session) {
- setIndex("session", "all", notification.session, (all = []) => [...all, notification])
- if (!notification.viewed) {
- setIndex("session", "unseen", notification.session, (unseen = []) => [...unseen, notification])
- setIndex("session", "unseenCount", notification.session, (count = 0) => count + 1)
- if (notification.type === "error") setIndex("session", "unseenHasError", notification.session, true)
- }
- }
-
- if (notification.directory) {
- setIndex("project", "all", notification.directory, (all = []) => [...all, notification])
- if (!notification.viewed) {
- setIndex("project", "unseen", notification.directory, (unseen = []) => [...unseen, notification])
- setIndex("project", "unseenCount", notification.directory, (count = 0) => count + 1)
- if (notification.type === "error") setIndex("project", "unseenHasError", notification.directory, true)
- }
- }
- }
-
- const removeFromIndex = (notification: Notification) => {
- if (notification.session) {
- setIndex("session", "all", notification.session, (all = []) => all.filter((n) => n !== notification))
- if (!notification.viewed) {
- const unseen = (index.session.unseen[notification.session] ?? empty).filter((n) => n !== notification)
- updateUnseen("session", notification.session, unseen)
- }
- }
-
- if (notification.directory) {
- setIndex("project", "all", notification.directory, (all = []) => all.filter((n) => n !== notification))
- if (!notification.viewed) {
- const unseen = (index.project.unseen[notification.directory] ?? empty).filter((n) => n !== notification)
- updateUnseen("project", notification.directory, unseen)
- }
- }
+ states.set(ctx.sdk.scope, root)
+ return root.state
}
createEffect(() => {
- if (!ready()) return
- if (meta.pruned) return
- meta.pruned = true
- const list = pruneNotifications(store.list)
- batch(() => {
- setStore("list", list)
- setIndex(reconcile(buildNotificationIndex(list), { merge: false }))
+ global.servers.list().forEach((conn) => ensure(ServerConnection.key(conn)))
+ })
+
+ createEffect(() => {
+ const scopes = new Set(global.servers.list().map((conn) => server.scope(ServerConnection.key(conn))))
+ states.forEach((value, scope) => {
+ if (scopes.has(scope)) return
+ value.dispose()
+ states.delete(scope)
})
})
- const append = (notification: Notification) => {
- const list = pruneNotifications([...store.list, notification])
- const keep = new Set(list)
- const removed = store.list.filter((n) => !keep.has(n))
+ onCleanup(() => states.forEach((value) => value.dispose()))
- batch(() => {
- if (keep.has(notification)) appendToIndex(notification)
- removed.forEach((n) => removeFromIndex(n))
- setStore("list", list)
- })
- }
-
- const lookup = async (directory: string, sessionID?: string) => {
- if (!sessionID) return undefined
- const sync = serverSync().ensureDirSyncContext(directory)
- const session = sync.session.get(sessionID)
- if (session) return session
- return sync.session
- .sync(sessionID)
- .then(() => sync.session.get(sessionID))
- .catch(() => undefined)
- }
-
- const viewedInCurrentSession = (directory: string, sessionID?: string) => {
- const activeDirectory = currentDirectory()
- const activeSession = currentSession()
- if (!activeDirectory) return false
- if (!activeSession) return false
- if (!sessionID) return false
- if (directory !== activeDirectory) return false
- return sessionID === activeSession
- }
-
- const handleSessionIdle = (directory: string, event: { properties: { sessionID?: string } }, time: number) => {
- const sessionID = event.properties.sessionID
- void lookup(directory, sessionID).then((session) => {
- if (meta.disposed) return
- if (!session) return
- if (session.parentID) return
-
- if (settings.sounds.agentEnabled()) {
- void playSoundById(settings.sounds.agent())
- }
-
- append({
- directory,
- time,
- viewed: viewedInCurrentSession(directory, sessionID),
- type: "turn-complete",
- session: sessionID,
- })
-
- const href = `/${base64Encode(directory)}/session/${sessionID}`
- if (settings.notifications.agent()) {
- void platform.notify(language.t("notification.session.responseReady.title"), session.title ?? sessionID, href)
- }
- })
- }
-
- const handleSessionError = (
- directory: string,
- event: { properties: { sessionID?: string; error?: EventSessionError["properties"]["error"] } },
- time: number,
- ) => {
- const sessionID = event.properties.sessionID
- void lookup(directory, sessionID).then((session) => {
- if (meta.disposed) return
- if (session?.parentID) return
-
- if (settings.sounds.errorsEnabled()) {
- void playSoundById(settings.sounds.errors())
- }
-
- const error = "error" in event.properties ? event.properties.error : undefined
- append({
- directory,
- time,
- viewed: viewedInCurrentSession(directory, sessionID),
- type: "error",
- session: sessionID ?? "global",
- error,
- })
- const description =
- session?.title ??
- (typeof error === "string" ? error : language.t("notification.session.error.fallbackDescription"))
- const href = sessionID ? `/${base64Encode(directory)}/session/${sessionID}` : `/${base64Encode(directory)}`
- if (settings.notifications.errors()) {
- void platform.notify(language.t("notification.session.error.title"), description, href)
- }
- })
- }
-
- const unsub = serverSDK().event.listen((e) => {
- const event = e.details
- if (event.type !== "session.idle" && event.type !== "session.error") return
-
- const directory = e.name
- const time = Date.now()
- if (event.type === "session.idle") {
- handleSessionIdle(directory, event, time)
- return
- }
- handleSessionError(directory, event, time)
- })
- onCleanup(() => {
- meta.disposed = true
- unsub()
- })
+ const selected = () => ensure(activeServer())
return {
- ready,
+ ready: () => selected().ready(),
+ ensureServerState: ensure,
session: {
- all(session: string) {
- return index.session.all[session] ?? empty
- },
- unseen(session: string) {
- return index.session.unseen[session] ?? empty
- },
- unseenCount(session: string) {
- return index.session.unseenCount[session] ?? 0
- },
- unseenHasError(session: string) {
- return index.session.unseenHasError[session] ?? false
- },
- markViewed(session: string) {
- const unseen = index.session.unseen[session] ?? empty
- if (!unseen.length) return
-
- const projects = [
- ...new Set(unseen.flatMap((notification) => (notification.directory ? [notification.directory] : []))),
- ]
- batch(() => {
- setStore("list", (n) => n.session === session && !n.viewed, "viewed", true)
- updateUnseen("session", session, [])
- projects.forEach((directory) => {
- const next = (index.project.unseen[directory] ?? empty).filter(
- (notification) => notification.session !== session,
- )
- updateUnseen("project", directory, next)
- })
- })
- },
+ all: (session: string) => selected().session.all(session),
+ unseen: (session: string) => selected().session.unseen(session),
+ unseenCount: (session: string) => selected().session.unseenCount(session),
+ unseenHasError: (session: string) => selected().session.unseenHasError(session),
+ markViewed: (session: string) => selected().session.markViewed(session),
},
project: {
- all(directory: string) {
- return index.project.all[directory] ?? empty
- },
- unseen(directory: string) {
- return index.project.unseen[directory] ?? empty
- },
- unseenCount(directory: string) {
- return index.project.unseenCount[directory] ?? 0
- },
- unseenHasError(directory: string) {
- return index.project.unseenHasError[directory] ?? false
- },
- markViewed(directory: string) {
- const unseen = index.project.unseen[directory] ?? empty
- if (!unseen.length) return
-
- const sessions = [
- ...new Set(unseen.flatMap((notification) => (notification.session ? [notification.session] : []))),
- ]
- batch(() => {
- setStore("list", (n) => n.directory === directory && !n.viewed, "viewed", true)
- updateUnseen("project", directory, [])
- sessions.forEach((session) => {
- const next = (index.session.unseen[session] ?? empty).filter(
- (notification) => notification.directory !== directory,
- )
- updateUnseen("session", session, next)
- })
- })
- },
+ all: (directory: string) => selected().project.all(directory),
+ unseen: (directory: string) => selected().project.unseen(directory),
+ unseenCount: (directory: string) => selected().project.unseenCount(directory),
+ unseenHasError: (directory: string) => selected().project.unseenHasError(directory),
+ markViewed: (directory: string) => selected().project.markViewed(directory),
},
}
},
})
+
+type NotificationState = ReturnType
+
+function createServerNotificationState(input: {
+ sdk: ServerSDK
+ sync: ServerSync
+ active: Accessor
+ directory: Accessor
+ sessionID: Accessor
+ platform: ReturnType
+ settings: ReturnType
+ language: ReturnType
+}) {
+ const serverSDK = () => input.sdk
+ const serverSync = () => input.sync
+ const platform = input.platform
+ const settings = input.settings
+ const language = input.language
+
+ const empty: Notification[] = []
+
+ const currentDirectory = input.directory
+ const currentSession = input.sessionID
+
+ const [store, setStore, _, ready] = persisted(
+ Persist.serverGlobal(serverSDK().scope, "notification", ["notification.v1"]),
+ createStore({
+ list: [] as Notification[],
+ }),
+ )
+ const [index, setIndex] = createStore(buildNotificationIndex(store.list))
+
+ const meta = { pruned: false, disposed: false }
+
+ const updateUnseen = (scope: "session" | "project", key: string, unseen: Notification[]) => {
+ setIndex(scope, "unseen", key, unseen)
+ setIndex(scope, "unseenCount", key, unseen.length)
+ setIndex(
+ scope,
+ "unseenHasError",
+ key,
+ unseen.some((notification) => notification.type === "error"),
+ )
+ }
+
+ const appendToIndex = (notification: Notification) => {
+ if (notification.session) {
+ setIndex("session", "all", notification.session, (all = []) => [...all, notification])
+ if (!notification.viewed) {
+ setIndex("session", "unseen", notification.session, (unseen = []) => [...unseen, notification])
+ setIndex("session", "unseenCount", notification.session, (count = 0) => count + 1)
+ if (notification.type === "error") setIndex("session", "unseenHasError", notification.session, true)
+ }
+ }
+
+ if (notification.directory) {
+ setIndex("project", "all", notification.directory, (all = []) => [...all, notification])
+ if (!notification.viewed) {
+ setIndex("project", "unseen", notification.directory, (unseen = []) => [...unseen, notification])
+ setIndex("project", "unseenCount", notification.directory, (count = 0) => count + 1)
+ if (notification.type === "error") setIndex("project", "unseenHasError", notification.directory, true)
+ }
+ }
+ }
+
+ const removeFromIndex = (notification: Notification) => {
+ if (notification.session) {
+ setIndex("session", "all", notification.session, (all = []) => all.filter((n) => n !== notification))
+ if (!notification.viewed) {
+ const unseen = (index.session.unseen[notification.session] ?? empty).filter((n) => n !== notification)
+ updateUnseen("session", notification.session, unseen)
+ }
+ }
+
+ if (notification.directory) {
+ setIndex("project", "all", notification.directory, (all = []) => all.filter((n) => n !== notification))
+ if (!notification.viewed) {
+ const unseen = (index.project.unseen[notification.directory] ?? empty).filter((n) => n !== notification)
+ updateUnseen("project", notification.directory, unseen)
+ }
+ }
+ }
+
+ createEffect(() => {
+ if (!ready()) return
+ if (meta.pruned) return
+ meta.pruned = true
+ const list = pruneNotifications(store.list)
+ batch(() => {
+ setStore("list", list)
+ setIndex(reconcile(buildNotificationIndex(list), { merge: false }))
+ })
+ })
+
+ const append = (notification: Notification) => {
+ const list = pruneNotifications([...store.list, notification])
+ const keep = new Set(list)
+ const removed = store.list.filter((n) => !keep.has(n))
+
+ batch(() => {
+ if (keep.has(notification)) appendToIndex(notification)
+ removed.forEach((n) => removeFromIndex(n))
+ setStore("list", list)
+ })
+ }
+
+ const lookup = async (directory: string, sessionID?: string) => {
+ if (!sessionID) return undefined
+ const sync = serverSync().ensureDirSyncContext(directory)
+ const session = sync.session.get(sessionID)
+ if (session) return session
+ return sync.session
+ .sync(sessionID)
+ .then(() => sync.session.get(sessionID))
+ .catch(() => undefined)
+ }
+
+ const viewedInCurrentSession = (directory: string, sessionID?: string) => {
+ if (!input.active()) return false
+ const activeDirectory = currentDirectory()
+ const activeSession = currentSession()
+ if (!activeSession) return false
+ if (!sessionID) return false
+ if (activeDirectory && directory !== activeDirectory) return false
+ return sessionID === activeSession
+ }
+
+ const handleSessionIdle = (directory: string, event: { properties: { sessionID?: string } }, time: number) => {
+ const sessionID = event.properties.sessionID
+ void lookup(directory, sessionID).then((session) => {
+ if (meta.disposed) return
+ if (!session) return
+ if (session.parentID) return
+
+ if (settings.sounds.agentEnabled()) {
+ void playSoundById(settings.sounds.agent())
+ }
+
+ append({
+ directory,
+ time,
+ viewed: viewedInCurrentSession(directory, sessionID),
+ type: "turn-complete",
+ session: sessionID,
+ })
+
+ const href = `/${base64Encode(directory)}/session/${sessionID}`
+ if (settings.notifications.agent()) {
+ void platform.notify(language.t("notification.session.responseReady.title"), session.title ?? sessionID, href)
+ }
+ })
+ }
+
+ const handleSessionError = (
+ directory: string,
+ event: { properties: { sessionID?: string; error?: EventSessionError["properties"]["error"] } },
+ time: number,
+ ) => {
+ const sessionID = event.properties.sessionID
+ void lookup(directory, sessionID).then((session) => {
+ if (meta.disposed) return
+ if (session?.parentID) return
+
+ if (settings.sounds.errorsEnabled()) {
+ void playSoundById(settings.sounds.errors())
+ }
+
+ const error = "error" in event.properties ? event.properties.error : undefined
+ append({
+ directory,
+ time,
+ viewed: viewedInCurrentSession(directory, sessionID),
+ type: "error",
+ session: sessionID ?? "global",
+ error,
+ })
+ const description =
+ session?.title ??
+ (typeof error === "string" ? error : language.t("notification.session.error.fallbackDescription"))
+ const href = sessionID ? `/${base64Encode(directory)}/session/${sessionID}` : `/${base64Encode(directory)}`
+ if (settings.notifications.errors()) {
+ void platform.notify(language.t("notification.session.error.title"), description, href)
+ }
+ })
+ }
+
+ const unsub = serverSDK().event.listen((e) => {
+ const event = e.details
+ if (event.type !== "session.idle" && event.type !== "session.error") return
+
+ const directory = e.name
+ const time = Date.now()
+ if (event.type === "session.idle") {
+ handleSessionIdle(directory, event, time)
+ return
+ }
+ handleSessionError(directory, event, time)
+ })
+ onCleanup(() => {
+ meta.disposed = true
+ unsub()
+ })
+
+ return {
+ ready,
+ session: {
+ all(session: string) {
+ return index.session.all[session] ?? empty
+ },
+ unseen(session: string) {
+ return index.session.unseen[session] ?? empty
+ },
+ unseenCount(session: string) {
+ return index.session.unseenCount[session] ?? 0
+ },
+ unseenHasError(session: string) {
+ return index.session.unseenHasError[session] ?? false
+ },
+ markViewed(session: string) {
+ const unseen = index.session.unseen[session] ?? empty
+ if (!unseen.length) return
+
+ const projects = [
+ ...new Set(unseen.flatMap((notification) => (notification.directory ? [notification.directory] : []))),
+ ]
+ batch(() => {
+ setStore("list", (n) => n.session === session && !n.viewed, "viewed", true)
+ updateUnseen("session", session, [])
+ projects.forEach((directory) => {
+ const next = (index.project.unseen[directory] ?? empty).filter(
+ (notification) => notification.session !== session,
+ )
+ updateUnseen("project", directory, next)
+ })
+ })
+ },
+ },
+ project: {
+ all(directory: string) {
+ return index.project.all[directory] ?? empty
+ },
+ unseen(directory: string) {
+ return index.project.unseen[directory] ?? empty
+ },
+ unseenCount(directory: string) {
+ return index.project.unseenCount[directory] ?? 0
+ },
+ unseenHasError(directory: string) {
+ return index.project.unseenHasError[directory] ?? false
+ },
+ markViewed(directory: string) {
+ const unseen = index.project.unseen[directory] ?? empty
+ if (!unseen.length) return
+
+ const sessions = [
+ ...new Set(unseen.flatMap((notification) => (notification.session ? [notification.session] : []))),
+ ]
+ batch(() => {
+ setStore("list", (n) => n.directory === directory && !n.viewed, "viewed", true)
+ updateUnseen("project", directory, [])
+ sessions.forEach((session) => {
+ const next = (index.session.unseen[session] ?? empty).filter(
+ (notification) => notification.directory !== directory,
+ )
+ updateUnseen("session", session, next)
+ })
+ })
+ },
+ },
+ }
+}
diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx
index 4610608947..8243c07ab9 100644
--- a/packages/app/src/pages/home.tsx
+++ b/packages/app/src/pages/home.tsx
@@ -341,15 +341,15 @@ export function NewHome() {
}
function unseenCount(conn: ServerConnection.Any, project: LocalProject) {
- if (ServerConnection.key(conn) !== server.key) return 0
- return directories(project).reduce((total, directory) => total + notification.project.unseenCount(directory), 0)
+ const state = notification.ensureServerState(ServerConnection.key(conn))
+ return directories(project).reduce((total, directory) => total + state.project.unseenCount(directory), 0)
}
function clearNotifications(conn: ServerConnection.Any, project: LocalProject) {
- if (ServerConnection.key(conn) !== server.key) return
+ const state = notification.ensureServerState(ServerConnection.key(conn))
directories(project)
- .filter((directory) => notification.project.unseenCount(directory) > 0)
- .forEach((directory) => notification.project.markViewed(directory))
+ .filter((directory) => state.project.unseenCount(directory) > 0)
+ .forEach((directory) => state.project.markViewed(directory))
}
function openSession(session: Session) {
diff --git a/packages/app/src/pages/layout-new.tsx b/packages/app/src/pages/layout-new.tsx
index f9222cdeb7..aa9f41890b 100644
--- a/packages/app/src/pages/layout-new.tsx
+++ b/packages/app/src/pages/layout-new.tsx
@@ -1,26 +1,18 @@
import { createEffect, Suspense, type ParentProps } from "solid-js"
-import { useNavigate, useParams } from "@solidjs/router"
+import { useNavigate } from "@solidjs/router"
import { DebugBar } from "@/components/debug-bar"
import { HelpButton } from "@/components/help-button"
import { Titlebar, type TitlebarUpdate } from "@/components/titlebar"
-import { useNotification } from "@/context/notification"
import { usePlatform } from "@/context/platform"
import { setNavigate } from "@/utils/notification-click"
import { setV2Toast, ToastRegion } from "@/utils/toast"
export default function NewLayout(props: ParentProps) {
const platform = usePlatform()
- const notification = useNotification()
const navigate = useNavigate()
- const params = useParams<{ id?: string }>()
setNavigate(navigate)
createEffect(() => setV2Toast(true))
- createEffect(() => {
- if (!notification.ready() || !params.id) return
- if (notification.session.unseenCount(params.id) === 0) return
- notification.session.markViewed(params.id)
- })
const update: TitlebarUpdate = {
version: () => {