diff --git a/src/App.vue b/src/App.vue
index 34dcd14..f03b95e 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -26,6 +26,11 @@ export default {
},
created() {
this.$store.commit('setInquiries', storedInquiries.getStoredInquiries())
+ addEventListener('storage', event => {
+ if (event.key === storedInquiries.myInquiriesKey) {
+ this.$store.commit('setInquiries', storedInquiries.getStoredInquiries())
+ }
+ })
}
}
diff --git a/src/lib/storedInquiries/index.js b/src/lib/storedInquiries/index.js
index 04f568e..2257d5b 100644
--- a/src/lib/storedInquiries/index.js
+++ b/src/lib/storedInquiries/index.js
@@ -4,11 +4,13 @@ import events from '@/lib/utils/events'
import migration from './_migrations'
const migrate = migration._migrate
+const myInquiriesKey = 'myInquiries'
export default {
version: 2,
+ myInquiriesKey,
getStoredInquiries() {
- let myInquiries = JSON.parse(localStorage.getItem('myInquiries'))
+ let myInquiries = JSON.parse(localStorage.getItem(myInquiriesKey))
if (!myInquiries) {
const oldInquiries = localStorage.getItem('myQueries')
if (oldInquiries) {
@@ -26,7 +28,8 @@ export default {
const newInquiry = JSON.parse(JSON.stringify(baseInquiry))
newInquiry.name = newInquiry.name + ' Copy'
newInquiry.id = nanoid()
- newInquiry.createdAt = new Date()
+ newInquiry.createdAt = new Date().toJSON()
+ newInquiry.updatedAt = new Date().toJSON()
delete newInquiry.isPredefined
return newInquiry
@@ -38,7 +41,7 @@ export default {
updateStorage(inquiries) {
localStorage.setItem(
- 'myInquiries',
+ myInquiriesKey,
JSON.stringify({ version: this.version, inquiries })
)
},
diff --git a/src/lib/tab.js b/src/lib/tab.js
index c743765..c00b197 100644
--- a/src/lib/tab.js
+++ b/src/lib/tab.js
@@ -28,6 +28,7 @@ export default class Tab {
this.isSaved = !!inquiry.id
this.state = state
+ this.updatedAt = inquiry.updatedAt
}
async execute() {
diff --git a/src/store/actions.js b/src/store/actions.js
index 35292d3..17e2768 100644
--- a/src/store/actions.js
+++ b/src/store/actions.js
@@ -15,13 +15,14 @@ export default {
return inquiry.id
},
- async saveInquiry({ state }, { inquiryTab, newName, overwrite }) {
+ async saveInquiry({ state }, { inquiryTab, newName }) {
const value = {
- id: inquiryTab.isPredefined || !overwrite ? nanoid() : inquiryTab.id,
+ id: inquiryTab.isPredefined || newName ? nanoid() : inquiryTab.id,
query: inquiryTab.query,
viewType: inquiryTab.dataView.mode,
viewOptions: inquiryTab.dataView.getOptionsForSave(),
- name: newName || inquiryTab.name
+ name: newName || inquiryTab.name,
+ updatedAt: new Date().toJSON()
}
// Get inquiries from local storage
diff --git a/src/store/mutations.js b/src/store/mutations.js
index 693b5a5..ef854f9 100644
--- a/src/store/mutations.js
+++ b/src/store/mutations.js
@@ -7,7 +7,8 @@ export default {
},
updateTab(state, { tab, newValues }) {
- const { name, id, query, viewType, viewOptions, isSaved } = newValues
+ const { name, id, query, viewType, viewOptions, isSaved, updatedAt } =
+ newValues
const oldId = tab.id
if (id && state.currentTabId === oldId) {
@@ -36,6 +37,9 @@ export default {
// Saved inquiry is not predefined
delete tab.isPredefined
}
+ if (updatedAt) {
+ tab.updatedAt = updatedAt
+ }
},
deleteTab(state, tab) {
diff --git a/src/views/MainView/MainMenu.vue b/src/views/MainView/MainMenu.vue
index 387265c..38dc4bd 100644
--- a/src/views/MainView/MainMenu.vue
+++ b/src/views/MainView/MainMenu.vue
@@ -10,16 +10,16 @@
Save
+
+
+
+
+
+
+
+ This inquiry has been modified in the mean time. This can happen if an
+ inquiry is saved in another window or browser tab. Do you want to
+ overwrite that changes or save the current state as a new inquiry?
+
+
+
+ Cancel
+ Overwrite
+ Save as new
+
+
@@ -79,24 +104,26 @@ export default {
data() {
return {
name: '',
- errorMsg: null,
- overwrite: false
+ errorMsg: null
}
},
computed: {
- currentInquiry() {
+ inquiries() {
+ return this.$store.state.inquiries
+ },
+ currentInquiryTab() {
return this.$store.state.currentTab
},
isSaved() {
- return this.currentInquiry && this.currentInquiry.isSaved
+ return this.currentInquiryTab && this.currentInquiryTab.isSaved
},
isPredefined() {
- return this.currentInquiry && this.currentInquiry.isPredefined
+ return this.currentInquiryTab && this.currentInquiryTab.isPredefined
},
runDisabled() {
return (
- this.currentInquiry &&
- (!this.$store.state.db || !this.currentInquiry.query)
+ this.currentInquiryTab &&
+ (!this.$store.state.db || !this.currentInquiryTab.query)
)
}
},
@@ -121,24 +148,35 @@ export default {
},
cancelSave() {
this.$modal.hide('save')
+ this.$modal.hide('inquiry-conflict')
eventBus.$off('inquirySaved')
},
- onSave() {
- this.overwrite = true
- if (storedInquiries.isTabNeedName(this.currentInquiry)) {
+ onSave(skipConcurrentEditingCheck = false) {
+ this.errorMsg = null
+ this.name = ''
+ if (storedInquiries.isTabNeedName(this.currentInquiryTab)) {
this.openSaveModal()
- } else {
- this.saveInquiry()
+ return
}
+
+ if (!skipConcurrentEditingCheck) {
+ const inquiryInStore = this.inquiries.find(
+ inquiry => inquiry.id === this.currentInquiryTab.id
+ )
+
+ if (inquiryInStore?.updatedAt !== this.currentInquiryTab?.updatedAt) {
+ this.$modal.show('inquiry-conflict')
+ return
+ }
+ }
+ this.saveInquiry()
},
onSaveAs() {
- console.log('save as')
- this.overwrite = false
+ this.errorMsg = null
+ this.name = ''
this.openSaveModal()
},
openSaveModal() {
- this.errorMsg = null
- this.name = ''
this.$modal.show('save')
},
validateSaveFormAndSaveInquiry() {
@@ -149,26 +187,26 @@ export default {
this.saveInquiry()
},
async saveInquiry() {
- const dataSet = this.currentInquiry.result
- const tabView = this.currentInquiry.view
+ const dataSet = this.currentInquiryTab.result
+ const tabView = this.currentInquiryTab.view
// Save inquiry
const value = await this.$store.dispatch('saveInquiry', {
- inquiryTab: this.currentInquiry,
- newName: this.name,
- overwrite: this.overwrite
+ inquiryTab: this.currentInquiryTab,
+ newName: this.name
})
// Update tab in store
this.$store.commit('updateTab', {
- tab: this.currentInquiry,
+ tab: this.currentInquiryTab,
newValues: {
name: value.name,
id: value.id,
query: value.query,
viewType: value.viewType,
viewOptions: value.viewOptions,
- isSaved: true
+ isSaved: true,
+ updatedAt: value.updatedAt
}
})
@@ -178,12 +216,13 @@ export default {
// it will be without sql result and has default view - table.
// That's why we need to restore data and view
this.$nextTick(() => {
- this.currentInquiry.result = dataSet
- this.currentInquiry.view = tabView
+ this.currentInquiryTab.result = dataSet
+ this.currentInquiryTab.view = tabView
})
- // Hide dialog
+ // Hide dialogs
this.$modal.hide('save')
+ this.$modal.hide('inquiry-conflict')
// Signal about saving
eventBus.$emit('inquirySaved')
@@ -195,7 +234,7 @@ export default {
if ((e.key === 'r' || e.key === 'Enter') && (e.ctrlKey || e.metaKey)) {
e.preventDefault()
if (!this.runDisabled) {
- this.currentInquiry.execute()
+ this.currentInquiryTab.execute()
}
return
}