label="Save"
:iconPath="mdilContentSave"
@click="saveHandler((close = false))"
- />
+ class="relative"
+ >
+ <!-- Unsaved Changes Indicator -->
+ <div
+ v-show="unsavedChanges"
+ class="absolute right-1 h-1 w-1 rounded-full bg-theme-brand"
+ ></div>
+ </CustomButton>
<Toggle
v-if="canModify"
label="Edit"
:initialValue="getInitialEditorValue()"
:initialEditType="loadDefaultEditorMode()"
:addImageBlobHook="addImageBlobHook"
- @change="startDraftSaveTimeout"
+ @change="startContentChangedTimeout"
/>
</div>
</LoadingIndicator>
import { mdilContentSave, mdilDelete } from "@mdi/light-js";
import Mousetrap from "mousetrap";
import { useToast } from "primevue/usetoast";
-import { computed, nextTick, onMounted, ref, watch } from "vue";
+import { computed, onMounted, ref, watch } from "vue";
import { useRouter } from "vue-router";
import {
});
const canModify = computed(() => globalStore.authType != authTypes.readOnly);
-let draftSaveTimeout = null;
+let contentChangedTimeout = null;
const editMode = ref(false);
const globalStore = useGlobalStore();
const isSaveChangesModalVisible = ref(false);
const newTitle = ref();
const toast = useToast();
const toastEditor = ref();
+const unsavedChanges = ref(false);
// 'e' to edit
Mousetrap.bind("e", () => {
function setEditMode() {
setBeforeUnloadConfirmation(true);
newTitle.value = note.value.title;
+ unsavedChanges.value = false;
editMode.value = true;
}
}
function noteSaveSuccess(close = false) {
+ unsavedChanges.value = false;
if (close) {
closeNote();
}
// Note Closure
function closeHandler() {
- if (
- newTitle.value != note.value.title ||
- toastEditor.value.getMarkdown() != note.value.content
- ) {
+ if (isContentChanged()) {
isSaveChangesModalVisible.value = true;
} else {
closeNote();
});
}
-// Drafts
-function clearDraftSaveTimeout() {
- if (draftSaveTimeout != null) {
- clearTimeout(draftSaveTimeout);
+// Content Change Watcher
+function startContentChangedTimeout() {
+ clearContentChangedTimeout();
+ contentChangedTimeout = setTimeout(contentChangedHandler, 1000);
+}
+
+function clearContentChangedTimeout() {
+ if (contentChangedTimeout != null) {
+ clearTimeout(contentChangedTimeout);
+ }
+}
+
+function contentChangedHandler() {
+ if (isContentChanged()) {
+ unsavedChanges.value = true;
+ saveDraft();
+ } else {
+ unsavedChanges.value = false;
+ clearDraft();
}
}
+// Drafts
function saveDraft() {
const content = toastEditor.value.getMarkdown();
if (content) {
}
}
-function startDraftSaveTimeout() {
- clearDraftSaveTimeout();
- draftSaveTimeout = setTimeout(saveDraft, 1000);
-}
-
function clearDraft() {
localStorage.removeItem(note.value.title);
}
return defaultWysiwygMode || "markdown";
}
+function isContentChanged() {
+ return (
+ newTitle.value != note.value.title ||
+ toastEditor.value.getMarkdown() != note.value.content
+ );
+}
+
watch(() => props.title, init);
onMounted(init);
</script>