feat(client): support temporarily disabling read-only note when read-only threshold is triggered

feat(client): stop the cursor from going to the top of the note when note transitions to read-only but we're still editing

feat(client): stop the cursor from going to the top of the note when note transitions to read-only but we're still editing, take 2
This commit is contained in:
perf3ct 2025-05-12 18:49:48 +00:00
parent ae83f0a1c7
commit f07ad03343
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232

View File

@ -127,12 +127,51 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
if (data === undefined) { if (data === undefined) {
return; return;
} }
// Check if note is read-only before attempting to save
const isReadOnlyBefore = await this.noteContext.isReadOnly();
// If the note is read-only due to size but user is still editing,
// we need to temporarily disable read-only mode
if (isReadOnlyBefore && !this.noteContext.viewScope?.readOnlyTemporarilyDisabled) {
// Auto-enable temporary edit mode (same as "Edit this note" button)
if (this.noteContext.viewScope) {
this.noteContext.viewScope.readOnlyTemporarilyDisabled = true;
appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext: this.noteContext });
}
}
protectedSessionHolder.touchProtectedSessionIfNecessary(note); protectedSessionHolder.touchProtectedSessionIfNecessary(note);
await server.put(`notes/${noteId}/data`, data, this.componentId); try {
await server.put(`notes/${noteId}/data`, data, this.componentId);
this.getTypeWidget().dataSaved(); this.getTypeWidget().dataSaved();
// Check if this save operation made the note cross the threshold
if (!isReadOnlyBefore && (await this.noteContext.isReadOnly())) {
// Note just became read-only after this save - enable temporary edit mode
if (this.noteContext.viewScope) {
this.noteContext.viewScope.readOnlyTemporarilyDisabled = true;
appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext: this.noteContext });
}
}
} catch (err) {
// If save failed because the note just became read-only,
// enable temporary edit mode and try again
if (!isReadOnlyBefore && (await this.noteContext.isReadOnly())) {
if (this.noteContext.viewScope) {
this.noteContext.viewScope.readOnlyTemporarilyDisabled = true;
appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext: this.noteContext });
// Try saving again with temporary edit mode enabled
await server.put(`notes/${noteId}/data`, data, this.componentId);
this.getTypeWidget().dataSaved();
}
} else {
// For other errors, just rethrow
throw err;
}
}
}); });
appContext.addBeforeUnloadListener(this); appContext.addBeforeUnloadListener(this);
@ -364,9 +403,73 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
return this.spacedUpdate.isAllSavedAndTriggerUpdate(); return this.spacedUpdate.isAllSavedAndTriggerUpdate();
} }
readOnlyTemporarilyDisabledEvent({ noteContext }: EventData<"readOnlyTemporarilyDisabled">) { async readOnlyTemporarilyDisabledEvent({ noteContext }: EventData<"readOnlyTemporarilyDisabled">) {
if (this.isNoteContext(noteContext.ntxId)) { if (this.isNoteContext(noteContext.ntxId)) {
this.refresh(); // Check if we're dealing with a text note/editor type
const isTextNote = this.type === "editableText" || this.type === "readOnlyText";
if (isTextNote) {
// Get current editor and selection before refresh
const currentEditor = await this.noteContext?.getTextEditor();
let cursorInfo = null;
if (currentEditor) {
try {
const selection = currentEditor.model.document.selection;
if (selection) {
const cursorPos = selection.getFirstPosition();
if (cursorPos) {
// Store minimal info about cursor position to restore later
cursorInfo = {
// Store paragraph index and character offset
paraIndex: cursorPos.path[0] || 0,
offset: cursorPos.offset
};
}
}
} catch (e) {
// Ignore errors in getting selection
}
}
// Perform refresh to transition editor
await this.refresh();
// Immediately attempt to restore cursor position without delay
if (cursorInfo) {
const newEditor = await this.noteContext?.getTextEditor();
if (newEditor) {
try {
newEditor.model.change(writer => {
const root = newEditor.model.document.getRoot();
if (!root) return;
// Find the paragraph at the same index or closest available
const targetParaIndex = Math.min(cursorInfo.paraIndex, root.childCount - 1);
const paragraph = root.getChild(targetParaIndex);
if (paragraph) {
// Use an explicit cast to access maxOffset
const maxOffset = (paragraph as any).maxOffset || 0;
const safeOffset = Math.min(cursorInfo.offset, maxOffset);
// Set cursor at the preserved position
const position = writer.createPositionAt(paragraph, safeOffset);
writer.setSelection(position);
}
});
// Focus editor to make cursor visible immediately
newEditor.editing.view.focus();
} catch (e) {
// Silently fail if we can't restore cursor
}
}
}
} else {
// Not a text editor, just do a regular refresh
await this.refresh();
}
} }
} }