From b098e4cb99302c01bbc1b477307bbed3869f4b4f Mon Sep 17 00:00:00 2001 From: Laurynas Gadliauskas Date: Tue, 1 Jun 2021 12:35:56 +0300 Subject: [PATCH] feat: Unsaved changes confirmation (#4) --- frontend/src/i18n/en.json | 1 + frontend/src/views/Files.vue | 28 +++++++++++++++++++++++++++- frontend/src/views/files/Editor.vue | 26 ++++++++++++++++++++++---- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 0181ed4c..d2c51f9f 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -115,6 +115,7 @@ }, "permanent": "Permanent", "prompts": { + "unsavedChanges": "Changes that you made may not be saved. Leave page?", "archive": "Archive", "archiveMessage": "Choose archive name and format:", "unarchive": "Unarchive", diff --git a/frontend/src/views/Files.vue b/frontend/src/views/Files.vue index 8f50426d..1808d218 100644 --- a/frontend/src/views/Files.vue +++ b/frontend/src/views/Files.vue @@ -5,7 +5,11 @@ - +

@@ -47,6 +51,7 @@ export default { return { error: null, width: window.innerWidth, + unsavedChanges: false, }; }, computed: { @@ -68,6 +73,12 @@ export default { } }, }, + beforeRouteUpdate(to, from, next) { + this.verifyRouteChange(next); + }, + beforeRouteLeave(to, from, next) { + this.verifyRouteChange(next); + }, created() { this.fetchData(); }, @@ -92,6 +103,21 @@ export default { this.$store.commit("updateRequest", {}); }, methods: { + verifyRouteChange(next) { + if ( + this.currentView === "editor" && + this.unsavedChanges && + !confirm(this.$t("prompts.unsavedChanges")) + ) { + next(false); + } else { + this.unsavedChanges = false; + next(); + } + }, + setChanged(value) { + this.unsavedChanges = value; + }, ...mapMutations(["setLoading"]), async fetchData() { // Reset view information. diff --git a/frontend/src/views/files/Editor.vue b/frontend/src/views/files/Editor.vue index 519bd886..192a101b 100644 --- a/frontend/src/views/files/Editor.vue +++ b/frontend/src/views/files/Editor.vue @@ -44,7 +44,9 @@ export default { Breadcrumbs, }, data: function () { - return {}; + return { + unsavedChanges: false, + }; }, computed: { ...mapState(["req", "user"]), @@ -78,10 +80,17 @@ export default { return breadcrumbs; }, }, + watch: { + unsavedChanges() { + this.$emit("changed", this.unsavedChanges); + }, + }, created() { + window.addEventListener("beforeunload", this.beforeWindowUnload); window.addEventListener("keydown", this.keyEvent); }, beforeDestroy() { + window.removeEventListener("beforeunload", this.beforeWindowUnload); window.removeEventListener("keydown", this.keyEvent); this.editor.destroy(); }, @@ -97,14 +106,18 @@ export default { wrap: true, }); + this.editor.on("change", () => (this.unsavedChanges = true)); + if (theme == "dark") { this.editor.setTheme("ace/theme/twilight"); } }, methods: { - back() { - let uri = url.removeLastDir(this.$route.path) + "/"; - this.$router.push({ path: uri }); + beforeWindowUnload(e) { + if (this.unsavedChanges) { + e.preventDefault(); + e.returnValue = ""; + } }, keyEvent(event) { if (!event.ctrlKey && !event.metaKey) { @@ -121,6 +134,7 @@ export default { async save() { const button = "save"; buttons.loading("save"); + this.unsavedChanges = false; try { await api.put(this.$route.path, this.editor.getValue()); @@ -131,6 +145,10 @@ export default { } }, close() { + if (this.unsavedChanges && !confirm(this.$t("prompts.unsavedChanges"))) { + return; + } + this.$store.commit("updateRequest", {}); let uri = url.removeLastDir(this.$route.path) + "/";