Browse Source

fix: better state and cache management

pull/7202/head
mertmit 10 months ago
parent
commit
179a7bd934
  1. 20
      packages/nc-gui/components/cell/User.vue
  2. 45
      packages/nc-gui/components/project/AccessSettings.vue
  3. 4
      packages/nc-gui/components/project/View.vue
  4. 4
      packages/nc-gui/store/base.ts
  5. 34
      packages/nc-gui/store/bases.ts
  6. 3
      packages/nc-gui/store/users.ts
  7. 2
      packages/nocodb/src/models/Base.ts
  8. 67
      packages/nocodb/src/models/BaseUser.ts
  9. 18
      packages/nocodb/src/services/base-users/base-users.service.ts

20
packages/nc-gui/components/cell/User.vue

@ -45,9 +45,11 @@ const isEditable = inject(EditModeInj, ref(false))
const activeCell = inject(ActiveCellInj, ref(false))
const workspaceStore = useWorkspace()
const basesStore = useBases()
const { activeWorkspace } = storeToRefs(workspaceStore)
const { basesUser, activeProjectId } = storeToRefs(basesStore)
const baseUsers = computed(() => (activeProjectId.value ? basesUser.value.get(activeProjectId.value) || [] : []))
// use both ActiveCellInj or EditModeInj to determine the active state
// since active will be false in case of form view
@ -75,7 +77,7 @@ const options = computed<{ id: string; email: string; display_name: string }[]>(
const collaborators: { id: string; email: string; display_name: string }[] = []
collaborators.push(
...(activeWorkspace.value.collaborators?.map((user: any) => ({
...(baseUsers.value?.map((user: any) => ({
id: user.id,
email: user.email,
display_name: user.display_name,
@ -151,6 +153,11 @@ watch(isOpen, (n, _o) => {
}
})
// set isOpen to false when active cell is changed
watch(active, (n, _o) => {
if (!n) isOpen.value = false
})
useSelectedCellKeyupListener(activeCell, (e) => {
switch (e.key) {
case 'Escape':
@ -240,9 +247,10 @@ useEventListener(document, 'click', handleClose, true)
// search with email
const filterOption = (input: string, option: any) => {
const email = options.value.find((o) => o.id === option.value)?.email
if (email) {
return email.toLowerCase().includes(input.toLowerCase())
const opt = options.value.find((o) => o.id === option.value)
const searchVal = opt?.display_name || opt?.email
if (searchVal) {
return searchVal.toLowerCase().includes(input.toLowerCase())
}
}
</script>

45
packages/nc-gui/components/project/AccessSettings.vue

@ -8,11 +8,11 @@ import {
timeAgo,
} from 'nocodb-sdk'
import type { WorkspaceUserRoles } from 'nocodb-sdk'
import InfiniteLoading from 'v3-infinite-loading'
import { isEeUI, storeToRefs } from '#imports'
import { OrderedProjectRoles, OrgUserRoles, ProjectRoles, WorkspaceRolesToProjectRoles, extractRolesObj } from 'nocodb-sdk'
import { isEeUI, storeToRefs, timeAgo } from '#imports'
const basesStore = useBases()
const { getProjectUsers, createProjectUser, updateProjectUser, removeProjectUser } = basesStore
const { getBaseUsers, createProjectUser, updateProjectUser, removeProjectUser } = basesStore
const { activeProjectId } = storeToRefs(basesStore)
const { orgRoles, baseRoles } = useRoles()
@ -30,7 +30,6 @@ interface Collaborators {
const collaborators = ref<Collaborators[]>([])
const totalCollaborators = ref(0)
const userSearchText = ref('')
const currentPage = ref(0)
const isLoading = ref(false)
const isSearching = ref(false)
@ -38,18 +37,14 @@ const accessibleRoles = ref<(typeof ProjectRoles)[keyof typeof ProjectRoles][]>(
const loadCollaborators = async () => {
try {
currentPage.value += 1
const { users, totalRows } = await getProjectUsers({
const { users, totalRows } = await getBaseUsers({
baseId: activeProjectId.value!,
page: currentPage.value,
...(!userSearchText.value ? {} : ({ searchText: userSearchText.value } as any)),
limit: 20,
force: true,
})
totalCollaborators.value = totalRows
collaborators.value = [
...collaborators.value,
...users.map((user: any) => ({
...user,
base_roles: user.roles,
@ -66,24 +61,6 @@ const loadCollaborators = async () => {
}
}
const loadListData = async ($state: any) => {
const prevUsersCount = collaborators.value?.length || 0
if (collaborators.value?.length === totalCollaborators.value) {
$state.complete()
return
}
$state.loading()
// const oldPagesCount = currentPage.value || 0
await loadCollaborators()
if (prevUsersCount === collaborators.value?.length) {
$state.complete()
return
}
$state.loaded()
}
const updateCollaborator = async (collab: any, roles: ProjectRoles) => {
try {
if (
@ -120,7 +97,6 @@ watchDebounced(
async () => {
isSearching.value = true
currentPage.value = 0
totalCollaborators.value = 0
collaborators.value = []
@ -224,17 +200,6 @@ onMounted(async () => {
</div>
</div>
</div>
<InfiniteLoading v-bind="$attrs" @infinite="loadListData">
<template #spinner>
<div class="flex flex-row w-full justify-center mt-2">
<GeneralLoader />
</div>
</template>
<template #complete>
<span></span>
</template>
</InfiniteLoading>
</div>
</template>
</div>

4
packages/nc-gui/components/project/View.vue

@ -5,7 +5,7 @@ import { isEeUI } from '#imports'
const basesStore = useBases()
const { getProjectUsers } = basesStore
const { getBaseUsers } = basesStore
const { openedProject, activeProjectId, baseUserCount } = storeToRefs(basesStore)
const { activeTables } = storeToRefs(useTablesStore())
@ -38,7 +38,7 @@ const updateBaseUserCount = async () => {
if (!baseUserCount || !isUIAllowed('newUser')) return
try {
const { totalRows } = await getProjectUsers({
const { totalRows } = await getBaseUsers({
baseId: activeProjectId.value!,
page: 1,
searchText: undefined,

4
packages/nc-gui/store/base.ts

@ -172,6 +172,10 @@ export const useBase = defineStore('baseStore', () => {
await loadTables()
await basesStore.getBaseUsers({
baseId: base.value.id || baseId.value,
})
// if (withTheme) setTheme(baseMeta.value?.theme)
return baseLoadedHook.trigger(base.value)

34
packages/nc-gui/store/bases.ts

@ -12,6 +12,8 @@ export const useBases = defineStore('basesStore', () => {
const bases = ref<Map<string, NcProject>>(new Map())
const basesList = computed<NcProject[]>(() => Array.from(bases.value.values()).sort((a, b) => a.updated_at - b.updated_at))
const basesUser = ref<Map<string, User[]>>(new Map())
const baseUserCount = ref<number | undefined>(undefined)
const router = useRouter()
@ -48,33 +50,35 @@ export const useBases = defineStore('basesStore', () => {
const isProjectsLoading = ref(false)
async function getProjectUsers({
baseId,
limit,
page,
searchText,
}: {
baseId: string
limit: number
page: number
searchText: string | undefined
}) {
async function getBaseUsers({ baseId, searchText, force = false }: { baseId: string; searchText?: string; force?: boolean }) {
if (!force && !limit && !page && basesUser.value.has(baseId)) {
const users = basesUser.value.get(baseId)
return {
users,
totalRows: users?.length ?? 0,
}
}
const response: any = await api.auth.baseUserList(baseId, {
query: {
limit,
offset: (page - 1) * limit,
query: searchText,
},
} as RequestParams)
const totalRows = response.users.pageInfo.totalRows ?? 0
basesUser.value.set(baseId, response.users.list)
return {
users: response.users.list,
totalRows,
}
}
const clearBasesUser = () => {
basesUser.value.clear()
}
const createProjectUser = async (baseId: string, user: User) => {
await api.auth.baseUserAdd(baseId, user as ProjectUserReqType)
}
@ -312,13 +316,15 @@ export const useBases = defineStore('basesStore', () => {
activeProjectId,
openedProject,
openedProjectBasesMap,
getProjectUsers,
getBaseUsers,
createProjectUser,
updateProjectUser,
navigateToProject,
removeProjectUser,
navigateToFirstProjectOrHome,
toggleStarred,
basesUser,
clearBasesUser,
}
})

3
packages/nc-gui/store/users.ts

@ -4,6 +4,7 @@ export const useUsers = defineStore('userStore', () => {
const { api } = useApi()
const { user } = useGlobal()
const { loadRoles } = useRoles()
const basesStore = useBases()
const updateUserProfile = async ({
attrs,
@ -20,6 +21,8 @@ export const useUsers = defineStore('userStore', () => {
...user.value,
...attrs,
}
basesStore.clearBasesUser()
}
const loadCurrentUser = loadRoles

2
packages/nocodb/src/models/Base.ts

@ -295,8 +295,6 @@ export default class Base implements BaseType {
let base = await this.get(baseId);
const users = await BaseUser.getUsersList({
base_id: baseId,
offset: 0,
limit: 1000,
});
for (const user of users) {

67
packages/nocodb/src/models/BaseUser.ts

@ -3,7 +3,6 @@ import type { BaseType } from 'nocodb-sdk';
import User from '~/models/User';
import Base from '~/models/Base';
import {
CacheDelDirection,
// CacheDelDirection,
CacheGetType,
CacheScope,
@ -67,27 +66,37 @@ export default class BaseUser {
`${CacheScope.BASE_USER}:${baseId}:${userId}`,
CacheGetType.TYPE_OBJECT,
));
if (!baseUser) {
baseUser = await ncMeta.metaGet2(null, null, MetaTable.PROJECT_USERS, {
fk_user_id: userId,
base_id: baseId,
if (!baseUser || !baseUser.roles) {
const queryBuilder = ncMeta
.knex(MetaTable.USERS)
.select(
`${MetaTable.USERS}.id`,
`${MetaTable.USERS}.email`,
`${MetaTable.USERS}.display_name`,
`${MetaTable.USERS}.invite_token`,
`${MetaTable.USERS}.roles as main_roles`,
`${MetaTable.USERS}.created_at as created_at`,
`${MetaTable.PROJECT_USERS}.base_id`,
`${MetaTable.PROJECT_USERS}.roles as roles`,
);
queryBuilder.leftJoin(MetaTable.PROJECT_USERS, function () {
this.on(
`${MetaTable.PROJECT_USERS}.fk_user_id`,
'=',
`${MetaTable.USERS}.id`,
).andOn(
`${MetaTable.PROJECT_USERS}.base_id`,
'=',
ncMeta.knex.raw('?', [baseId]),
);
});
if (baseUser) {
const {
id,
email,
invite_token,
roles: main_roles,
} = await User.get(userId, ncMeta);
baseUser = {
...baseUser,
id,
email,
invite_token,
main_roles,
};
queryBuilder.where(`${MetaTable.USERS}.id`, userId);
baseUser = await queryBuilder.first();
if (baseUser) {
await NocoCache.set(
`${CacheScope.BASE_USER}:${baseId}:${userId}`,
baseUser,
@ -100,13 +109,9 @@ export default class BaseUser {
public static async getUsersList(
{
base_id,
limit = 25,
offset = 0,
query,
}: {
base_id: string;
limit?: number;
offset?: number;
query?: string;
},
ncMeta = Noco.ncMeta,
@ -120,6 +125,7 @@ export default class BaseUser {
.select(
`${MetaTable.USERS}.id`,
`${MetaTable.USERS}.email`,
`${MetaTable.USERS}.display_name`,
`${MetaTable.USERS}.invite_token`,
`${MetaTable.USERS}.roles as main_roles`,
`${MetaTable.USERS}.created_at as created_at`,
@ -127,10 +133,6 @@ export default class BaseUser {
`${MetaTable.PROJECT_USERS}.roles as roles`,
);
if (limit) {
queryBuilder.offset(offset).limit(limit);
}
if (query) {
queryBuilder.where('email', 'like', `%${query.toLowerCase?.()}%`);
}
@ -259,12 +261,9 @@ export default class BaseUser {
},
);
// delete cache
await NocoCache.deepDel(
CacheScope.BASE_USER,
`${CacheScope.BASE_USER}:${baseId}:${userId}`,
CacheDelDirection.CHILD_TO_PARENT,
);
// delete list cache to refresh list
await NocoCache.del(`${CacheScope.BASE_USER}:${baseId}:${userId}`);
await NocoCache.del(`${CacheScope.BASE_USER}:${baseId}:list`);
return response;
}

18
packages/nocodb/src/services/base-users/base-users.service.ts

@ -28,19 +28,15 @@ export class BaseUsersService {
constructor(protected appHooksService: AppHooksService) {}
async userList(param: { baseId: string; query: any }) {
return new PagedResponseImpl(
await BaseUser.getUsersList({
const baseUsers = await BaseUser.getUsersList({
...param.query,
base_id: param.baseId,
}),
{
...param.query,
count: await BaseUser.getUsersCount({
base_id: param.baseId,
});
return new PagedResponseImpl(baseUsers, {
...param.query,
}),
},
);
count: baseUsers.length,
});
}
async userInvite(param: {
@ -110,7 +106,7 @@ export class BaseUsersService {
return NcError.badRequest('Invalid base id');
}
if (baseUser) {
if (baseUser && baseUser.roles) {
NcError.badRequest(
`${user.email} with role ${baseUser.roles} already exists in this base`,
);

Loading…
Cancel
Save