1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-06 18:18:53 +08:00

refactoring manipulations with My queries

This commit is contained in:
lana-k
2021-01-05 23:09:47 +01:00
parent 9035cae19f
commit 8d083a93c4
7 changed files with 207 additions and 176 deletions

View File

@@ -54,7 +54,7 @@
<script> <script>
import TextField from '@/components/TextField' import TextField from '@/components/TextField'
import CloseIcon from '@/components/svg/close' import CloseIcon from '@/components/svg/close'
import queryTab from '@/queryTab' import storedQueries from '@/storedQueries'
export default { export default {
name: 'MainMenu', name: 'MainMenu',
@@ -100,9 +100,9 @@ export default {
}, },
methods: { methods: {
createNewQuery () { createNewQuery () {
const tab = queryTab.newBlankTab() this.$store.dispatch('addTab').then(id => {
this.$store.commit('addTab', tab) this.$store.commit('setCurrentTabId', id)
this.$store.commit('setCurrentTabId', tab.id) })
}, },
cancelSave () { cancelSave () {
this.$modal.hide('save') this.$modal.hide('save')
@@ -112,14 +112,14 @@ export default {
this.errorMsg = null this.errorMsg = null
this.name = '' this.name = ''
if (queryTab.isTabNeedName(this.currentQuery)) { if (storedQueries.isTabNeedName(this.currentQuery)) {
this.$modal.show('save') this.$modal.show('save')
} else { } else {
this.saveQuery() this.saveQuery()
} }
}, },
saveQuery () { saveQuery () {
const isNeedName = queryTab.isTabNeedName(this.currentQuery) const isNeedName = storedQueries.isTabNeedName(this.currentQuery)
if (isNeedName && !this.name) { if (isNeedName && !this.name) {
this.errorMsg = 'Query name can\'t be empty' this.errorMsg = 'Query name can\'t be empty'
return return
@@ -128,7 +128,7 @@ export default {
const tabView = this.currentQuery.view const tabView = this.currentQuery.view
// Save query // Save query
const value = queryTab.save(this.currentQuery, this.name) const value = storedQueries.save(this.currentQuery, this.name)
// Update tab in store // Update tab in store
this.$store.commit('updateTab', { this.$store.commit('updateTab', {

View File

@@ -13,6 +13,7 @@ import 'codemirror/theme/neo.css'
import 'codemirror/addon/hint/show-hint.js' import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/show-hint.css' import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/hint/sql-hint.js' import 'codemirror/addon/hint/sql-hint.js'
import 'codemirror/addon/display/autorefresh.js'
import { debounce } from 'debounce' import { debounce } from 'debounce'
const sqlHint = CM.hint.sql const sqlHint = CM.hint.sql
@@ -42,7 +43,9 @@ export default {
mode: 'text/x-mysql', mode: 'text/x-mysql',
theme: 'neo', theme: 'neo',
lineNumbers: true, lineNumbers: true,
line: true line: true,
autofocus: true,
autoRefresh: true
} }
} }
}, },

View File

@@ -1,58 +0,0 @@
import { nanoid } from 'nanoid'
import store from '@/store'
export default {
newBlankTab () {
return {
id: nanoid(),
name: null,
tempName: store.state.untitledLastIndex
? `Untitled ${store.state.untitledLastIndex}`
: 'Untitled',
isUnsaved: true
}
},
isTabNeedName (queryTab) {
const isFromScratch = !queryTab.initName
return isFromScratch || queryTab.isPredefined
},
getDataToSave (queryTab, newName) {
return {
id: queryTab.isPredefined ? nanoid() : queryTab.id,
query: queryTab.query,
chart: queryTab.getChartSatateForSave(),
name: newName || queryTab.initName
}
},
save (queryTab, newName) {
const value = this.getDataToSave(queryTab, newName)
const isNeedName = this.isTabNeedName(queryTab)
// Get queries from local storage
let myQueries = JSON.parse(localStorage.getItem('myQueries'))
// Set createdAt
if (isNeedName) {
value.createdAt = new Date()
} else {
var queryIndex = myQueries.findIndex(oldQuery => oldQuery.id === queryTab.id)
value.createdAt = myQueries[queryIndex].createdAt
}
// Insert in queries list
if (!myQueries) {
myQueries = [value]
} else if (isNeedName) {
myQueries.push(value)
} else {
myQueries[queryIndex] = value
}
// Save to local storage
localStorage.setItem('myQueries', JSON.stringify(myQueries))
return value
}
}

View File

@@ -1,5 +1,6 @@
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import { nanoid } from 'nanoid'
Vue.use(Vuex) Vue.use(Vuex)
@@ -19,15 +20,7 @@ export const mutations = {
state.dbName = dbName state.dbName = dbName
state.schema = schema state.schema = schema
}, },
addTab (state, tab) {
// add new tab only if was not already opened
if (!state.tabs.some(openedTab => openedTab.id === tab.id)) {
state.tabs.push(tab)
if (!tab.name) {
state.untitledLastIndex += 1
}
}
},
updateTab (state, { index, name, id, query, chart, isUnsaved }) { updateTab (state, { index, name, id, query, chart, isUnsaved }) {
const tab = state.tabs[index] const tab = state.tabs[index]
const oldId = tab.id const oldId = tab.id
@@ -78,7 +71,34 @@ export const mutations = {
} }
} }
export const actions = {
async addTab ({ state }, tab) {
// If no tab then create a new blank one...
if (!tab) {
tab = {
id: nanoid(),
name: null,
tempName: state.untitledLastIndex
? `Untitled ${state.untitledLastIndex}`
: 'Untitled',
isUnsaved: true
}
}
// add new tab only if was not already opened
if (!state.tabs.some(openedTab => openedTab.id === tab.id)) {
state.tabs.push(tab)
if (!tab.name) {
state.untitledLastIndex += 1
}
}
return tab.id
}
}
export default new Vuex.Store({ export default new Vuex.Store({
state, state,
mutations mutations,
actions
}) })

83
src/storedQueries.js Normal file
View File

@@ -0,0 +1,83 @@
import { nanoid } from 'nanoid'
export default {
getStoredQueries () {
return JSON.parse(localStorage.getItem('myQueries')) || []
},
duplicateQuery (baseQuery) {
const newQuery = JSON.parse(JSON.stringify(baseQuery))
newQuery.name = newQuery.name + ' Copy'
newQuery.id = nanoid()
newQuery.createdAt = new Date()
delete newQuery.isPredefined
return newQuery
},
isTabNeedName (queryTab) {
const isFromScratch = !queryTab.initName
return isFromScratch || queryTab.isPredefined
},
save (queryTab, newName) {
const value = {
id: queryTab.isPredefined ? nanoid() : queryTab.id,
query: queryTab.query,
chart: queryTab.getChartSatateForSave(),
name: newName || queryTab.initName
}
const isNeedName = this.isTabNeedName(queryTab)
// Get queries from local storage
let myQueries = this.getStoredQueries()
// Set createdAt
if (isNeedName) {
value.createdAt = new Date()
} else {
var queryIndex = myQueries.findIndex(oldQuery => oldQuery.id === queryTab.id)
value.createdAt = myQueries[queryIndex].createdAt
}
// Insert in queries list
if (!myQueries) {
myQueries = [value]
} else if (isNeedName) {
myQueries.push(value)
} else {
myQueries[queryIndex] = value
}
// Save to local storage
this.updateStorage(myQueries)
return value
},
updateStorage (value) {
localStorage.setItem('myQueries', JSON.stringify(value))
},
export (data) {
// Create downloader
const downloader = document.createElement('a')
downloader.style.display = 'none'
document.body.appendChild(downloader)
// Prepare data
const name = data.name || 'My sqlitevis queries'
const json = JSON.stringify(data, null, 4)
const blob = new Blob([json], { type: 'octet/stream' })
const url = window.URL.createObjectURL(blob)
downloader.href = url
downloader.download = `${name}.json`
// Trigger click
downloader.click()
// Clear
window.URL.revokeObjectURL(url)
downloader.remove()
}
}

View File

@@ -153,7 +153,6 @@
<button class="primary" @click="deleteQuery">Delete</button> <button class="primary" @click="deleteQuery">Delete</button>
</div> </div>
</modal> </modal>
<a ref="downloader" />
</div> </div>
</template> </template>
@@ -167,6 +166,7 @@ import TextField from '@/components/TextField'
import CheckBox from '@/components/CheckBox' import CheckBox from '@/components/CheckBox'
import tooltipMixin from '@/mixins/tooltips' import tooltipMixin from '@/mixins/tooltips'
import { nanoid } from 'nanoid' import { nanoid } from 'nanoid'
import storedQueries from '@/storedQueries'
export default { export default {
name: 'MyQueries', name: 'MyQueries',
@@ -223,7 +223,7 @@ export default {
} }
}, },
created () { created () {
this.queries = JSON.parse(localStorage.getItem('myQueries')) || [] this.queries = storedQueries.getStoredQueries()
}, },
mounted () { mounted () {
this.resizeObserver = new ResizeObserver(this.calcMaxTableHeight) this.resizeObserver = new ResizeObserver(this.calcMaxTableHeight)
@@ -271,9 +271,10 @@ export default {
openQuery (index) { openQuery (index) {
const tab = JSON.parse(JSON.stringify(this.showedQueries[index])) const tab = JSON.parse(JSON.stringify(this.showedQueries[index]))
tab.isUnsaved = false tab.isUnsaved = false
this.$store.commit('addTab', tab) this.$store.dispatch('addTab', tab).then(id => {
this.$store.commit('setCurrentTabId', tab.id) this.$store.commit('setCurrentTabId', id)
this.$router.push('/editor') this.$router.push('/editor')
})
}, },
showRenameDialog (id) { showRenameDialog (id) {
this.errorMsg = null this.errorMsg = null
@@ -291,7 +292,7 @@ export default {
this.$set(this.queries, this.currentQueryIndex, currentQuery) this.$set(this.queries, this.currentQueryIndex, currentQuery)
// update queries in local storage // update queries in local storage
this.saveQueriesInLocalStorage() storedQueries.updateStorage(this.queries)
// update tab, if renamed query is opened // update tab, if renamed query is opened
const tabIndex = this.findTabIndex(currentQuery.id) const tabIndex = this.findTabIndex(currentQuery.id)
@@ -306,17 +307,13 @@ export default {
this.$modal.hide('rename') this.$modal.hide('rename')
}, },
duplicateQuery (index) { duplicateQuery (index) {
const newQuery = JSON.parse(JSON.stringify(this.showedQueries[index])) const newQuery = storedQueries.duplicateQuery(this.showedQueries[index])
newQuery.name = newQuery.name + ' Copy'
newQuery.id = nanoid()
newQuery.createdAt = new Date()
delete newQuery.isPredefined
if (this.selectAll) { if (this.selectAll) {
this.selectedQueriesIds.add(newQuery.id) this.selectedQueriesIds.add(newQuery.id)
this.selectedQueriesCount = this.selectedQueriesIds.size this.selectedQueriesCount = this.selectedQueriesIds.size
} }
this.queries.push(newQuery) this.queries.push(newQuery)
this.saveQueriesInLocalStorage() storedQueries.updateStorage(this.queries)
}, },
showDeleteDialog (id) { showDeleteDialog (id) {
this.deleteGroup = typeof id !== 'string' this.deleteGroup = typeof id !== 'string'
@@ -349,38 +346,28 @@ export default {
this.selectedQueriesIds.clear() this.selectedQueriesIds.clear()
} }
this.selectedQueriesCount = this.selectedQueriesIds.size this.selectedQueriesCount = this.selectedQueriesIds.size
this.saveQueriesInLocalStorage() storedQueries.updateStorage(this.queries)
}, },
findTabIndex (id) { findTabIndex (id) {
return this.$store.state.tabs.findIndex(tab => tab.id === id) return this.$store.state.tabs.findIndex(tab => tab.id === id)
}, },
exportQuery (index) { exportQuery (index) {
let data let data
let name
// single operation // single operation
if (typeof index === 'number') { if (typeof index === 'number') {
data = JSON.parse(JSON.stringify(this.showedQueries[index])) data = JSON.parse(JSON.stringify(this.showedQueries[index]))
name = data.name
delete data.isPredefined delete data.isPredefined
} else { } else {
// group operation // group operation
data = this.selectAll data = this.selectAll
? JSON.parse(JSON.stringify(this.allQueries)) ? JSON.parse(JSON.stringify(this.allQueries))
: this.allQueries.filter(query => this.selectedQueriesIds.has(query.id)) : this.allQueries.filter(query => this.selectedQueriesIds.has(query.id))
name = 'My sqliteviz queries'
data.forEach(query => delete query.isPredefined) data.forEach(query => delete query.isPredefined)
} }
// export data to file // export data to file
const downloader = this.$refs.downloader storedQueries.export(data)
const json = JSON.stringify(data, null, 4)
const blob = new Blob([json], { type: 'octet/stream' })
const url = window.URL.createObjectURL(blob)
downloader.href = url
downloader.download = `${name}.json`
downloader.click()
window.URL.revokeObjectURL(url)
}, },
importQueries () { importQueries () {
const file = this.$refs.importFile.files[0] const file = this.$refs.importFile.files[0]
@@ -407,14 +394,11 @@ export default {
} }
this.queries = this.queries.concat(importedQueries) this.queries = this.queries.concat(importedQueries)
this.saveQueriesInLocalStorage() storedQueries.updateStorage(this.queries)
this.$refs.importFile.value = null this.$refs.importFile.value = null
} }
reader.readAsText(file) reader.readAsText(file)
}, },
saveQueriesInLocalStorage () {
localStorage.setItem('myQueries', JSON.stringify(this.queries))
},
toggleSelectAll (checked) { toggleSelectAll (checked) {
this.selectAll = checked this.selectAll = checked
this.$refs.rowCheckBox.forEach(item => { item.checked = checked }) this.$refs.rowCheckBox.forEach(item => { item.checked = checked })
@@ -556,7 +540,7 @@ tbody tr:hover .icons-container {
.dialog input { .dialog input {
width: 100%; width: 100%;
} }
a, #import-file { #import-file {
display: none; display: none;
} }
button.toolbar { button.toolbar {

View File

@@ -1,8 +1,7 @@
import { expect } from 'chai' import { expect } from 'chai'
import { mutations } from '@/store' import { mutations, actions } from '@/store'
const { const {
saveSchema, saveSchema,
addTab,
updateTab, updateTab,
deleteTab, deleteTab,
setCurrentTabId, setCurrentTabId,
@@ -10,6 +9,8 @@ const {
updatePredefinedQueries updatePredefinedQueries
} = mutations } = mutations
const { addTab } = actions
describe('mutations', () => { describe('mutations', () => {
it('saveSchema', () => { it('saveSchema', () => {
// mock state // mock state
@@ -28,75 +29,6 @@ describe('mutations', () => {
expect(state.schema).to.eql(schema) expect(state.schema).to.eql(schema)
}) })
it('addTab (new)', () => {
// mock state
const state = {
tabs: [],
untitledLastIndex: 0
}
const tab = {
id: 1,
name: null,
tempName: 'Untitled',
query: 'SELECT * from foo',
chart: {},
isUnsaved: true
}
addTab(state, tab)
expect(state.tabs[0]).to.eql(tab)
expect(state.untitledLastIndex).to.equal(1)
})
it('addTab (saved)', () => {
// mock state
const state = {
tabs: [],
untitledLastIndex: 0
}
const tab = {
id: 1,
name: 'test',
tempName: null,
query: 'SELECT * from foo',
chart: {},
isUnsaved: false
}
addTab(state, tab)
expect(state.tabs[0]).to.eql(tab)
expect(state.untitledLastIndex).to.equal(0)
})
it('addTab (existed)', () => {
const tab1 = {
id: 1,
name: 'test',
tempName: null,
query: 'SELECT * from foo',
chart: {},
isUnsaved: false
}
const tab2 = {
id: 2,
name: 'bar',
tempName: null,
query: 'SELECT * from bar',
chart: {},
isUnsaved: false
}
// mock state
const state = {
tabs: [ tab1, tab2 ],
untitledLastIndex: 0,
}
addTab(state, tab1)
expect(state.tabs.length).to.equal(2)
expect(state.untitledLastIndex).to.equal(0)
})
it('updateTab (save)', () => { it('updateTab (save)', () => {
const tab = { const tab = {
id: 1, id: 1,
@@ -438,4 +370,71 @@ describe('mutations', () => {
updatePredefinedQueries(state, queries) updatePredefinedQueries(state, queries)
expect(state.predefinedQueries).to.eql(queries) expect(state.predefinedQueries).to.eql(queries)
}) })
})
describe('actions', () => {
it('addTab (new)', async () => {
// mock state
const state = {
tabs: [],
untitledLastIndex: 0
}
const id = await addTab({ state })
expect(state.tabs[0].id).to.eql(id)
expect(state.tabs[0].name).to.eql(null)
expect(state.tabs[0].tempName).to.eql('Untitled')
expect(state.tabs[0].isUnsaved).to.eql(true)
expect(state.untitledLastIndex).to.equal(1)
})
it('addTab (saved)', async () => {
// mock state
const state = {
tabs: [],
untitledLastIndex: 0
}
const tab = {
id: 1,
name: 'test',
tempName: null,
query: 'SELECT * from foo',
chart: {},
isUnsaved: false
}
await addTab({ state }, tab)
expect(state.tabs[0]).to.eql(tab)
expect(state.untitledLastIndex).to.equal(0)
})
it('addTab (existed)', async () => {
const tab1 = {
id: 1,
name: 'test',
tempName: null,
query: 'SELECT * from foo',
chart: {},
isUnsaved: false
}
const tab2 = {
id: 2,
name: 'bar',
tempName: null,
query: 'SELECT * from bar',
chart: {},
isUnsaved: false
}
// mock state
const state = {
tabs: [ tab1, tab2 ],
untitledLastIndex: 0,
}
await addTab({ state }, tab1)
expect(state.tabs.length).to.equal(2)
expect(state.untitledLastIndex).to.equal(0)
})
}) })