Keep changes in preview after save (#360)

A bit hacky, but should allow preview to update to latest file contents
after save
This commit is contained in:
Evan Simkowitz 2024-09-10 13:37:30 -07:00 committed by GitHub
parent 04fb8e5aad
commit 13ac6af4d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 21 deletions

View File

@ -178,7 +178,7 @@ type MarkdownProps = {
};
const Markdown = ({ text, textAtom, showTocAtom, style, className, resolveOpts, onClickExecute }: MarkdownProps) => {
const textAtomValue = useAtomValueSafe(textAtom);
const textAtomValue = useAtomValueSafe<string>(textAtom);
const tocRef = useRef<TocItem[]>([]);
const showToc = useAtomValueSafe(showTocAtom) ?? false;
const contentsOsRef = useRef<OverlayScrollbarsComponentRef>(null);

View File

@ -2,11 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
import { atoms } from "@/app/store/global";
import { useAtomValueSafe } from "@/util/util";
import loader from "@monaco-editor/loader";
import { Editor, Monaco } from "@monaco-editor/react";
import { atom, useAtomValue } from "jotai";
import { Atom, atom, useAtomValue } from "jotai";
import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api";
import React, { useMemo, useRef } from "react";
import React, { useMemo, useRef, useState } from "react";
import "./codeeditor.less";
// there is a global monaco variable (TODO get the correct TS type)
@ -70,7 +71,8 @@ function defaultEditorOptions(): MonacoTypes.editor.IEditorOptions {
}
interface CodeEditorProps {
text: string;
text?: string;
textAtom?: Atom<string> | Atom<Promise<string>>;
filename: string;
language?: string;
onChange?: (text: string) => void;
@ -87,11 +89,13 @@ const stickyScrollEnabledAtom = atom((get) => {
return settings["editor:stickyscrollenabled"] ?? false;
});
export function CodeEditor({ text, language, filename, onChange, onMount }: CodeEditorProps) {
export function CodeEditor({ text, textAtom, language, filename, onChange, onMount }: CodeEditorProps) {
const divRef = useRef<HTMLDivElement>(null);
const unmountRef = useRef<() => void>(null);
const minimapEnabled = useAtomValue(minimapEnabledAtom);
const stickyScrollEnabled = useAtomValue(stickyScrollEnabledAtom);
const textAtomValue = useAtomValueSafe<string>(textAtom);
const [textValue] = useState(() => textAtomValue ?? text);
const theme = "wave-theme-dark";
React.useEffect(() => {
@ -127,7 +131,7 @@ export function CodeEditor({ text, language, filename, onChange, onMount }: Code
<div className="code-editor" ref={divRef}>
<Editor
theme={theme}
value={text}
value={textValue}
options={editorOpts}
onChange={handleEditorChange}
onMount={handleEditorOnMount}

View File

@ -126,7 +126,9 @@ export class PreviewModel implements ViewModel {
fileMimeType: jotai.Atom<Promise<string>>;
fileMimeTypeLoadable: jotai.Atom<Loadable<string>>;
fileContent: jotai.Atom<Promise<string>>;
newFileContent: jotai.PrimitiveAtom<string | null>;
fileContentLastSaved: jotai.PrimitiveAtom<string>;
fileContentModified: jotai.PrimitiveAtom<string>;
previewFileContent: jotai.WritableAtom<Promise<string>, [value: string], void>;
openFileModal: jotai.PrimitiveAtom<boolean>;
openFileError: jotai.PrimitiveAtom<string>;
@ -208,6 +210,7 @@ export class PreviewModel implements ViewModel {
const blockData = get(this.blockAtom);
return blockData?.meta?.edit ?? false;
});
this.viewName = jotai.atom("Preview");
this.viewText = jotai.atom((get) => {
let headerPath = get(this.metaFilePath);
@ -241,7 +244,7 @@ export class PreviewModel implements ViewModel {
},
];
let saveClassName = "secondary";
if (get(this.newFileContent) !== null) {
if (get(this.fileContentModified) !== null) {
saveClassName = "primary";
}
if (isCeView) {
@ -368,7 +371,6 @@ export class PreviewModel implements ViewModel {
return fileInfo?.mimetype;
});
this.fileMimeTypeLoadable = loadable(this.fileMimeType);
this.newFileContent = jotai.atom(null) as jotai.PrimitiveAtom<string | null>;
this.goParentDirectory = this.goParentDirectory.bind(this);
const fullFileAtom = jotai.atom<Promise<FullFile>>(async (get) => {
@ -389,6 +391,23 @@ export class PreviewModel implements ViewModel {
this.fullFile = fullFileAtom;
this.fileContent = fileContentAtom;
this.fileContentModified = jotai.atom();
this.fileContentLastSaved = jotai.atom();
this.previewFileContent = jotai.atom(
async (get) => {
const fileContentModified = get(this.fileContentModified);
const fileContentLastSaved = get(this.fileContentLastSaved);
if (!fileContentModified) {
return fileContentLastSaved ?? (await get(this.fileContent));
} else {
return fileContentModified;
}
},
(_get, set, value) => {
set(this.fileContentModified, value);
}
);
this.specializedView = jotai.atom<Promise<{ specializedView?: string; errorStr?: string }>>(async (get) => {
return this.getSpecializedView(get);
});
@ -533,7 +552,7 @@ export class PreviewModel implements ViewModel {
if (filePath == null) {
return;
}
const newFileContent = globalStore.get(this.newFileContent);
const newFileContent = globalStore.get(this.fileContentModified);
if (newFileContent == null) {
console.log("not saving file, newFileContent is null");
return;
@ -541,7 +560,8 @@ export class PreviewModel implements ViewModel {
const conn = globalStore.get(this.connection) ?? "";
try {
services.FileService.SaveFile(conn, filePath, util.stringToBase64(newFileContent));
globalStore.set(this.newFileContent, null);
globalStore.set(this.fileContentModified, null);
globalStore.set(this.fileContentLastSaved, newFileContent);
console.log("saved file", filePath);
} catch (error) {
console.error("Error saving file:", error);
@ -549,9 +569,7 @@ export class PreviewModel implements ViewModel {
}
async handleFileRevert() {
const fileContent = await globalStore.get(this.fileContent);
this.monacoRef.current?.setValue(fileContent);
globalStore.set(this.newFileContent, null);
globalStore.set(this.fileContentModified, null);
}
async handleOpenFile(filePath: string) {
@ -621,7 +639,7 @@ export class PreviewModel implements ViewModel {
const loadableSV = globalStore.get(this.loadableSpecializedView);
if (loadableSV.state == "hasData") {
if (loadableSV.data.specializedView == "codeedit") {
if (globalStore.get(this.newFileContent) != null) {
if (globalStore.get(this.fileContentModified) != null) {
menuItems.push({ type: "separator" });
menuItems.push({
label: "Save File",
@ -711,7 +729,11 @@ function MarkdownPreview({ model }: SpecializedViewProps) {
}, [connName, fileInfo.dir]);
return (
<div className="view-preview view-preview-markdown">
<Markdown textAtom={model.fileContent} showTocAtom={model.markdownShowToc} resolveOpts={resolveOpts} />
<Markdown
textAtom={model.previewFileContent}
showTocAtom={model.markdownShowToc}
resolveOpts={resolveOpts}
/>
</div>
);
}
@ -762,8 +784,7 @@ function StreamingPreview({ model }: SpecializedViewProps) {
}
function CodeEditPreview({ model }: SpecializedViewProps) {
const fileContent = jotai.useAtomValue(model.fileContent);
const setNewFileContent = jotai.useSetAtom(model.newFileContent);
const setNewFileContent = jotai.useSetAtom(model.previewFileContent);
const fileName = jotai.useAtomValue(model.statFilePath);
function codeEditKeyDownHandler(e: WaveKeyboardEvent): boolean {
@ -812,16 +833,16 @@ function CodeEditPreview({ model }: SpecializedViewProps) {
return (
<CodeEditor
text={fileContent}
textAtom={model.previewFileContent}
filename={fileName}
onChange={(text) => setNewFileContent(text)}
onChange={setNewFileContent}
onMount={onMount}
/>
);
}
function CSVViewPreview({ model, parentRef }: SpecializedViewProps) {
const fileContent = jotai.useAtomValue(model.fileContent);
const fileContent = jotai.useAtomValue(model.previewFileContent);
const fileName = jotai.useAtomValue(model.statFilePath);
return <CSVView parentRef={parentRef} readonly={true} content={fileContent} filename={fileName} />;
}