Skip to content

Commit d69130b

Browse files
refactor: redux migration part 4
1 parent 96409d8 commit d69130b

File tree

10 files changed

+99
-36
lines changed

10 files changed

+99
-36
lines changed

app/src/electron/csp.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@ export function getCSP() {
55
// unsafe-eval is enabled in development only, otherwise vite will explode!
66
if (is.dev) {
77
return [
8-
"default-src 'self'",
9-
"script-src 'self' 'unsafe-eval' 'unsafe-inline' blob:", // Vite needs eval for HMR
10-
"style-src 'self' 'unsafe-inline'",
11-
"img-src 'self' data: blob: embed: http://localhost:5173",
12-
"font-src 'self' data:",
13-
"connect-src 'self' ws://localhost:5173 http://localhost:5173", // WebSocket for HMR
8+
"default-src 'self' chrome-extension://*",
9+
"script-src 'self' 'unsafe-eval' 'unsafe-inline' blob: chrome-extension://*", // Vite needs eval for HMR
10+
"style-src 'self' 'unsafe-inline' chrome-extension://*",
11+
"img-src 'self' data: blob: embed: http://localhost:5173 chrome-extension://*",
12+
"font-src 'self' data: chrome-extension://*",
13+
"connect-src 'self' ws://localhost:5173 http://localhost:5173 chrome-extension://*", // WebSocket for HMR
1414
"object-src 'none'",
1515
"base-uri 'self'",
1616
"form-action 'self'",
17-
"frame-ancestors 'none'",
1817
].join("; ");
1918
} else {
2019
return [

app/src/electron/init.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222

2323
import installExtension, {
2424
REACT_DEVELOPER_TOOLS,
25+
REDUX_DEVTOOLS,
2526
} from "electron-devtools-installer";
2627
import { setupCsp } from "./csp";
2728

@@ -82,7 +83,8 @@ function setupWindowEvents() {
8283

8384
export async function init() {
8485
if (is.dev) {
85-
await installExtension([REACT_DEVELOPER_TOOLS]);
86+
await installExtension([REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS]);
87+
await installExtension([REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS]);
8688
}
8789
await Paths.initialize();
8890
const migrationsPerformed = await isAlphaMigrationPerformed();
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import { useTranslation } from "react-i18next";
22
import ConstrainedWidth from "../editor/constrained-width";
3-
import RecentNotes from "./recents";
43

54
export default function HomePage() {
65
const { t } = useTranslation();
76
return (
87
<div className="flex items-center flex-col px-24 editor-fade-in min-h-full relative p-12">
98
<ConstrainedWidth className="flex flex-col gap-3">
109
<h1 className="text-3xl font-semibold">{t("home.welcome")}</h1>
11-
<RecentNotes />
1210
</ConstrainedWidth>
1311
</div>
14-
)
12+
);
1513
}
16-
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {
2+
Collapsible,
3+
CollapsibleContent,
4+
CollapsibleTrigger,
5+
} from "@/components/ui";
6+
import { memo, ReactNode, useState } from "react";
7+
import { useNoteItem } from "../hooks/use-note-item";
8+
import { cn, getNoteIcon } from "@/lib/utils";
9+
import { ChevronRight, Plus } from "lucide-react";
10+
11+
export function NoteListItem({
12+
id,
13+
children,
14+
}: {
15+
id: string;
16+
children: ReactNode[] | ReactNode;
17+
}) {
18+
return (
19+
<>
20+
<NoteItem id={id}>{children}</NoteItem>
21+
<NoteDropZone id={id} />
22+
</>
23+
);
24+
}
25+
26+
function NoteItem({
27+
id,
28+
children,
29+
}: {
30+
id: string;
31+
children: ReactNode[] | ReactNode;
32+
}) {
33+
const [open, setOpen] = useState(false);
34+
const note = useNoteItem(id);
35+
36+
if (!note) return null;
37+
38+
return (
39+
<Collapsible open={open} onOpenChange={setOpen}>
40+
<div className="group flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground">
41+
<CollapsibleTrigger asChild>
42+
<button className="flex items-center gap-1 rounded-sm p-0.5 hover:bg-muted/50">
43+
<ChevronRight
44+
className={cn(
45+
"size-4 transition-transform duration-200",
46+
open && "rotate-90",
47+
)}
48+
/>
49+
{getNoteIcon(note.icon, "size-4")}
50+
</button>
51+
</CollapsibleTrigger>
52+
<span className="flex-1 truncate text-left select-none">
53+
{note.title || "Untitled"}
54+
</span>
55+
<button className="opacity-0 transition-opacity hover:bg-muted/50 group-hover:opacity-100 rounded-sm p-0.5">
56+
<Plus className="size-4" />
57+
</button>
58+
</div>
59+
<CollapsibleContent className="pl-2">{children}</CollapsibleContent>
60+
</Collapsible>
61+
);
62+
}
63+
64+
const NoteDropZone = memo(function ({ id }: { id: string }) {
65+
return <div className="h-1 bg-primary/20"></div>;
66+
});

app/src/features/note/components/note-list.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { cn } from "@/lib/utils";
22
import { useNoteListState } from "../hooks/use-note-list-state";
3+
import { NoteListItem } from "./note-list-item";
34

45
export interface NoteListProps {
56
className?: string;
@@ -10,9 +11,11 @@ export default function NoteList(props: NoteListProps) {
1011
const state = useNoteListState(props.parentId);
1112

1213
return (
13-
<div className={cn("flex flex-col gap-2", props.className)}>
14+
<div className={cn("flex flex-col", props.className)}>
1415
{state.noteIds.map((id) => (
15-
<div>{id}</div>
16+
<NoteListItem id={id} key={id}>
17+
<NoteList parentId={id} />
18+
</NoteListItem>
1619
))}
1720
</div>
1821
);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useAppSelector } from "@/features/store/hooks";
2+
import { selectNoteById } from "../store/note-selectors";
3+
4+
/**
5+
* Hook to get note data **within sidebar views.** Do NOT use this to
6+
* render arbitrary items. This expects the note to exist within cache.
7+
* @param id
8+
*/
9+
export function useNoteItem(id: string) {
10+
const note = useAppSelector((state) => selectNoteById(state, id));
11+
12+
if (!note) return null;
13+
return note;
14+
}

app/src/features/note/note-list-root.tsx

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,10 @@ import { ChevronRight } from "lucide-react";
1010
import { useState } from "react";
1111
import { useTranslation } from "react-i18next";
1212
import NoteList from "./components/note-list";
13-
import { useNoteChildren } from "./use-note-children";
1413

1514
export default function NoteListRoot() {
1615
const { t } = useTranslation("translation", { keyPrefix: "sidebar" });
1716
const [open, setOpen] = useState(false);
18-
const { data } = useNoteChildren(null);
19-
const rootNotes = data;
20-
21-
if (rootNotes == null || rootNotes.length === 0) {
22-
return (
23-
<div>
24-
<span>{t("notes.noPages")}</span>
25-
</div>
26-
);
27-
}
28-
29-
const leadingHint = new Rank(rootNotes[0].orderHint).prev().toString();
30-
const finalHint = new Rank(rootNotes[rootNotes.length - 1].orderHint)
31-
.next()
32-
.toString();
3317

3418
return (
3519
<Collapsible open={open} onOpenChange={setOpen}>

app/src/features/note/store/note-selectors.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { RootState } from "@/features/store/redux";
22
import { notesAdapter } from "./notes-adapter";
33
import { createSelector } from "@reduxjs/toolkit";
4+
import { Rank } from "@/common/rank";
45

56
const selectNotesState = (store: RootState) => store["notes-slice"];
67

@@ -10,8 +11,7 @@ export const { selectAll: selectAllNotes, selectById: selectNoteById } =
1011
export const selectNotesByParentId = createSelector(
1112
[
1213
selectAllNotes,
13-
(_state: RootState, workspaceId: string, _parentId: string | null) =>
14-
workspaceId,
14+
(_state: RootState, workspaceId: string) => workspaceId,
1515
(_state: RootState, _workspaceId: string, parentId: string | null) =>
1616
parentId,
1717
],
@@ -21,6 +21,7 @@ export const selectNotesByParentId = createSelector(
2121
(note) =>
2222
note.workspaceId === workspaceId && note.parentId === parentId,
2323
)
24+
.toSorted((a, b) => Rank.sorter(a.orderHint, b.orderHint))
2425
.map((n) => n.id);
2526
},
2627
);

app/src/features/search/search-dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import {
88
} from "@/components/ui";
99
import { useNavigateToNote } from "@/hooks/use-navigate-to-note";
1010
import { getNoteIcon } from "@/lib/utils";
11-
import { useNotes } from "@/query/use-notes";
1211
import { useCallback, useRef } from "react";
1312
import { useTranslation } from "react-i18next";
1413
import { setSearchOpen, setSearchQuery, useSearchState } from "./search-state";
1514

1615
export default function SearchDialog() {
16+
return <></>;
1717
const open = useSearchState((s) => s.open);
1818
const query = useSearchState((s) => s.query);
1919
const { t } = useTranslation();

app/src/hooks/use-shortcuts.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { toggleSidebar } from "@/context/local-state";
22
import { showSearch } from "@/features/search/search-state";
3-
import { useCreateNoteMutation } from "@/query/use-create-note";
43
import { useEffect } from "react";
54

65
export const useShortcuts = () => {
7-
const createNew = useCreateNoteMutation(true);
86
useEffect(() => {
97
const down = (e: KeyboardEvent) => {
108
const cmd = (key: string) => {
@@ -15,7 +13,6 @@ export const useShortcuts = () => {
1513
};
1614
if (cmd("n")) {
1715
e.preventDefault();
18-
createNew.create({});
1916
} else if (cmd("k")) {
2017
showSearch();
2118
} else if (alt("b")) {

0 commit comments

Comments
 (0)