diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 95f8c01843fd..98548ab2333b 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -972,17 +972,6 @@ export default function LegacyLayout(props: ParentProps) { keybind: "shift+alt+arrowdown", onSelect: () => navigateSessionByUnseen(1), }, - { - id: "session.archive", - title: language.t("command.session.archive"), - category: language.t("command.category.session"), - keybind: "mod+shift+backspace", - disabled: !params.dir || !params.id, - onSelect: () => { - const session = currentSessions().find((s) => s.id === params.id) - if (session) void archiveSession(session) - }, - }, { id: "workspace.new", title: language.t("workspace.new"), diff --git a/packages/app/src/pages/session/session-archive.ts b/packages/app/src/pages/session/session-archive.ts new file mode 100644 index 000000000000..5e1314dbd14d --- /dev/null +++ b/packages/app/src/pages/session/session-archive.ts @@ -0,0 +1,71 @@ +import { useNavigate } from "@solidjs/router" +import { produce } from "solid-js/store" +import { notifySessionTabsRemoved } from "@/components/titlebar-session-events" +import { useLanguage } from "@/context/language" +import { useSDK } from "@/context/sdk" +import { useSync } from "@/context/sync" +import { useTabs } from "@/context/tabs" +import { errorMessage } from "@/pages/layout/helpers" +import { useSessionKey } from "@/pages/session/session-layout" +import { legacySessionHref, requireServerKey, sessionHref } from "@/utils/session-route" +import { showToast } from "@/utils/toast" + +export function useSessionArchive() { + const language = useLanguage() + const navigate = useNavigate() + const sdk = useSDK() + const sync = useSync() + const tabs = useTabs() + const { params } = useSessionKey() + + const navigateAfterRemoval = (sessionID: string, parentID?: string, nextSessionID?: string) => { + if (params.id !== sessionID) return + const href = (id: string) => + params.serverKey ? sessionHref(requireServerKey(params.serverKey), id) : legacySessionHref(sdk().directory, id) + if (parentID) { + navigate(href(parentID)) + return + } + if (nextSessionID) { + navigate(href(nextSessionID)) + return + } + if (params.serverKey) { + tabs.newDraft({ server: requireServerKey(params.serverKey), directory: sdk().directory }) + return + } + navigate(`/${params.dir}/session`) + } + + const archive = async (sessionID: string) => { + const session = sync().session.get(sessionID) + if (!session) return + if ((await sdk().protocol) !== "v1") return + + const sessions = sync().data.session ?? [] + const index = sessions.findIndex((s) => s.id === sessionID) + const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1]) + + await sdk() + .client.session.update({ sessionID, directory: sdk().directory, time: { archived: Date.now() } }) + .then(() => { + sync().set( + produce((draft) => { + const index = draft.session.findIndex((s) => s.id === sessionID) + if (index !== -1) draft.session.splice(index, 1) + }), + ) + sync().session.evict(sessionID) + navigateAfterRemoval(sessionID, session.parentID, nextSession?.id) + notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] }) + }) + .catch((err) => { + showToast({ + title: language.t("common.requestFailed"), + description: errorMessage(err, language.t("common.requestFailed")), + }) + }) + } + + return { archive, navigateAfterRemoval } +} diff --git a/packages/app/src/pages/session/timeline/message-timeline.tsx b/packages/app/src/pages/session/timeline/message-timeline.tsx index 25abdc9e7341..18a153d29971 100644 --- a/packages/app/src/pages/session/timeline/message-timeline.tsx +++ b/packages/app/src/pages/session/timeline/message-timeline.tsx @@ -63,10 +63,10 @@ import { SessionContextUsage } from "@/components/session-context-usage" import { useDialog } from "@opencode-ai/ui/context/dialog" import { useLanguage } from "@/context/language" import { useSessionKey } from "@/pages/session/session-layout" +import { useSessionArchive } from "@/pages/session/session-archive" import { useServerSDK } from "@/context/server-sdk" import { usePlatform } from "@/context/platform" import { useSettings } from "@/context/settings" -import { useTabs } from "@/context/tabs" import { legacySessionHref, requireServerKey, sessionHref } from "@/utils/session-route" import { useSDK } from "@/context/sdk" import { useSync } from "@/context/sync" @@ -263,8 +263,8 @@ export function MessageTimeline(props: { const sdk = useSDK() const sync = useSync() const settings = useSettings() - const tabs = useTabs() const dialog = useDialog() + const sessionArchive = useSessionArchive() const language = useLanguage() const { params, sessionKey } = useSessionKey() const ownerSessionKey = sessionKey() @@ -790,25 +790,6 @@ export function MessageTimeline(props: { titleMutation.mutate({ id, title: next }) } - const navigateAfterSessionRemoval = (sessionID: string, parentID?: string, nextSessionID?: string) => { - if (params.id !== sessionID) return - const href = (id: string) => - params.serverKey ? sessionHref(requireServerKey(params.serverKey), id) : legacySessionHref(sdk().directory, id) - if (parentID) { - navigate(href(parentID)) - return - } - if (nextSessionID) { - navigate(href(nextSessionID)) - return - } - if (params.serverKey) { - tabs.newDraft({ server: requireServerKey(params.serverKey), directory: sdk().directory }) - return - } - navigate(`/${params.dir}/session`) - } - const exportSession = async (sessionID: string) => { try { const data = await fetchSessionExport({ @@ -832,36 +813,6 @@ export function MessageTimeline(props: { } } - const archiveSession = async (sessionID: string) => { - const session = sync().session.get(sessionID) - if (!session) return - if ((await sdk().protocol) !== "v1") return - - const sessions = sync().data.session ?? [] - const index = sessions.findIndex((s) => s.id === sessionID) - const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1]) - - await sdk() - .client.session.update({ sessionID, directory: sdk().directory, time: { archived: Date.now() } }) - .then(() => { - sync().set( - produce((draft) => { - const index = draft.session.findIndex((s) => s.id === sessionID) - if (index !== -1) draft.session.splice(index, 1) - }), - ) - sync().session.evict(sessionID) - navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id) - notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] }) - }) - .catch((err) => { - showToast({ - title: language.t("common.requestFailed"), - description: errorMessage(err), - }) - }) - } - const deleteSession = async (sessionID: string) => { const session = sync().session.get(sessionID) if (!session) return false @@ -911,7 +862,7 @@ export function MessageTimeline(props: { } } - navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id) + sessionArchive.navigateAfterRemoval(sessionID, session.parentID, nextSession?.id) sync().set( produce((draft) => { @@ -1593,7 +1544,7 @@ export function MessageTimeline(props: { exportSession(id)}> {language.t("common.export")} - void archiveSession(id)}> + void sessionArchive.archive(id)}> {language.t("common.archive")} @@ -1667,7 +1618,7 @@ export function MessageTimeline(props: { exportSession(id)}> {language.t("common.export")}... - void archiveSession(id)}> + void sessionArchive.archive(id)}> {language.t("common.archive")} diff --git a/packages/app/src/pages/session/use-session-commands.tsx b/packages/app/src/pages/session/use-session-commands.tsx index c4c8c44a99f0..18d5b172ff17 100644 --- a/packages/app/src/pages/session/use-session-commands.tsx +++ b/packages/app/src/pages/session/use-session-commands.tsx @@ -18,6 +18,7 @@ import { createSessionTabs } from "@/pages/session/helpers" import { extractPromptFromParts } from "@/utils/prompt" import { Message, Part, UserMessage } from "@opencode-ai/sdk/v2" import { useSessionLayout } from "@/pages/session/session-layout" +import { useSessionArchive } from "@/pages/session/session-archive" import { createSessionOwnership } from "./session-ownership" import { useLocal } from "@/context/local" @@ -52,6 +53,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => { const navigate = useNavigate() const { params, sessionKey, tabs, view } = useSessionLayout() const sessionOwnership = createSessionOwnership(sessionKey) + const sessionArchive = useSessionArchive() const openDialog = async (load: () => Promise, show: (value: T) => void) => { const owner = sessionOwnership.capture() const value = await load() @@ -497,6 +499,16 @@ export const useSessionCommands = (actions: SessionCommandContext) => { disabled: !params.id, onSelect: exportSession, }), + sessionCommand({ + id: "session.archive", + title: language.t("command.session.archive"), + keybind: "mod+shift+backspace", + disabled: !params.id, + onSelect: () => { + const id = params.id + if (id) void sessionArchive.archive(id) + }, + }), ] const fileCmds = () => {