1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-07 22:35:36 +00:00

Resolve "Web-frontend results into error when switching rapidly between tables"

This commit is contained in:
Nigel Gott 2021-09-02 08:22:31 +00:00
parent 3d56805128
commit fd62565f86
4 changed files with 108 additions and 59 deletions
changelog.md
web-frontend
modules/database/components/table
package.jsonyarn.lock

View file

@ -11,6 +11,8 @@
first option with that value.
* The API now returns appropriate errors when trying to create a field with a name which is too long.
* Importing table data with a column name that is too long will now truncate that name.
* Fixed error when rapidly switching between template tables or views in the template
preview.
## Released (2021-08-11)

View file

@ -9,7 +9,7 @@
:table-loading="tableLoading"
:read-only="true"
store-prefix="template/"
@selected-view="selectView($event.id)"
@selected-view="selectView({ viewId: $event.id })"
></Table>
</template>
@ -20,6 +20,7 @@ import FieldService from '@baserow/modules/database/services/field'
import { populateField } from '@baserow/modules/database/store/field'
import ViewService from '@baserow/modules/database/services/view'
import { populateView } from '@baserow/modules/database/store/view'
import { Mutex } from 'async-mutex'
export default {
name: 'TableTemplate',
@ -37,9 +38,9 @@ export default {
fields: [],
primary: {},
views: [],
selectedViewId: null,
view: {},
tableLoading: true,
mutex: new Mutex(),
}
},
watch: {
@ -51,78 +52,111 @@ export default {
this.fetchTable(this.pageValue.database, this.pageValue.table)
},
methods: {
async runExclusiveUnlessAlreadyLocked({ callback, alreadyLocked = false }) {
if (alreadyLocked) {
return await callback()
} else {
return await this.mutex.runExclusive(callback)
}
},
/**
* Fetches and prepares all the table, field and view data for the provided
* database and table.
*/
async fetchTable(database, table) {
this.tableLoading = true
this.database = database
this.table = table
await this.runExclusiveUnlessAlreadyLocked({
callback: async () => {
// If the new table is already selected, then we don't need to do anything.
if (table.id === this.table.id) {
return
}
// Fetch and prepare the fields of the given table. The primary field is
// extracted from the array and moved to a separate object because that is what
// the `Table` components expects.
const { data: fieldsData } = await FieldService(this.$client).fetchAll(
table.id
)
fieldsData.forEach((part, index, d) => {
populateField(fieldsData[index], this.$registry)
this.tableLoading = true
this.database = database
this.table = table
// Fetch and prepare the fields of the given table. The primary field is
// extracted from the array and moved to a separate object because that is what
// the `Table` components expects.
const { data: fieldsData } = await FieldService(
this.$client
).fetchAll(table.id)
fieldsData.forEach((part, index, d) => {
populateField(fieldsData[index], this.$registry)
})
const primaryIndex = fieldsData.findIndex(
(item) => item.primary === true
)
const primary =
primaryIndex !== -1 ? fieldsData.splice(primaryIndex, 1)[0] : null
this.fields = fieldsData
this.primary = primary
// Fetch and prepare the views of the given table.
const { data: viewsData } = await ViewService(this.$client).fetchAll(
table.id,
true,
true
)
viewsData.forEach((part, index, d) => {
populateView(viewsData[index], this.$registry)
})
this.views = viewsData
// After selecting the table, the user expects to see the table data and that is
// only possible if a view is selected. By calling the `selectView` method
// without parameters, the first view is selected.
await this.selectView({ alreadyLocked: true })
},
})
const primaryIndex = fieldsData.findIndex((item) => item.primary === true)
const primary =
primaryIndex !== -1 ? fieldsData.splice(primaryIndex, 1)[0] : null
this.fields = fieldsData
this.primary = primary
// Fetch and prepare the views of the given table.
const { data: viewsData } = await ViewService(this.$client).fetchAll(
table.id,
true,
true
)
viewsData.forEach((part, index, d) => {
populateView(viewsData[index], this.$registry)
})
this.views = viewsData
// After selecting the table, the user expects to see the table data and that is
// only possible if a view is selected. By calling the `selectView` method
// without parameters, the first view is selected.
await this.selectView()
},
/**
* Selects the view with the given `viewId`. If no `viewId` is provided, then the
* first view will be selected.
*/
async selectView(viewId = null) {
this.tableLoading = true
// If no viewId is provided, we want to select the first the first view.
const firstView = this.views.length > 0 ? this.views[0] : null
if (viewId === null && firstView !== null) {
viewId = firstView.id
async selectView({ viewId = null, alreadyLocked = false }) {
// If the new view is already selected, we don't need to do anything.
if (viewId === this.view.id) {
return
}
// Update the selected state in all the views.
this.views.forEach((view) => {
view._.selected = view.id === viewId
await this.runExclusiveUnlessAlreadyLocked({
alreadyLocked,
callback: async () => {
// If the new view is already selected, we don't need to do anything.
if (viewId === this.view.id) {
return
}
this.tableLoading = true
// If no viewId is provided, we want to select the first the first view.
const firstView = this.views.length > 0 ? this.views[0] : null
if (viewId === null && firstView !== null) {
viewId = firstView.id
}
// Update the selected state in all the views.
this.views.forEach((view) => {
view._.selected = view.id === viewId
})
const view = this.views.find((item) => item.id === viewId)
this.view = view
// It might be possible that the view also has some stores that need to be
// filled with initial data, so we're going to call the fetch function here.
const type = this.$registry.get('view', view.type)
await type.fetch(
{ store: this.$store },
view,
this.fields,
this.primary,
'template/'
)
this.tableLoading = false
},
})
const view = this.views.find((item) => item.id === viewId)
this.view = view
// It might be possible that the view also has some stores that need to be
// filled with initial data, so we're going to call the fetch function here.
const type = this.$registry.get('view', view.type)
await type.fetch(
{ store: this.$store },
view,
this.fields,
this.primary,
'template/'
)
this.tableLoading = false
},
},
}

View file

@ -19,6 +19,7 @@
"@fortawesome/fontawesome-free": "^5.15.3",
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/i18n": "^7.0.1",
"async-mutex": "^0.3.1",
"axios": "^0.21.0",
"bignumber.js": "^9.0.1",
"chart.js": "2.9.4",

View file

@ -2515,6 +2515,13 @@ async-foreach@^0.1.3:
resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=
async-mutex@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.1.tgz#7033af665f1c7cebed8b878267a43ba9e77c5f67"
integrity sha512-vRfQwcqBnJTLzVQo72Sf7KIUbcSUP5hNchx6udI1U6LuPQpfePgdjJzlCe76yFZ8pxlLjn9lwcl/Ya0TSOv0Tw==
dependencies:
tslib "^2.1.0"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -11512,6 +11519,11 @@ tslib@^2.0.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
tslib@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"