diff --git a/packages/nc-gui-v2/composables/useInjectionState/index.ts b/packages/nc-gui-v2/composables/useInjectionState/index.ts index d96955c07f..6fdb541cb3 100644 --- a/packages/nc-gui-v2/composables/useInjectionState/index.ts +++ b/packages/nc-gui-v2/composables/useInjectionState/index.ts @@ -1,4 +1,5 @@ import type { InjectionKey } from 'vue' +import { inject, provide, tryOnScopeDispose } from '#imports' export function useInjectionState( composable: (...args: Arguments) => Return, @@ -6,15 +7,31 @@ export function useInjectionState( ): readonly [useInjectionState: (...args: Arguments) => Return, useInjectedState: () => Return | undefined] { const key: string | InjectionKey = Symbol(keyName) + let providableState: Return | undefined + const useProvidingState = (...args: Arguments) => { const providedState = composable(...args) provide(key, providedState) + providableState = providedState + return providedState } - const useInjectedState = () => inject(key, undefined) + const useInjectedState = () => { + let injection = inject(key, undefined) + + if (typeof injection === 'undefined') { + injection = providableState + } + + return injection + } + + tryOnScopeDispose(() => { + providableState = undefined + }) return [useProvidingState, useInjectedState] } diff --git a/packages/nc-gui-v2/composables/useProject.ts b/packages/nc-gui-v2/composables/useProject.ts index f9128e9186..05206a32e7 100644 --- a/packages/nc-gui-v2/composables/useProject.ts +++ b/packages/nc-gui-v2/composables/useProject.ts @@ -1,34 +1,25 @@ +import type { MaybeRef } from '@vueuse/core' import { SqlUiFactory } from 'nocodb-sdk' import type { OracleUi, ProjectType, TableType } from 'nocodb-sdk' -import type { MaybeRef } from '@vueuse/core' -import { useNuxtApp, useRoute, useState } from '#app' +import { useNuxtApp, useRoute } from '#app' import type { ProjectMetaInfo } from '~/lib' -import { USER_PROJECT_ROLES } from '~/lib' import type { ThemeConfig } from '@/composables/useTheme' +import { useInjectionState } from '#imports' -export function useProject(projectId?: MaybeRef) { - const projectRoles = useState>(USER_PROJECT_ROLES, () => ({})) +const [setup, use] = useInjectionState((_projectId?: MaybeRef) => { const { $api } = useNuxtApp() - let _projectId = $ref('') - - const project = useState('project') - const tables = useState('tables', () => [] as TableType[]) const route = useRoute() const { includeM2M } = useGlobal() const { setTheme } = useTheme() - const projectMetaInfo = useState('projectMetaInfo') + + const projectId = computed(() => (_projectId ? unref(_projectId) : (route.params.projectId as string))) + const project = ref({}) + const tables = ref([]) + const projectRoles = ref>({}) + const projectMetaInfo = ref() + // todo: refactor path param name and variable name const projectType = $computed(() => route.params.projectType as string) - const isLoaded = ref(false) - - const projectBaseType = $computed(() => project.value?.bases?.[0]?.type || '') - const isMysql = computed(() => ['mysql', 'mysql2'].includes(projectBaseType)) - const isMssql = computed(() => projectBaseType === 'mssql') - const isPg = computed(() => projectBaseType === 'pg') - const sqlUi = computed( - () => SqlUiFactory.create({ client: projectBaseType }) as Exclude, typeof OracleUi>, - ) - const isSharedBase = computed(() => projectType === 'base') const projectMeta = computed(() => { try { @@ -38,6 +29,17 @@ export function useProject(projectId?: MaybeRef) { } }) + const projectBaseType = $computed(() => project.value?.bases?.[0]?.type || '') + + const sqlUi = computed( + () => SqlUiFactory.create({ client: projectBaseType }) as Exclude, typeof OracleUi>, + ) + + const isMysql = computed(() => ['mysql', 'mysql2'].includes(projectBaseType)) + const isMssql = computed(() => projectBaseType === 'mssql') + const isPg = computed(() => projectBaseType === 'pg') + const isSharedBase = computed(() => projectType === 'base') + async function loadProjectMetaInfo(force?: boolean) { if (!projectMetaInfo.value || force) { const data = await $api.project.metaGet(project.value.id!, {}, {}) @@ -74,51 +76,54 @@ export function useProject(projectId?: MaybeRef) { } async function loadProject() { - if (unref(projectId)) { - _projectId = unref(projectId)! - } else if (projectType === 'base') { + if (projectType === 'base') { const baseData = await $api.public.sharedBaseGet(route.params.projectId as string) - _projectId = baseData.project_id! + project.value = await $api.project.read(baseData.project_id!) } else { - _projectId = route.params.projectId as string + project.value = await $api.project.read(projectId.value) } - isLoaded.value = true - project.value = await $api.project.read(_projectId!) await loadProjectRoles() await loadTables() setTheme(projectMeta.value?.theme) } async function updateProject(data: Partial) { - if (unref(projectId)) { - _projectId = unref(projectId)! - } else if (projectType === 'base') { + if (projectType === 'base') { return + } + if (data.meta && typeof data.meta === 'string') { + await $api.project.update(projectId.value, data) } else { - _projectId = route.params.projectId as string + await $api.project.update(projectId.value, { ...data, meta: JSON.stringify(data.meta) }) } - - await $api.project.update(_projectId, data) } async function saveTheme(theme: Partial) { await updateProject({ - meta: JSON.stringify({ + color: theme.primaryColor, + meta: { ...projectMeta.value, theme, - }), + }, }) setTheme(theme) } + watch( + () => route.params, + (v) => { + if (!v?.projectId) { + setTheme() + } + }, + ) + + // TODO useProject should only called inside a project for now this doesn't work onScopeDispose(() => { - if (isLoaded.value === true) { - project.value = {} - tables.value = [] - projectMetaInfo.value = undefined - projectRoles.value = {} - setTheme({}) - } + project.value = {} + tables.value = [] + projectMetaInfo.value = undefined + projectRoles.value = {} }) return { @@ -138,4 +143,16 @@ export function useProject(projectId?: MaybeRef) { projectMeta, saveTheme, } +}, 'useProject') + +export const provideProject = setup + +export function useProject(projectId?: MaybeRef) { + const state = use() + + if (!state) { + return setup(projectId) + } + + return state } diff --git a/packages/nc-gui-v2/composables/useTheme/index.ts b/packages/nc-gui-v2/composables/useTheme/index.ts index 3ce5492322..8370380855 100644 --- a/packages/nc-gui-v2/composables/useTheme/index.ts +++ b/packages/nc-gui-v2/composables/useTheme/index.ts @@ -22,7 +22,7 @@ const [setup, use] = useInjectionState((config?: Partial) => { setTheme(config ?? currentTheme.value) /** set theme (persists in localstorage) */ - function setTheme(theme: Partial) { + function setTheme(theme?: Partial) { const themePrimary = theme?.primaryColor ? tinycolor(theme.primaryColor) : tinycolor(themeV2Colors['royal-blue'].DEFAULT) const themeAccent = theme?.accentColor ? tinycolor(theme.accentColor) : tinycolor(themeV2Colors.pink['500']) diff --git a/packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue b/packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue index bec342b083..9a9b08cd9f 100644 --- a/packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue +++ b/packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue @@ -87,21 +87,21 @@ const themePrimaryColor = ref(theme.value.primaryColor) const themeAccentColor = ref(theme.value.accentColor) -// Chrome provides object so if custom picker used we only edit primary otherwise use analogous as accent +// Chrome provides object so if custom picker used we only edit primary otherwise use complement as accent watch(themePrimaryColor, (nextColor) => { - const hexColor = nextColor.hex ? nextColor.hex : nextColor + const hexColor = nextColor.hex8 ? nextColor.hex8 : nextColor const tcolor = tinycolor(hexColor) if (tcolor) { - const analogous = tcolor.complement() + const complement = tcolor.complement() saveTheme({ primaryColor: hexColor, - accentColor: nextColor.hex ? theme.value.accentColor : analogous.toHexString(), + accentColor: nextColor.hex8 ? theme.value.accentColor : complement.toHex8String(), }) } }) watch(themeAccentColor, (nextColor) => { - const hexColor = nextColor.hex ? nextColor.hex : nextColor + const hexColor = nextColor.hex8 ? nextColor.hex8 : nextColor saveTheme({ primaryColor: theme.value.primaryColor, accentColor: hexColor, diff --git a/packages/nc-gui-v2/pages/index/index/[id].vue b/packages/nc-gui-v2/pages/index/index/[projectId].vue similarity index 68% rename from packages/nc-gui-v2/pages/index/index/[id].vue rename to packages/nc-gui-v2/pages/index/index/[projectId].vue index 9e0857d8f0..72ab27d685 100644 --- a/packages/nc-gui-v2/pages/index/index/[id].vue +++ b/packages/nc-gui-v2/pages/index/index/[projectId].vue @@ -2,7 +2,6 @@ import type { Form } from 'ant-design-vue' import { message } from 'ant-design-vue' import type { ProjectType } from 'nocodb-sdk' -import tinycolor from 'tinycolor2' import { extractSdkResponseErrorMsg, navigateTo, @@ -22,7 +21,7 @@ useSidebar({ hasSidebar: false }) const route = useRoute() -const { project, loadProject, updateProject } = useProject(route.params.id as string) +const { project, loadProject, updateProject } = useProject(route.params.projectId as string) await loadProject() @@ -38,15 +37,13 @@ const form = ref() const formState = reactive>({ title: '', - color: '#FFFFFF00', }) const renameProject = async () => { - formState.color = formState.color === '#FFFFFF00' ? '' : formState.color try { await updateProject(formState) - navigateTo(`/nc/${route.params.id}`) + navigateTo(`/nc/${route.params.projectId}`) } catch (e: any) { message.error(await extractSdkResponseErrorMsg(e)) } @@ -55,7 +52,6 @@ const renameProject = async () => { // select and focus title field on load onMounted(async () => { formState.title = project.value.title as string - formState.color = project.value.color && tinycolor(project.value.color).isValid() ? project.value.color : '#FFFFFF00' await nextTick(() => { // todo: replace setTimeout and follow better approach setTimeout(() => { @@ -98,27 +94,6 @@ onMounted(async () => { -
- Project color: - - - - - - - - -
-