mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 01:50:39 +09:00
feat(ui): add toggle button for inbox parent-child nesting
Adds a ListTree icon button in the inbox top bar to toggle nesting on/off. Preference is persisted in localStorage. When disabled, all issues display as a flat list without grouping. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
097f30b138
commit
d3e66c789e
2 changed files with 48 additions and 2 deletions
|
|
@ -7,6 +7,7 @@ export const DISMISSED_KEY = "paperclip:inbox:dismissed";
|
|||
export const READ_ITEMS_KEY = "paperclip:inbox:read-items";
|
||||
export const INBOX_LAST_TAB_KEY = "paperclip:inbox:last-tab";
|
||||
export const INBOX_ISSUE_COLUMNS_KEY = "paperclip:inbox:issue-columns";
|
||||
export const INBOX_NESTING_KEY = "paperclip:inbox:nesting";
|
||||
export type InboxTab = "mine" | "recent" | "unread" | "all";
|
||||
export type InboxApprovalFilter = "all" | "actionable" | "resolved";
|
||||
export const inboxIssueColumns = ["status", "id", "assignee", "project", "workspace", "parent", "labels", "updated"] as const;
|
||||
|
|
@ -151,6 +152,23 @@ export function resolveIssueWorkspaceName(
|
|||
return null;
|
||||
}
|
||||
|
||||
export function loadInboxNesting(): boolean {
|
||||
try {
|
||||
const raw = localStorage.getItem(INBOX_NESTING_KEY);
|
||||
return raw !== "false";
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function saveInboxNesting(enabled: boolean) {
|
||||
try {
|
||||
localStorage.setItem(INBOX_NESTING_KEY, String(enabled));
|
||||
} catch {
|
||||
// Ignore localStorage failures.
|
||||
}
|
||||
}
|
||||
|
||||
export function loadLastInboxTab(): InboxTab {
|
||||
try {
|
||||
const raw = localStorage.getItem(INBOX_LAST_TAB_KEY);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ import {
|
|||
RotateCcw,
|
||||
UserPlus,
|
||||
Search,
|
||||
ListTree,
|
||||
} from "lucide-react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PageTabBar } from "../components/PageTabBar";
|
||||
|
|
@ -84,10 +85,12 @@ import {
|
|||
getRecentTouchedIssues,
|
||||
isMineInboxTab,
|
||||
loadInboxIssueColumns,
|
||||
loadInboxNesting,
|
||||
normalizeInboxIssueColumns,
|
||||
resolveIssueWorkspaceName,
|
||||
resolveInboxSelectionIndex,
|
||||
saveInboxIssueColumns,
|
||||
saveInboxNesting,
|
||||
InboxApprovalFilter,
|
||||
type InboxIssueColumn,
|
||||
saveLastInboxTab,
|
||||
|
|
@ -110,6 +113,11 @@ type SectionKey =
|
|||
| "work_items"
|
||||
| "alerts";
|
||||
|
||||
/** A flat navigation entry for keyboard j/k traversal that includes expanded children. */
|
||||
type NavEntry =
|
||||
| { type: "top"; index: number; item: InboxWorkItem }
|
||||
| { type: "child"; parentIndex: number; issue: Issue };
|
||||
|
||||
function firstNonEmptyLine(value: string | null | undefined): string | null {
|
||||
if (!value) return null;
|
||||
const line = value.split("\n").map((chunk) => chunk.trim()).find(Boolean);
|
||||
|
|
@ -903,10 +911,20 @@ export function Inbox() {
|
|||
]);
|
||||
|
||||
// --- Parent-child nesting for inbox issues ---
|
||||
const [nestingEnabled, setNestingEnabled] = useState(() => loadInboxNesting());
|
||||
const toggleNesting = useCallback(() => {
|
||||
setNestingEnabled((prev) => {
|
||||
const next = !prev;
|
||||
saveInboxNesting(next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
const [collapsedInboxParents, setCollapsedInboxParents] = useState<Set<string>>(new Set());
|
||||
const { displayItems: nestedWorkItems, childrenByIssueId } = useMemo(
|
||||
() => buildInboxNesting(filteredWorkItems),
|
||||
[filteredWorkItems],
|
||||
() => nestingEnabled
|
||||
? buildInboxNesting(filteredWorkItems)
|
||||
: { displayItems: filteredWorkItems, childrenByIssueId: new Map<string, Issue[]>() },
|
||||
[filteredWorkItems, nestingEnabled],
|
||||
);
|
||||
const toggleInboxParentCollapse = useCallback((parentId: string) => {
|
||||
setCollapsedInboxParents((prev) => {
|
||||
|
|
@ -1429,6 +1447,16 @@ export function Inbox() {
|
|||
className="h-8 w-[220px] pl-8 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className={cn("hidden h-8 w-8 shrink-0 sm:inline-flex", nestingEnabled && "bg-accent")}
|
||||
onClick={toggleNesting}
|
||||
title={nestingEnabled ? "Disable parent-child nesting" : "Enable parent-child nesting"}
|
||||
>
|
||||
<ListTree className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<IssueColumnPicker
|
||||
availableColumns={availableIssueColumns}
|
||||
visibleColumnSet={visibleIssueColumnSet}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue