Skip to content

Commit 5768830

Browse files
authored
fix: selecting migrated notes
* fix: selecting migrated notes * perf: reduce re-rendering of notes when activating one * chore: apply tiny fixes * fix: name of handler
1 parent 57b100b commit 5768830

File tree

12 files changed

+174
-92
lines changed

12 files changed

+174
-92
lines changed

src/App.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { CodeSyncProvider } from "./components/CodeSyncProvider/CodeSyncProvider
66
import { DownloadPdfWithTheme } from "./components/DownloadThemedPdf/DownloadThemedPdf";
77
import { Header } from "./components/Header/Header";
88
import { LightThemeToggle } from "./components/LightThemeToggle/LightThemeToggle";
9-
import { type ILocationContext, LocationContext } from "./components/LocationProvider/LocationProvider";
9+
import { useVersionContext } from "./components/LocationProvider/VersionProvider";
1010
import { type IMetadataContext, MetadataContext } from "./components/MetadataProvider/MetadataProvider";
1111
import { NotesProvider } from "./components/NotesProvider/NotesProvider";
1212
import { PdfProvider } from "./components/PdfProvider/PdfProvider";
@@ -17,9 +17,7 @@ import { Sidebar } from "./components/Sidebar/Sidebar";
1717
import { ZoomControls } from "./components/ZoomControls/ZoomControls";
1818

1919
export function App() {
20-
const {
21-
locationParams: { version },
22-
} = useContext(LocationContext) as ILocationContext;
20+
const { version } = useVersionContext();
2321

2422
const { urlGetters } = useContext(MetadataContext) as IMetadataContext;
2523

src/components/CodeSyncProvider/CodeSyncProvider.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { migrateSelection as migrateSelectionRaw } from "@fluffylabs/links-metadata";
22
import type { ISelectionParams, ISynctexBlock, ISynctexBlockId, ISynctexData } from "@fluffylabs/links-metadata";
3-
import { createContext, useCallback, useContext, useEffect, useState } from "react";
3+
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
44
import type { PropsWithChildren } from "react";
55
import { isFeatureEnabled } from "../../devtools";
66
import { type ILocationContext, LocationContext } from "../LocationProvider/LocationProvider";
@@ -196,14 +196,24 @@ export function CodeSyncProvider({ children }: PropsWithChildren) {
196196
[synctexStore, texStore],
197197
);
198198

199-
const context = {
200-
getSynctexBlockAtLocation,
201-
getSynctexBlockById,
202-
getSynctexBlockRange,
203-
getSectionTitleAtSynctexBlock,
204-
getSubsectionTitleAtSynctexBlock,
205-
migrateSelection,
206-
};
199+
const context = useMemo(
200+
() => ({
201+
getSynctexBlockAtLocation,
202+
getSynctexBlockById,
203+
getSynctexBlockRange,
204+
getSectionTitleAtSynctexBlock,
205+
getSubsectionTitleAtSynctexBlock,
206+
migrateSelection,
207+
}),
208+
[
209+
getSynctexBlockAtLocation,
210+
getSynctexBlockById,
211+
getSynctexBlockRange,
212+
getSectionTitleAtSynctexBlock,
213+
getSubsectionTitleAtSynctexBlock,
214+
migrateSelection,
215+
],
216+
);
207217

208218
return <CodeSyncContext.Provider value={context}>{children}</CodeSyncContext.Provider>;
209219
}

src/components/LocationProvider/LocationProvider.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type ISelectionParams, type ISynctexBlock, isSameBlock } from "@fluffyl
22
import { type ReactNode, createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
33
import { deserializeLegacyLocation } from "../../utils/deserializeLegacyLocation";
44
import { type IMetadataContext, MetadataContext } from "../MetadataProvider/MetadataProvider";
5+
import { useGetLocationParamsToHash } from "./hooks/useGetLocationParamsToHash";
56
import type { ILocationParams, SearchParams } from "./types";
67
import {
78
BASE64_VALIDATION_REGEX,
@@ -57,13 +58,7 @@ export function LocationProvider({ children }: ILocationProviderProps) {
5758
[metadata],
5859
);
5960

60-
const getHashFromLocationParams = useCallback(
61-
(params: ILocationParams) => {
62-
const hash = locationParamsToHash(params, metadata);
63-
return hash;
64-
},
65-
[metadata],
66-
);
61+
const { getHashFromLocationParams } = useGetLocationParamsToHash();
6762

6863
const handleHashChange = useCallback(() => {
6964
const { rest: newHash, search, section } = extractSearchParams(window.location.hash);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createContext, useContext, useMemo } from "react";
2+
import { useLocationContext } from "./LocationProvider";
3+
4+
const versionContext = createContext<{ version: string } | undefined>(undefined);
5+
6+
export const useVersionContext = () => {
7+
const context = useContext(versionContext);
8+
9+
if (!context) {
10+
throw new Error("useVersionContext must be used within a VersionProvider");
11+
}
12+
13+
return context;
14+
};
15+
16+
export const VersionProvider = ({ children }: { children: React.ReactNode }) => {
17+
const locationContext = useLocationContext();
18+
19+
const context = useMemo(
20+
() => ({
21+
version: locationContext.locationParams.version,
22+
}),
23+
[locationContext.locationParams.version],
24+
);
25+
26+
return <versionContext.Provider value={context}>{children}</versionContext.Provider>;
27+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useCallback } from "react";
2+
import { useMetadataContext } from "../../MetadataProvider/MetadataProvider";
3+
import type { ILocationParams } from "../types";
4+
import { locationParamsToHash } from "../utils/locationParamsToHash";
5+
6+
export const useGetLocationParamsToHash = () => {
7+
const metaDataContext = useMetadataContext();
8+
const { metadata } = metaDataContext;
9+
const getHashFromLocationParams = useCallback(
10+
(params: ILocationParams) => {
11+
const hash = locationParamsToHash(params, metadata);
12+
return hash;
13+
},
14+
[metadata],
15+
);
16+
17+
return { getHashFromLocationParams };
18+
};

src/components/MetadataProvider/MetadataProvider.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type ReactNode, createContext, useEffect, useMemo, useState } from "react";
1+
import { type ReactNode, createContext, useContext, useEffect, useMemo, useState } from "react";
22

33
const METADATA_HOST = "";
44
const METADATA_JSON = `${METADATA_HOST}/metadata.json`;
@@ -34,6 +34,14 @@ interface IMetadataProviderProps {
3434

3535
export const MetadataContext = createContext<IMetadataContext | null>(null);
3636

37+
export const useMetadataContext = () => {
38+
const context = useContext(MetadataContext);
39+
if (!context) {
40+
throw new Error("useMetadataContext must be used within a MetadataProvider");
41+
}
42+
return context;
43+
};
44+
3745
export function MetadataProvider({ children }: IMetadataProviderProps) {
3846
const [metadata, setMetadata] = useState<IMetadata>();
3947

src/components/NoteManager/NoteManager.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { memo, useCallback, useContext, useEffect, useState } from "react";
1+
import { memo, useCallback, useContext, useEffect, useRef, useState } from "react";
22
import "./NoteManager.css";
33
import { Button, Textarea } from "@fluffylabs/shared-ui";
44
import { twMerge } from "tailwind-merge";
55
import { validateMath } from "../../utils/validateMath";
66
import { type ILocationContext, LocationContext } from "../LocationProvider/LocationProvider";
77
import { type INotesContext, NotesContext } from "../NotesProvider/NotesProvider";
88
import { LABEL_LOCAL } from "../NotesProvider/consts/labels";
9+
import type { IDecoratedNote } from "../NotesProvider/types/DecoratedNote";
910
import type { IStorageNote } from "../NotesProvider/types/StorageNote";
1011
import { Selection } from "../Selection/Selection";
1112
import { type ISelectionContext, SelectionContext } from "../SelectionProvider/SelectionProvider";
@@ -27,7 +28,7 @@ const MemoizedNotesList = memo(NotesList);
2728
function Notes() {
2829
const [noteContent, setNoteContent] = useState("");
2930
const [noteContentError, setNoteContentError] = useState("");
30-
const { locationParams } = useContext(LocationContext) as ILocationContext;
31+
const { locationParams, setLocationParams } = useContext(LocationContext) as ILocationContext;
3132
const { notesReady, activeNotes, notes, handleAddNote, handleDeleteNote, handleUpdateNote } = useContext(
3233
NotesContext,
3334
) as INotesContext;
@@ -67,6 +68,17 @@ function Notes() {
6768
handleClearSelection();
6869
}, [noteContent, pageNumber, selectedBlocks, handleAddNote, handleClearSelection, locationParams]);
6970

71+
const locationRef = useRef({ locationParams, setLocationParams });
72+
locationRef.current = { locationParams, setLocationParams };
73+
74+
const handleSelectNote = useCallback((note: IDecoratedNote) => {
75+
locationRef.current.setLocationParams({
76+
selectionStart: note.current.selectionStart,
77+
selectionEnd: note.current.selectionEnd,
78+
version: locationRef.current.locationParams.version,
79+
});
80+
}, []);
81+
7082
useEffect(() => {
7183
if (selectedBlocks.length === 0) {
7284
setNoteContent("");
@@ -97,6 +109,7 @@ function Notes() {
97109
notes={notes}
98110
onEditNote={handleUpdateNote}
99111
onDeleteNote={handleDeleteNote}
112+
onSelectNote={handleSelectNote}
100113
/>
101114
</div>
102115
);

src/components/NoteManager/components/Note.tsx

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Button, cn } from "@fluffylabs/shared-ui";
22
import { type ChangeEvent, useCallback, useEffect, useMemo, useState } from "react";
33
import { validateMath } from "../../../utils/validateMath";
4-
import { useLocationContext } from "../../LocationProvider/LocationProvider";
54
import type { INotesContext } from "../../NotesProvider/NotesProvider";
65
import { type IDecoratedNote, NoteSource } from "../../NotesProvider/types/DecoratedNote";
76
import type { IStorageNote } from "../../NotesProvider/types/StorageNote";
@@ -19,17 +18,16 @@ type NoteProps = {
1918
active: boolean;
2019
onEditNote: INotesContext["handleUpdateNote"];
2120
onDeleteNote: INotesContext["handleDeleteNote"];
21+
onSelectNote: (note: IDecoratedNote) => void;
2222
};
2323

24-
export function Note({ note, active = false, onEditNote, onDeleteNote }: NoteProps) {
24+
export function Note({ note, active = false, onEditNote, onDeleteNote, onSelectNote }: NoteProps) {
2525
const [isEditing, setIsEditing] = useState(false);
2626
const [noteDirty, setNoteDirty] = useState<IStorageNote>({
2727
...note.original,
2828
});
2929
const [noteContentError, setNoteContentError] = useState("");
3030

31-
const { setLocationParams, locationParams } = useLocationContext();
32-
3331
const isEditable = note.source !== NoteSource.Remote;
3432

3533
const handleSaveClick = useCallback(() => {
@@ -83,11 +81,7 @@ export function Note({ note, active = false, onEditNote, onDeleteNote }: NotePro
8381
return;
8482
}
8583

86-
setLocationParams({
87-
version: locationParams.version,
88-
selectionStart: note.original.selectionStart,
89-
selectionEnd: note.original.selectionEnd,
90-
});
84+
onSelectNote(note);
9185
};
9286

9387
const handleNoteEnter = (e: React.KeyboardEvent<HTMLDivElement>) => {
@@ -104,11 +98,7 @@ export function Note({ note, active = false, onEditNote, onDeleteNote }: NotePro
10498
return;
10599
}
106100

107-
setLocationParams({
108-
version: note.original.version,
109-
selectionStart: note.original.selectionStart,
110-
selectionEnd: note.original.selectionEnd,
111-
});
101+
onSelectNote(note);
112102
};
113103

114104
useEffect(() => {
@@ -161,7 +151,7 @@ export function Note({ note, active = false, onEditNote, onDeleteNote }: NotePro
161151
>
162152
{!active && (
163153
<>
164-
<NoteLink note={note} onEditNote={onEditNote} />
154+
<NoteLink note={note} active={false} />
165155
<NoteLayout.Text />
166156
</>
167157
)}

src/components/NoteManager/components/NoteLayout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ export const NoteText = () => {
2020

2121
export const SelectedText = () => {
2222
const { selectionString } = useContext(SelectionContext) as ISelectionContext;
23-
const { note, onEditNote } = useNoteContext();
23+
const { note } = useNoteContext();
2424

2525
return (
2626
<div className="px-6 py-3 bg-sidebar rounded-md border-brand-primary border flex flex-col gap-1">
2727
<div className="flex justify-between gap-1">
28-
<NoteLink note={note} onEditNote={onEditNote} />
28+
<NoteLink note={note} active={true} />
2929
</div>
3030
<blockquote className="italic" data-testid="selected-text">
3131
{selectionString}

0 commit comments

Comments
 (0)