mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-23 19:49:56 +01:00
web: fix previewing note history where only title has changed
This commit is contained in:
@@ -17,13 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { useCallback, useEffect, useLayoutEffect, useState } from "react";
|
||||
import { Flex, Text, Button } from "@theme-ui/components";
|
||||
import { Copy, Restore } from "../icons";
|
||||
import ContentToggle from "./content-toggle";
|
||||
@@ -59,6 +53,8 @@ function DiffViewer(props: DiffViewerProps) {
|
||||
|
||||
const onResolveContent = useCallback(
|
||||
(saveCopy: boolean) => {
|
||||
if (!conflictedContent) return;
|
||||
|
||||
const toKeep =
|
||||
selectedContent === 1 ? session.content?.conflicted : session.content;
|
||||
const toCopy =
|
||||
@@ -69,7 +65,7 @@ function DiffViewer(props: DiffViewerProps) {
|
||||
toKeep,
|
||||
toCopy: saveCopy ? toCopy : undefined,
|
||||
toKeepDateEdited: toKeep.dateEdited,
|
||||
dateResolved: conflictedContent!.dateModified
|
||||
dateResolved: conflictedContent.dateModified
|
||||
});
|
||||
},
|
||||
[conflictedContent, selectedContent, session.content, session.note]
|
||||
@@ -82,8 +78,6 @@ function DiffViewer(props: DiffViewerProps) {
|
||||
});
|
||||
}, [session]);
|
||||
|
||||
if (!conflictedContent || !content) return null;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
ref={(el) => {
|
||||
@@ -110,8 +104,8 @@ function DiffViewer(props: DiffViewerProps) {
|
||||
}}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
session.type === "diff"
|
||||
? diff(session.oldContentTitle || "", session.note.title)
|
||||
session.type === "diff" && session.oldTitle
|
||||
? diff(session.oldTitle || "", session.note.title)
|
||||
: session.note.title
|
||||
}}
|
||||
></Text>
|
||||
@@ -145,28 +139,34 @@ function DiffViewer(props: DiffViewerProps) {
|
||||
<Restore size={18} />
|
||||
<Text ml={1}>{strings.restoreThisVersion()}</Text>
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={async () => {
|
||||
const { closeTabs, openSession } = useEditorStore.getState();
|
||||
{!content || !conflictedContent ? null : (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={async () => {
|
||||
const { closeTabs, openSession } = useEditorStore.getState();
|
||||
|
||||
const noteId = await createCopy(session.note, content);
|
||||
const noteId = await createCopy(
|
||||
session.note,
|
||||
content,
|
||||
session.oldTitle
|
||||
);
|
||||
|
||||
closeTabs(session.id);
|
||||
closeTabs(session.id);
|
||||
|
||||
await notesStore.refresh();
|
||||
await openSession(noteId);
|
||||
}}
|
||||
mr={2}
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
display: "flex"
|
||||
}}
|
||||
>
|
||||
<Copy size={18} />
|
||||
<Text ml={1}>{strings.saveACopy()}</Text>
|
||||
</Button>
|
||||
await notesStore.refresh();
|
||||
await openSession(noteId);
|
||||
}}
|
||||
mr={2}
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
display: "flex"
|
||||
}}
|
||||
>
|
||||
<Copy size={18} />
|
||||
<Text ml={1}>{strings.saveACopy()}</Text>
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="errorSecondary"
|
||||
onClick={() => {
|
||||
@@ -179,165 +179,183 @@ function DiffViewer(props: DiffViewerProps) {
|
||||
</>
|
||||
) : null}
|
||||
</Flex>
|
||||
<ScrollSync>
|
||||
<Flex
|
||||
{!conflictedContent || !content ? (
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{
|
||||
flex: "1 1 auto",
|
||||
flexDirection: ["column", "column", "row"],
|
||||
overflow: "hidden"
|
||||
px: 2,
|
||||
py: 1,
|
||||
my: 1,
|
||||
borderTop: "1px solid var(--border)"
|
||||
}}
|
||||
>
|
||||
{strings.noContent()}
|
||||
</Text>
|
||||
) : (
|
||||
<ScrollSync>
|
||||
<Flex
|
||||
className="firstEditor"
|
||||
data-test-id="first-editor"
|
||||
sx={{
|
||||
flex: "1 1 auto",
|
||||
flexDirection: "column",
|
||||
width: ["100%", "100%", "50%"],
|
||||
height: ["50%", "50%", "100%"]
|
||||
flexDirection: ["column", "column", "row"],
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
{content.locked ? (
|
||||
<UnlockView
|
||||
title={getFormattedDate(content.dateEdited)}
|
||||
subtitle={strings.enterPasswordToUnlockVersion()}
|
||||
buttonTitle={strings.unlock()}
|
||||
unlock={async (password) => {
|
||||
const decryptedContent = await db.vault.decryptContent(
|
||||
content,
|
||||
password
|
||||
);
|
||||
setContent({
|
||||
...content,
|
||||
...decryptedContent,
|
||||
locked: false
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<ContentToggle
|
||||
label={
|
||||
session.type === "diff"
|
||||
? strings.olderVersion()
|
||||
: strings.currentNote()
|
||||
}
|
||||
readonly={session.type === "diff"}
|
||||
dateEdited={content.dateEdited}
|
||||
isSelected={selectedContent === 0}
|
||||
isOtherSelected={selectedContent === 1}
|
||||
onToggle={() => setSelectedContent((s) => (s === 0 ? -1 : 0))}
|
||||
resolveConflict={onResolveContent}
|
||||
sx={{
|
||||
borderStyle: "solid",
|
||||
borderWidth: 0,
|
||||
borderBottomWidth: 1,
|
||||
borderColor: "border",
|
||||
px: 2,
|
||||
pb: 1
|
||||
<Flex
|
||||
className="firstEditor"
|
||||
data-test-id="first-editor"
|
||||
sx={{
|
||||
flex: "1 1 auto",
|
||||
flexDirection: "column",
|
||||
width: ["100%", "100%", "50%"],
|
||||
height: ["50%", "50%", "100%"]
|
||||
}}
|
||||
>
|
||||
{content.locked ? (
|
||||
<UnlockView
|
||||
title={getFormattedDate(content.dateEdited)}
|
||||
subtitle={strings.enterPasswordToUnlockVersion()}
|
||||
buttonTitle={strings.unlock()}
|
||||
unlock={async (password) => {
|
||||
const decryptedContent = await db.vault.decryptContent(
|
||||
content,
|
||||
password
|
||||
);
|
||||
setContent({
|
||||
...content,
|
||||
...decryptedContent,
|
||||
locked: false
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<ScrollSyncPane>
|
||||
<Flex
|
||||
) : (
|
||||
<>
|
||||
<ContentToggle
|
||||
label={
|
||||
session.type === "diff"
|
||||
? strings.olderVersion()
|
||||
: strings.currentNote()
|
||||
}
|
||||
readonly={session.type === "diff"}
|
||||
dateEdited={content.dateEdited}
|
||||
isSelected={selectedContent === 0}
|
||||
isOtherSelected={selectedContent === 1}
|
||||
onToggle={() =>
|
||||
setSelectedContent((s) => (s === 0 ? -1 : 0))
|
||||
}
|
||||
resolveConflict={onResolveContent}
|
||||
sx={{
|
||||
px: 2,
|
||||
overflowY: "auto",
|
||||
flex: 1,
|
||||
borderStyle: "solid",
|
||||
borderWidth: 0,
|
||||
borderRightWidth: [0, 0, 1],
|
||||
borderBottomWidth: [1, 1, 0],
|
||||
borderColor: "border"
|
||||
borderBottomWidth: 1,
|
||||
borderColor: "border",
|
||||
px: 2,
|
||||
pb: 1
|
||||
}}
|
||||
>
|
||||
<Editor
|
||||
id={content.id}
|
||||
content={() => content.data}
|
||||
session={session}
|
||||
nonce={content.dateEdited}
|
||||
options={{ readonly: true, headless: true }}
|
||||
/>
|
||||
</Flex>
|
||||
</ScrollSyncPane>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex
|
||||
className="secondEditor"
|
||||
data-test-id="second-editor"
|
||||
sx={{
|
||||
flex: "1 1 auto",
|
||||
flexDirection: "column",
|
||||
width: ["100%", "100%", "50%"],
|
||||
height: ["50%", "50%", "100%"],
|
||||
borderLeft: conflictedContent.locked
|
||||
? "1px solid var(--border)"
|
||||
: "none"
|
||||
}}
|
||||
>
|
||||
{conflictedContent.locked ? (
|
||||
<UnlockView
|
||||
title={getFormattedDate(conflictedContent.dateEdited)}
|
||||
subtitle={strings.enterPasswordToUnlockVersion()}
|
||||
buttonTitle={strings.unlock()}
|
||||
unlock={async (password) => {
|
||||
const decryptedContent = await db.vault.decryptContent(
|
||||
conflictedContent,
|
||||
password
|
||||
);
|
||||
setConflictedContent({
|
||||
...conflictedContent,
|
||||
...decryptedContent,
|
||||
locked: false
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<ContentToggle
|
||||
readonly={session.type === "diff"}
|
||||
resolveConflict={onResolveContent}
|
||||
label={
|
||||
session.type === "diff"
|
||||
? strings.currentNote()
|
||||
: strings.incomingNote()
|
||||
}
|
||||
isSelected={selectedContent === 1}
|
||||
isOtherSelected={selectedContent === 0}
|
||||
dateEdited={conflictedContent.dateEdited}
|
||||
onToggle={() => setSelectedContent((s) => (s === 1 ? -1 : 1))}
|
||||
sx={{
|
||||
alignItems: "flex-end",
|
||||
borderStyle: "solid",
|
||||
borderWidth: 0,
|
||||
borderBottomWidth: 1,
|
||||
borderColor: "border",
|
||||
px: 2,
|
||||
pb: 1,
|
||||
pt: [1, 1, 0]
|
||||
/>
|
||||
|
||||
<ScrollSyncPane>
|
||||
<Flex
|
||||
sx={{
|
||||
px: 2,
|
||||
overflowY: "auto",
|
||||
flex: 1,
|
||||
borderStyle: "solid",
|
||||
borderWidth: 0,
|
||||
borderRightWidth: [0, 0, 1],
|
||||
borderBottomWidth: [1, 1, 0],
|
||||
borderColor: "border"
|
||||
}}
|
||||
>
|
||||
<Editor
|
||||
id={content.id}
|
||||
content={() => content.data}
|
||||
session={session}
|
||||
nonce={content.dateEdited}
|
||||
options={{ readonly: true, headless: true }}
|
||||
/>
|
||||
</Flex>
|
||||
</ScrollSyncPane>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex
|
||||
className="secondEditor"
|
||||
data-test-id="second-editor"
|
||||
sx={{
|
||||
flex: "1 1 auto",
|
||||
flexDirection: "column",
|
||||
width: ["100%", "100%", "50%"],
|
||||
height: ["50%", "50%", "100%"],
|
||||
borderLeft: conflictedContent?.locked
|
||||
? "1px solid var(--border)"
|
||||
: "none"
|
||||
}}
|
||||
>
|
||||
{conflictedContent.locked ? (
|
||||
<UnlockView
|
||||
title={getFormattedDate(conflictedContent.dateEdited)}
|
||||
subtitle={strings.enterPasswordToUnlockVersion()}
|
||||
buttonTitle={strings.unlock()}
|
||||
unlock={async (password) => {
|
||||
const decryptedContent = await db.vault.decryptContent(
|
||||
conflictedContent,
|
||||
password
|
||||
);
|
||||
setConflictedContent({
|
||||
...conflictedContent,
|
||||
...decryptedContent,
|
||||
locked: false
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<ScrollSyncPane>
|
||||
<Flex sx={{ px: 2, overflow: "auto" }}>
|
||||
<Editor
|
||||
id={`${conflictedContent.id}-conflicted`}
|
||||
session={session}
|
||||
content={() =>
|
||||
content.locked
|
||||
? conflictedContent.data
|
||||
: diff(content.data, conflictedContent.data)
|
||||
}
|
||||
nonce={conflictedContent.dateEdited}
|
||||
options={{ readonly: true, headless: true }}
|
||||
/>
|
||||
</Flex>
|
||||
</ScrollSyncPane>
|
||||
</>
|
||||
)}
|
||||
) : (
|
||||
<>
|
||||
<ContentToggle
|
||||
readonly={session.type === "diff"}
|
||||
resolveConflict={onResolveContent}
|
||||
label={
|
||||
session.type === "diff"
|
||||
? strings.currentNote()
|
||||
: strings.incomingNote()
|
||||
}
|
||||
isSelected={selectedContent === 1}
|
||||
isOtherSelected={selectedContent === 0}
|
||||
dateEdited={conflictedContent.dateEdited}
|
||||
onToggle={() =>
|
||||
setSelectedContent((s) => (s === 1 ? -1 : 1))
|
||||
}
|
||||
sx={{
|
||||
alignItems: "flex-end",
|
||||
borderStyle: "solid",
|
||||
borderWidth: 0,
|
||||
borderBottomWidth: 1,
|
||||
borderColor: "border",
|
||||
px: 2,
|
||||
pb: 1,
|
||||
pt: [1, 1, 0]
|
||||
}}
|
||||
/>
|
||||
<ScrollSyncPane>
|
||||
<Flex sx={{ px: 2, overflow: "auto" }}>
|
||||
<Editor
|
||||
id={`${conflictedContent.id}-conflicted`}
|
||||
session={session}
|
||||
content={() =>
|
||||
content.locked
|
||||
? conflictedContent.data
|
||||
: diff(content.data, conflictedContent.data)
|
||||
}
|
||||
nonce={conflictedContent.dateEdited}
|
||||
options={{ readonly: true, headless: true }}
|
||||
/>
|
||||
</Flex>
|
||||
</ScrollSyncPane>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ScrollSync>
|
||||
</ScrollSync>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -379,7 +397,7 @@ async function resolveConflict({
|
||||
await notesStore.refresh();
|
||||
}
|
||||
|
||||
async function createCopy(note: Note, content: ContentItem) {
|
||||
async function createCopy(note: Note, content: ContentItem, title?: string) {
|
||||
if (content.locked) {
|
||||
const contentId = await db.content.add({
|
||||
locked: true,
|
||||
@@ -389,7 +407,7 @@ async function createCopy(note: Note, content: ContentItem) {
|
||||
});
|
||||
return await db.notes.add({
|
||||
contentId,
|
||||
title: note.title + " (COPY)"
|
||||
title: title || note.title + " (COPY)"
|
||||
});
|
||||
} else {
|
||||
return await db.notes.add({
|
||||
@@ -397,7 +415,7 @@ async function createCopy(note: Note, content: ContentItem) {
|
||||
type: "tiptap",
|
||||
data: content.data
|
||||
},
|
||||
title: note.title + " (COPY)"
|
||||
title: title || note.title + " (COPY)"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,8 +144,8 @@ export type DiffEditorSession = BaseEditorSession & {
|
||||
type: "diff";
|
||||
note: Note;
|
||||
content: ContentItem;
|
||||
oldTitle?: string;
|
||||
historySessionId: string;
|
||||
oldContentTitle?: string;
|
||||
};
|
||||
|
||||
export type EditorSession =
|
||||
@@ -620,12 +620,13 @@ class EditorStore extends BaseStore<EditorStore> {
|
||||
openDiffSession = async (noteId: string, sessionId: string) => {
|
||||
const session = await db.noteHistory.session(sessionId);
|
||||
const note = await db.notes.note(noteId);
|
||||
if (!session || !note || !note.contentId) return;
|
||||
if (!session || !note) return;
|
||||
|
||||
const currentContent = await db.content.get(note.contentId);
|
||||
const currentContent = note.contentId
|
||||
? await db.content.get(note.contentId)
|
||||
: undefined;
|
||||
const oldContent = await db.noteHistory.content(session.id);
|
||||
|
||||
if (!oldContent || !currentContent) return;
|
||||
if (!oldContent) return;
|
||||
|
||||
const {
|
||||
getSession,
|
||||
@@ -659,7 +660,7 @@ class EditorStore extends BaseStore<EditorStore> {
|
||||
note,
|
||||
tabId,
|
||||
title: label,
|
||||
oldContentTitle: oldContent.title,
|
||||
oldTitle: oldContent.title,
|
||||
historySessionId: session.id,
|
||||
content: {
|
||||
type: oldContent.type || "tiptap",
|
||||
@@ -1356,7 +1357,7 @@ const useEditorStore = createPersistedStore(EditorStore, {
|
||||
}, [] as EditorSession[])
|
||||
}),
|
||||
storage: db.config() as PersistStorage<Partial<EditorStore>>
|
||||
});
|
||||
}) as ReturnType<typeof createPersistedStore<EditorStore>>;
|
||||
export { useEditorStore, SESSION_STATES };
|
||||
|
||||
const MILLISECONDS_IN_A_MINUTE = 60 * 1000;
|
||||
|
||||
Reference in New Issue
Block a user