Browse Source

fix(nc-gui): project state not properly reset

# What's changed?

* try loading project when clicking on list item
    * reduces potential waiting times
* only load project if no request is running already
* move fetching project to parent page to prevent multiple loads
pull/3637/head
braks 2 years ago
parent
commit
52c311f7e3
  1. 90
      packages/nc-gui/composables/useProject.ts
  2. 4
      packages/nc-gui/composables/useViewData.ts
  3. 34
      packages/nc-gui/pages/[projectType]/[projectId]/index.vue
  4. 37
      packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue
  5. 38
      packages/nc-gui/pages/index/index/index.vue

90
packages/nc-gui/composables/useProject.ts

@ -1,31 +1,53 @@
import type { MaybeRef } from '@vueuse/core' import type { MaybeRef } from '@vueuse/core'
import { SqlUiFactory } from 'nocodb-sdk'
import type { OracleUi, ProjectType, TableType } from 'nocodb-sdk' import type { OracleUi, ProjectType, TableType } from 'nocodb-sdk'
import { useNuxtApp, useRoute } from '#app' import { SqlUiFactory } from 'nocodb-sdk'
import type { ProjectMetaInfo } from '~/lib' import { isString } from '@vueuse/core'
import {
USER_PROJECT_ROLES,
computed,
createEventHook,
ref,
useApi,
useGlobal,
useInjectionState,
useNuxtApp,
useRoute,
useState,
useTheme,
watch,
} from '#imports'
import type { ProjectMetaInfo, Roles } from '~/lib'
import type { ThemeConfig } from '@/composables/useTheme' import type { ThemeConfig } from '@/composables/useTheme'
import { createEventHook, useInjectionState } from '#imports'
const [setup, use] = useInjectionState((_projectId?: MaybeRef<string>) => { const [setup, use] = useInjectionState((_projectId?: MaybeRef<string>) => {
const { $api, $e } = useNuxtApp() const { $e } = useNuxtApp()
const { api, isLoading } = useApi()
const route = useRoute() const route = useRoute()
const { includeM2M } = useGlobal() const { includeM2M } = useGlobal()
const { setTheme, theme } = useTheme() const { setTheme, theme } = useTheme()
const projectLoadedHook = createEventHook<ProjectType>() const projectLoadedHook = createEventHook<ProjectType>()
const projectId = computed(() => (_projectId ? unref(_projectId) : (route.params.projectId as string)))
const project = ref<ProjectType>({}) const project = ref<ProjectType>({})
const tables = ref<TableType[]>([]) const tables = ref<TableType[]>([])
const projectRoles = useState<Record<string, boolean>>(USER_PROJECT_ROLES, () => ({}))
const projectRoles = useState<Roles>(USER_PROJECT_ROLES, () => ({}))
const projectMetaInfo = ref<ProjectMetaInfo | undefined>() const projectMetaInfo = ref<ProjectMetaInfo | undefined>()
const projectId = computed(() => (_projectId ? unref(_projectId) : (route.params.projectId as string)))
// todo: refactor path param name and variable name // todo: refactor path param name and variable name
const projectType = $computed(() => route.params.projectType as string) const projectType = $computed(() => route.params.projectType as string)
const projectMeta = computed(() => { const projectMeta = computed<Record<string, any>>(() => {
try { try {
return typeof project.value.meta === 'string' ? JSON.parse(project.value.meta) : project.value.meta return project.value.meta && isString(project.value.meta) ? JSON.parse(project.value.meta) : project.value.meta
} catch (e) { } catch (e) {
return {} return {}
} }
@ -44,15 +66,15 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef<string>) => {
async function loadProjectMetaInfo(force?: boolean) { async function loadProjectMetaInfo(force?: boolean) {
if (!projectMetaInfo.value || force) { if (!projectMetaInfo.value || force) {
const data = await $api.project.metaGet(project.value.id!, {}, {}) projectMetaInfo.value = await api.project.metaGet(project.value.id!, {}, {})
projectMetaInfo.value = data
} }
} }
async function loadProjectRoles() { async function loadProjectRoles() {
projectRoles.value = {} projectRoles.value = {}
if (isSharedBase.value) { if (isSharedBase.value) {
const user = await $api.auth.me( const user = await api.auth.me(
{}, {},
{ {
headers: { headers: {
@ -60,33 +82,40 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef<string>) => {
}, },
}, },
) )
projectRoles.value = user.roles projectRoles.value = user.roles
} else if (project.value.id) { } else if (project.value.id) {
const user = await $api.auth.me({ project_id: project.value.id }) const user = await api.auth.me({ project_id: project.value.id })
projectRoles.value = user.roles projectRoles.value = user.roles
} }
} }
async function loadTables() { async function loadTables() {
if (project.value.id) { if (project.value.id) {
const tablesResponse = await $api.dbTable.list(project.value.id, { const tablesResponse = await api.dbTable.list(project.value.id, {
includeM2M: includeM2M.value, includeM2M: includeM2M.value,
}) })
if (tablesResponse.list) tables.value = tablesResponse.list if (tablesResponse.list) tables.value = tablesResponse.list
} }
} }
async function loadProject() { async function loadProject(id?: string) {
if (projectType === 'base') { if (id) {
const baseData = await $api.public.sharedBaseGet(route.params.projectId as string) project.value = await api.project.read(projectId.value)
project.value = await $api.project.read(baseData.project_id!) } else if (projectType === 'base') {
const baseData = await api.public.sharedBaseGet(route.params.projectId as string)
project.value = await api.project.read(baseData.project_id!)
} else if (projectId.value) { } else if (projectId.value) {
project.value = await $api.project.read(projectId.value) project.value = await api.project.read(projectId.value)
} else { } else {
return return
} }
await loadProjectRoles() await loadProjectRoles()
await loadTables() await loadTables()
setTheme(projectMeta.value?.theme) setTheme(projectMeta.value?.theme)
projectLoadedHook.trigger(project.value) projectLoadedHook.trigger(project.value)
@ -97,15 +126,13 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef<string>) => {
return return
} }
if (data.meta && typeof data.meta === 'string') { if (data.meta && typeof data.meta === 'string') {
await $api.project.update(projectId.value, data) await api.project.update(projectId.value, data)
} else { } else {
await $api.project.update(projectId.value, { ...data, meta: JSON.stringify(data.meta) }) await api.project.update(projectId.value, { ...data, meta: JSON.stringify(data.meta) })
} }
} }
async function saveTheme(_theme: Partial<ThemeConfig>) { async function saveTheme(_theme: Partial<ThemeConfig>) {
$e('c:themes:change')
const fullTheme = { const fullTheme = {
primaryColor: theme.value.primaryColor, primaryColor: theme.value.primaryColor,
accentColor: theme.value.accentColor, accentColor: theme.value.accentColor,
@ -119,25 +146,30 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef<string>) => {
theme: fullTheme, theme: fullTheme,
}, },
}) })
setTheme(fullTheme) setTheme(fullTheme)
$e('c:themes:change')
} }
watch( watch(
() => route.params, () => route.params,
(v) => { (next) => {
if (!v?.projectId) { if (!next.projectId) {
setTheme() setTheme()
} }
}, },
) )
// TODO useProject should only called inside a project for now this doesn't work const reset = () => {
onScopeDispose(() => {
project.value = {} project.value = {}
tables.value = [] tables.value = []
projectMetaInfo.value = undefined projectMetaInfo.value = undefined
projectRoles.value = {} projectRoles.value = {}
}) }
// TODO useProject should only called inside a project for now this doesn't work
// onScopeDispose(reset)
return { return {
project, project,
@ -156,6 +188,8 @@ const [setup, use] = useInjectionState((_projectId?: MaybeRef<string>) => {
projectMeta, projectMeta,
saveTheme, saveTheme,
projectLoadedHook: projectLoadedHook.on, projectLoadedHook: projectLoadedHook.on,
reset,
isLoading,
} }
}, 'useProject') }, 'useProject')

4
packages/nc-gui/composables/useViewData.ts

@ -1,8 +1,6 @@
import type { Api, ColumnType, FormType, GalleryType, PaginatedType, TableType, ViewType } from 'nocodb-sdk' import type { Api, ColumnType, FormType, GalleryType, PaginatedType, TableType, ViewType } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue' import type { ComputedRef, Ref } from 'vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import { useNuxtApp } from '#app'
import { import {
IsPublicInj, IsPublicInj,
NOCO, NOCO,
@ -10,6 +8,8 @@ import {
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
getHTMLEncodedText, getHTMLEncodedText,
useApi, useApi,
useI18n,
useNuxtApp,
useProject, useProject,
useUIPermission, useUIPermission,
} from '#imports' } from '#imports'

34
packages/nc-gui/pages/[projectType]/[projectId]/index.vue

@ -5,6 +5,8 @@ import {
computed, computed,
definePageMeta, definePageMeta,
navigateTo, navigateTo,
onBeforeMount,
onBeforeUnmount,
onKeyStroke, onKeyStroke,
openLink, openLink,
projectThemeColors, projectThemeColors,
@ -16,6 +18,7 @@ import {
useI18n, useI18n,
useProject, useProject,
useRoute, useRoute,
useRouter,
useTabs, useTabs,
useUIPermission, useUIPermission,
} from '#imports' } from '#imports'
@ -29,9 +32,20 @@ const { t } = useI18n()
const route = useRoute() const route = useRoute()
const router = useRouter()
const { appInfo, token, signOut, signedIn, user } = useGlobal() const { appInfo, token, signOut, signedIn, user } = useGlobal()
const { projectLoadedHook, project, isSharedBase, loadProjectMetaInfo, projectMetaInfo, saveTheme } = useProject() const {
project,
isSharedBase,
loadProjectMetaInfo,
projectMetaInfo,
saveTheme,
isLoading: isLoadingProject,
loadProject,
reset,
} = useProject()
const { clearTabs, addTab } = useTabs() const { clearTabs, addTab } = useTabs()
@ -114,7 +128,7 @@ const copyProjectInfo = async () => {
// Copied to clipboard // Copied to clipboard
message.info(t('msg.info.copiedToClipboard')) message.info(t('msg.info.copiedToClipboard'))
} catch (e: any) { } catch (e: any) {
console.log(e) console.error(e)
message.error(e.message) message.error(e.message)
} }
} }
@ -125,7 +139,7 @@ const copyAuthToken = async () => {
// Copied to clipboard // Copied to clipboard
message.info(t('msg.info.copiedToClipboard')) message.info(t('msg.info.copiedToClipboard'))
} catch (e: any) { } catch (e: any) {
console.log(e) console.error(e)
message.error(e.message) message.error(e.message)
} }
} }
@ -140,11 +154,23 @@ onKeyStroke(
clearTabs() clearTabs()
projectLoadedHook(() => { onBeforeMount(async () => {
if (!isLoadingProject.value) {
await loadProject()
}
if (!route.params.type && isUIAllowed('teamAndAuth')) { if (!route.params.type && isUIAllowed('teamAndAuth')) {
addTab({ type: TabType.AUTH, title: t('title.teamAndAuth') }) addTab({ type: TabType.AUTH, title: t('title.teamAndAuth') })
} }
/** If v1 url found navigate to corresponding new url */
const { type, name, view } = route.query
if (type && name) {
await router.replace(`/nc/${route.params.projectId}/${type}/${name}${view ? `/${view}` : ''}`)
}
}) })
onBeforeUnmount(reset)
</script> </script>
<template> <template>

37
packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue

@ -1,48 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TabItem } from '~/composables' import type { TabItem } from '~/composables'
import { TabType } from '~/composables' import { TabType } from '~/composables'
import { import { TabMetaInj, provide, useGlobal, useProject, useSidebar, useTabs } from '#imports'
TabMetaInj,
onBeforeMount,
provide,
ref,
useGlobal,
useProject,
useRoute,
useRouter,
useSidebar,
useTabs,
} from '#imports'
import MdiAirTableIcon from '~icons/mdi/table-large' import MdiAirTableIcon from '~icons/mdi/table-large'
import MdiView from '~icons/mdi/eye-circle-outline' import MdiView from '~icons/mdi/eye-circle-outline'
import MdiAccountGroup from '~icons/mdi/account-group' import MdiAccountGroup from '~icons/mdi/account-group'
const { project, loadProject, loadTables } = useProject() const { isLoading: isLoadingProject } = useProject()
const { tabs, activeTabIndex, activeTab, closeTab } = useTabs() const { tabs, activeTabIndex, activeTab, closeTab } = useTabs()
const { isLoading } = useGlobal() const { isLoading } = useGlobal()
const route = useRoute()
const router = useRouter()
const isReady = ref(false)
onBeforeMount(async () => {
if (!Object.keys(project.value).length) await loadProject()
/** If v1 url found navigate to corresponding new url */
const { type, name, view } = route.query
if (type && name) {
await router.replace(`/nc/${route.params.projectId}/${type}/${name}${view ? `/${view}` : ''}`)
}
await loadTables()
isReady.value = true
})
provide(TabMetaInj, activeTab) provide(TabMetaInj, activeTab)
const icon = (tab: TabItem) => { const icon = (tab: TabItem) => {
@ -110,7 +79,7 @@ function onEdit(targetKey: number, action: 'add' | 'remove' | string) {
</div> </div>
<div class="w-full min-h-[300px] flex-auto"> <div class="w-full min-h-[300px] flex-auto">
<NuxtPage v-if="isReady" /> <NuxtPage v-if="!isLoadingProject" />
<div v-else class="w-full h-full flex justify-center items-center"> <div v-else class="w-full h-full flex justify-center items-center">
<a-spin size="large" /> <a-spin size="large" />

38
packages/nc-gui/pages/index/index/index.vue

@ -9,8 +9,10 @@ import {
navigateTo, navigateTo,
projectThemeColors, projectThemeColors,
ref, ref,
themeV2Colors,
useApi, useApi,
useNuxtApp, useNuxtApp,
useProject,
useSidebar, useSidebar,
useUIPermission, useUIPermission,
} from '#imports' } from '#imports'
@ -27,6 +29,8 @@ const { isUIAllowed } = useUIPermission()
useSidebar({ hasSidebar: true, isOpen: true }) useSidebar({ hasSidebar: true, isOpen: true })
const { loadProject } = useProject()
const filterQuery = ref('') const filterQuery = ref('')
const projects = ref<ProjectType[]>() const projects = ref<ProjectType[]>()
@ -70,10 +74,14 @@ await loadProjects()
const handleProjectColor = async (projectId: string, color: string) => { const handleProjectColor = async (projectId: string, color: string) => {
const tcolor = tinycolor(color) const tcolor = tinycolor(color)
if (tcolor.isValid()) { if (tcolor.isValid()) {
const complement = tcolor.complement() const complement = tcolor.complement()
const project: ProjectType = await $api.project.read(projectId) const project: ProjectType = await $api.project.read(projectId)
const meta = project?.meta && typeof project.meta === 'string' ? JSON.parse(project.meta) : project.meta || {} const meta = project?.meta && typeof project.meta === 'string' ? JSON.parse(project.meta) : project.meta || {}
await $api.project.update(projectId, { await $api.project.update(projectId, {
color, color,
meta: JSON.stringify({ meta: JSON.stringify({
@ -84,8 +92,10 @@ const handleProjectColor = async (projectId: string, color: string) => {
}, },
}), }),
}) })
// Update local project // Update local project
const localProject = projects.value?.find((p) => p.id === projectId) const localProject = projects.value?.find((p) => p.id === projectId)
if (localProject) { if (localProject) {
localProject.color = color localProject.color = color
localProject.meta = JSON.stringify({ localProject.meta = JSON.stringify({
@ -100,9 +110,23 @@ const handleProjectColor = async (projectId: string, color: string) => {
} }
const getProjectPrimary = (project: ProjectType) => { const getProjectPrimary = (project: ProjectType) => {
const meta = project?.meta && typeof project.meta === 'string' ? JSON.parse(project.meta) : project.meta || {} if (!project) return
return meta?.theme?.primaryColor || themeV2Colors['royal-blue'].DEFAULT
const meta = project.meta && typeof project.meta === 'string' ? JSON.parse(project.meta) : project.meta || {}
return meta.theme?.primaryColor || themeV2Colors['royal-blue'].DEFAULT
} }
const customRow = (record: ProjectType) => ({
onClick: async () => {
await navigateTo(`/nc/${record.id}`)
$e('a:project:open')
await loadProject(record.id)
},
class: ['group'],
})
</script> </script>
<template> <template>
@ -183,15 +207,7 @@ const getProjectPrimary = (project: ProjectType) => {
<a-table <a-table
v-else v-else
key="table" key="table"
:custom-row=" :custom-row="customRow"
(record) => ({
onClick: () => {
navigateTo(`/nc/${record.id}`)
$e('a:project:open')
},
class: ['group'],
})
"
:data-source="filteredProjects" :data-source="filteredProjects"
:pagination="{ position: ['bottomCenter'] }" :pagination="{ position: ['bottomCenter'] }"
> >

Loading…
Cancel
Save