feat(ui): add recent section toggle
This commit is contained in:
@@ -58,6 +58,7 @@ import {
|
||||
} from './sidebar/ConfirmDialogs';
|
||||
import { BulkActionBar } from './sidebar/BulkActionBar';
|
||||
import { useSessionMultiSelectStore } from '@/stores/useSessionMultiSelectStore';
|
||||
import { useSessionDisplayStore } from '@/stores/useSessionDisplayStore';
|
||||
import { type SessionGroup, type SessionNode } from './sidebar/types';
|
||||
import {
|
||||
type ActiveNowEntry,
|
||||
@@ -996,19 +997,27 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
return meta;
|
||||
}, [projectSections, homeDirectory]);
|
||||
|
||||
const activeNowSessions = React.useMemo(
|
||||
() => deriveActiveNowSessions(activeNowEntries, new Map(sessions.map((session) => [session.id, session])))
|
||||
.sort((a, b) => compareSessionsByPinnedAndTime(a, b, pinnedSessionIds)),
|
||||
[activeNowEntries, pinnedSessionIds, sessions],
|
||||
);
|
||||
const showRecentSection = useSessionDisplayStore((state) => state.showRecentSection);
|
||||
|
||||
const liveActiveSessions = React.useMemo(
|
||||
() => deriveLiveActiveNowSessions(sessions, liveSessionStatuses),
|
||||
[liveSessionStatuses, sessions],
|
||||
);
|
||||
const activeNowSessions = React.useMemo(() => {
|
||||
if (!showRecentSection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return deriveActiveNowSessions(activeNowEntries, new Map(sessions.map((session) => [session.id, session])))
|
||||
.sort((a, b) => compareSessionsByPinnedAndTime(a, b, pinnedSessionIds));
|
||||
}, [activeNowEntries, pinnedSessionIds, sessions, showRecentSection]);
|
||||
|
||||
const liveActiveSessions = React.useMemo(() => {
|
||||
if (!showRecentSection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return deriveLiveActiveNowSessions(sessions, liveSessionStatuses);
|
||||
}, [liveSessionStatuses, sessions, showRecentSection]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (liveActiveSessions.length === 0) {
|
||||
if (!showRecentSection || liveActiveSessions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1020,9 +1029,13 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
persistActiveNowEntries(safeStorage, next);
|
||||
return next;
|
||||
});
|
||||
}, [liveActiveSessions, safeStorage]);
|
||||
}, [liveActiveSessions, safeStorage, showRecentSection]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!showRecentSection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const allKnownSessionsById = new Map<string, Session>();
|
||||
[...sessions, ...archivedSessions].forEach((session) => {
|
||||
allKnownSessionsById.set(session.id, session);
|
||||
@@ -1035,11 +1048,15 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
|
||||
setActiveNowEntries(pruned);
|
||||
persistActiveNowEntries(safeStorage, pruned);
|
||||
}, [activeNowEntries, archivedSessions, safeStorage, sessions]);
|
||||
}, [activeNowEntries, archivedSessions, safeStorage, sessions, showRecentSection]);
|
||||
|
||||
// Prefetch is wired below, after recentSessionIds is computed.
|
||||
|
||||
const activitySections = React.useMemo(() => {
|
||||
if (!showRecentSection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const toItem = (session: Session) => {
|
||||
const existing = sessionSidebarMetaById.get(session.id);
|
||||
const sessionDirectory = normalizePath((session as Session & { directory?: string | null }).directory ?? null);
|
||||
@@ -1054,11 +1071,11 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
return [
|
||||
{ key: 'active-now' as const, title: t('sessions.sidebar.activity.recentTitle'), items: activeNowSessions.map(toItem) },
|
||||
];
|
||||
}, [activeNowSessions, sessionSidebarMetaById, t]);
|
||||
}, [activeNowSessions, sessionSidebarMetaById, showRecentSection, t]);
|
||||
|
||||
const recentSessionIds = React.useMemo(() => {
|
||||
return new Set(activitySections.flatMap((section) => section.items.map((item) => item.node.session.id)));
|
||||
}, [activitySections]);
|
||||
return new Set(activeNowSessions.map((session) => session.id));
|
||||
}, [activeNowSessions]);
|
||||
|
||||
const recentSessionIdsList = React.useMemo(() => [...recentSessionIds], [recentSessionIds]);
|
||||
|
||||
@@ -1412,7 +1429,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
],
|
||||
);
|
||||
|
||||
const topContent = !hasSessionSearchQuery ? (
|
||||
const topContent = showRecentSection && !hasSessionSearchQuery ? (
|
||||
<SidebarActivitySections
|
||||
sections={activitySections}
|
||||
renderSessionNode={renderSessionNode}
|
||||
|
||||
@@ -78,7 +78,9 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
} = props;
|
||||
|
||||
const displayMode = useSessionDisplayStore((state) => state.displayMode);
|
||||
const showRecentSection = useSessionDisplayStore((state) => state.showRecentSection);
|
||||
const setDisplayMode = useSessionDisplayStore((state) => state.setDisplayMode);
|
||||
const toggleRecentSection = useSessionDisplayStore((state) => state.toggleRecentSection);
|
||||
|
||||
if (hideDirectoryControls) {
|
||||
return null;
|
||||
@@ -240,6 +242,14 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
{displayMode === 'minimal' ? <RiCheckLine className="h-4 w-4 text-primary" /> : null}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={toggleRecentSection}
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<span>{t('sessions.sidebar.header.displayMode.showRecent')}</span>
|
||||
{showRecentSection ? <RiCheckLine className="h-4 w-4 text-primary" /> : null}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={collapseAllProjects} className="flex items-center gap-2">
|
||||
<RiContractUpDownLine className="h-4 w-4" />
|
||||
<span>{t('sessions.sidebar.header.displayMode.collapseAll')}</span>
|
||||
|
||||
@@ -180,6 +180,7 @@ export const dict = {
|
||||
'sessions.sidebar.header.displayMode.label': 'Display mode',
|
||||
'sessions.sidebar.header.displayMode.default': 'Default',
|
||||
'sessions.sidebar.header.displayMode.minimal': 'Minimal',
|
||||
'sessions.sidebar.header.displayMode.showRecent': 'Show recent section',
|
||||
'sessions.sidebar.header.displayMode.collapseAll': 'Collapse all',
|
||||
'sessions.sidebar.header.displayMode.expandAll': 'Expand all',
|
||||
'sessions.sidebar.header.search.matchCountSingle': '{count} match',
|
||||
|
||||
@@ -181,6 +181,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"sessions.sidebar.header.displayMode.label": "Modo de visualización",
|
||||
"sessions.sidebar.header.displayMode.default": "Predeterminado",
|
||||
"sessions.sidebar.header.displayMode.minimal": "Mínimo",
|
||||
"sessions.sidebar.header.displayMode.showRecent": "Mostrar recientes",
|
||||
"sessions.sidebar.header.displayMode.collapseAll": "Colapsar todo",
|
||||
"sessions.sidebar.header.displayMode.expandAll": "Expandir todo",
|
||||
"sessions.sidebar.header.search.matchCountSingle": "{count} coincidencia",
|
||||
|
||||
@@ -181,6 +181,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'sessions.sidebar.header.displayMode.label': '표시 모드',
|
||||
'sessions.sidebar.header.displayMode.default': '기본값',
|
||||
'sessions.sidebar.header.displayMode.minimal': '최소',
|
||||
'sessions.sidebar.header.displayMode.showRecent': '최근 섹션 표시',
|
||||
'sessions.sidebar.header.displayMode.collapseAll': '모두 접기',
|
||||
'sessions.sidebar.header.displayMode.expandAll': '모두 펼치기',
|
||||
'sessions.sidebar.header.search.matchCountSingle': '{count}개 일치',
|
||||
|
||||
@@ -181,6 +181,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"sessions.sidebar.header.displayMode.label": "Modo de visualização",
|
||||
"sessions.sidebar.header.displayMode.default": "Padrão",
|
||||
"sessions.sidebar.header.displayMode.minimal": "Mínimo",
|
||||
"sessions.sidebar.header.displayMode.showRecent": "Mostrar recentes",
|
||||
"sessions.sidebar.header.displayMode.collapseAll": "Recolher tudo",
|
||||
"sessions.sidebar.header.displayMode.expandAll": "Expandir tudo",
|
||||
"sessions.sidebar.header.search.matchCountSingle": "{count} correspondência",
|
||||
|
||||
@@ -181,6 +181,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"sessions.sidebar.header.displayMode.label": "Режим відображення",
|
||||
"sessions.sidebar.header.displayMode.default": "За замовчуванням",
|
||||
"sessions.sidebar.header.displayMode.minimal": "Мінімальний",
|
||||
"sessions.sidebar.header.displayMode.showRecent": "Показувати нещодавні",
|
||||
"sessions.sidebar.header.displayMode.collapseAll": "Згорнути все",
|
||||
"sessions.sidebar.header.displayMode.expandAll": "Розгорнути все",
|
||||
"sessions.sidebar.header.search.matchCountSingle": "{count} збіг",
|
||||
|
||||
@@ -181,6 +181,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'sessions.sidebar.header.displayMode.label': '显示模式',
|
||||
'sessions.sidebar.header.displayMode.default': '默认',
|
||||
'sessions.sidebar.header.displayMode.minimal': '精简',
|
||||
'sessions.sidebar.header.displayMode.showRecent': '显示最近部分',
|
||||
'sessions.sidebar.header.displayMode.collapseAll': '全部折叠',
|
||||
'sessions.sidebar.header.displayMode.expandAll': '全部展开',
|
||||
'sessions.sidebar.header.search.matchCountSingle': '{count} 个匹配',
|
||||
|
||||
@@ -5,14 +5,20 @@ export type SessionDisplayMode = 'default' | 'minimal';
|
||||
|
||||
type SessionDisplayStore = {
|
||||
displayMode: SessionDisplayMode;
|
||||
showRecentSection: boolean;
|
||||
setDisplayMode: (mode: SessionDisplayMode) => void;
|
||||
setShowRecentSection: (show: boolean) => void;
|
||||
toggleRecentSection: () => void;
|
||||
};
|
||||
|
||||
export const useSessionDisplayStore = create<SessionDisplayStore>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
displayMode: 'default',
|
||||
showRecentSection: true,
|
||||
setDisplayMode: (mode) => set({ displayMode: mode }),
|
||||
setShowRecentSection: (show) => set({ showRecentSection: show }),
|
||||
toggleRecentSection: () => set((state) => ({ showRecentSection: !state.showRecentSection })),
|
||||
}),
|
||||
{
|
||||
name: 'session-display-mode',
|
||||
|
||||
Reference in New Issue
Block a user