From e5c2c968bbdadc20155e8fe203fa8c0d1b8e7b64 Mon Sep 17 00:00:00 2001 From: NicolasRichel Date: Wed, 1 Apr 2026 14:20:15 +0200 Subject: [PATCH 1/3] perf: add some cache to projects/models loading --- .../projects/project-card/ProjectCard.vue | 2 +- src/router/resolvers/views/project-board.js | 4 +-- src/router/resolvers/views/space-board.js | 2 +- src/services/ModelService.js | 21 ++++++++++---- src/services/ProjectService.js | 28 ++++++++++++++++--- src/state/models.js | 4 +-- src/state/projects.js | 8 +++--- 7 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/components/specific/projects/project-card/ProjectCard.vue b/src/components/specific/projects/project-card/ProjectCard.vue index 4a65cb84d..8528ac7c8 100644 --- a/src/components/specific/projects/project-card/ProjectCard.vue +++ b/src/components/specific/projects/project-card/ProjectCard.vue @@ -152,7 +152,7 @@ onMounted(() => { () => props.project, async () => { loading.value = true; - const models = await ModelService.fetchModels(props.project); + const models = await ModelService.fetchModels(props.project, { cache: true }); displayedModels.value = models.reduce( (acc, model) => { if ( diff --git a/src/router/resolvers/views/project-board.js b/src/router/resolvers/views/project-board.js index e85f4899c..84f25f4da 100644 --- a/src/router/resolvers/views/project-board.js +++ b/src/router/resolvers/views/project-board.js @@ -17,14 +17,14 @@ export default function projectBoardResolver(route) { const project = projects.setCurrentProject(+route.params.projectID); spaces.loadSpaceSubInfo(space); - projects.loadSpaceProjects(space); + projects.loadSpaceProjects(space, { cache: true }); load("project-users", [ projects.loadProjectUsers(project), projects.loadProjectInvitations(project) ]); load("project-models", [ - models.loadProjectModels(project) + models.loadProjectModels(project, { cache: true }) ]); load("project-files", [ files.loadProjectFileStructure(project), diff --git a/src/router/resolvers/views/space-board.js b/src/router/resolvers/views/space-board.js index 6b885540e..4c5fddc8f 100644 --- a/src/router/resolvers/views/space-board.js +++ b/src/router/resolvers/views/space-board.js @@ -16,6 +16,6 @@ export default async function spaceBoardResolver(route) { spaces.loadSpaceInvitations(space) ]); load("space-projects", [ - projects.loadSpaceProjects(space) + projects.loadSpaceProjects(space, { cache: true }) ]); } diff --git a/src/services/ModelService.js b/src/services/ModelService.js index 3ccc33ec9..64abe7109 100644 --- a/src/services/ModelService.js +++ b/src/services/ModelService.js @@ -4,13 +4,23 @@ import { MODEL_TYPE } from "../config/models.js"; import { ERRORS, RuntimeError, ErrorService } from "./ErrorService.js"; class ModelService { - callQueue = queue(async task => { - return await task(); - }, 40); + constructor() { + this.cache = new Map(); + this.callQueue = queue(async task => { + return await task(); + }, 40); + } - async fetchModels(project) { + async fetchModels(project, { cache } = {}) { try { - return await this.callQueue.push(() => apiClient.modelApi.getModelsSummary(project.cloud.id, project.id)); + const key = `project-models-${project.id}`; + if (cache && this.cache.has(key)) { + return this.cache.get(key); + } else { + const models = await this.callQueue.push(() => apiClient.modelApi.getModelsSummary(project.cloud.id, project.id)); + this.cache.set(key, models); + return models; + } } catch (error) { ErrorService.handleError( new RuntimeError(ERRORS.MODELS_FETCH_ERROR, error) @@ -101,6 +111,7 @@ class ModelService { throw new RuntimeError(ERRORS.MODEL_DELETE_ERROR, error); } } + fetchModelElements(project, model, params = {}) { return apiClient.modelApi.getElements( project.cloud.id, diff --git a/src/services/ProjectService.js b/src/services/ProjectService.js index f7e9c1f4f..6ba61d478 100644 --- a/src/services/ProjectService.js +++ b/src/services/ProjectService.js @@ -2,18 +2,36 @@ import { apiClient, backendClient } from "./api-client.js"; import { ERRORS, RuntimeError, ErrorService } from "./ErrorService.js"; class ProjectService { - async fetchUserProjects() { + constructor() { + this.cache = new Map(); + } + + async fetchUserProjects({ cache } = {}) { try { - return await apiClient.collaborationApi.getSelfProjects(); + const key = "user-projects"; + if (cache && this.cache.has(key)) { + return this.cache.get(key); + } else { + const projects = await apiClient.collaborationApi.getSelfProjects(); + this.cache.set(key, projects); + return projects; + } } catch (error) { ErrorService.handleError(new RuntimeError(ERRORS.PROJECTS_FETCH_ERROR, error)); return []; } } - async fetchSpaceProjects(space) { + async fetchSpaceProjects(space, { cache } = {}) { try { - return await apiClient.collaborationApi.getProjects(space.id); + const key = `space-projects-${space.id}`; + if (cache && this.cache.has(key)) { + return this.cache.get(key); + } else { + const projects = await apiClient.collaborationApi.getProjects(space.id); + this.cache.set(key, projects); + return projects; + } } catch (error) { ErrorService.handleError(new RuntimeError(ERRORS.PROJECTS_FETCH_ERROR, error)); return []; @@ -165,9 +183,11 @@ class ProjectService { } return res; } + updateProjectNotification(spaceId, projectId, notification) { return backendClient.put(`/cloud/${spaceId}/project/${projectId}/notification`, notification); } + deleteProjectNotification(spaceId, projectId) { return backendClient.delete(`/cloud/${spaceId}/project/${projectId}/notification`); } diff --git a/src/state/models.js b/src/state/models.js index 5a404e066..7bded290e 100644 --- a/src/state/models.js +++ b/src/state/models.js @@ -8,8 +8,8 @@ const state = reactive({ projectModels: [] }); -const loadProjectModels = async project => { - const models = await ModelService.fetchModels(project); +const loadProjectModels = async (project, options) => { + const models = await ModelService.fetchModels(project, options); const projectModels = []; for (const model of models) { if (model.id === project.main_model_id) { diff --git a/src/state/projects.js b/src/state/projects.js index 7448110dd..7ad495d43 100644 --- a/src/state/projects.js +++ b/src/state/projects.js @@ -24,8 +24,8 @@ const setCurrentProject = (id) => { return readonly(state.currentProject); }; -const loadUserProjects = async () => { - const projects = await ProjectService.fetchUserProjects(); +const loadUserProjects = async options => { + const projects = await ProjectService.fetchUserProjects(options); state.userProjects = sortProjects(projects); @@ -40,8 +40,8 @@ const loadUserProjects = async () => { return projects; }; -const loadSpaceProjects = async (space) => { - const projects = await ProjectService.fetchSpaceProjects(space); +const loadSpaceProjects = async (space, options) => { + const projects = await ProjectService.fetchSpaceProjects(space, options); state.spaceProjects = sortProjects(projects); return projects; }; From b2aee4a0a9e94373b4a998848471ef5bad97a090 Mon Sep 17 00:00:00 2001 From: NicolasRichel Date: Thu, 2 Apr 2026 14:48:36 +0200 Subject: [PATCH 2/3] perf: keep scroll position when navigating back to space-board from project-board --- src/composables/session.js | 28 ++++++++++++++++++++++++ src/views/space-board/SpaceBoard.vue | 22 ++++++++++++++++++- src/views/user-projects/UserProjects.vue | 22 ++++++++++++++++++- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/composables/session.js b/src/composables/session.js index 38b094876..ac21c5322 100644 --- a/src/composables/session.js +++ b/src/composables/session.js @@ -7,6 +7,8 @@ const STORAGE_KEYS = Object.freeze({ PROJECT_MODEL_TAB: `${KEY_PREFIX}:project-model-tab`, GED_FILES_TAB: `${KEY_PREFIX}:ged-files-tab`, GED_TARGET_FOLDER: `${KEY_PREFIX}:ged-target-folder`, + SPACE_BOARD_VIEW_SCROLL: `${KEY_PREFIX}:space-board-view-scroll`, + USER_PROJECTS_VIEW_SCROLL: `${KEY_PREFIX}:user-projects-view-scroll`, }); const getEntry = (key) => JSON.parse(sessionStorage.getItem(key)); @@ -70,6 +72,30 @@ const gedTargetFolder = { }, }; +const spaceBoardViewScroll = { + get(spaceId) { + return getEntry(`${STORAGE_KEYS.SPACE_BOARD_VIEW_SCROLL}:${spaceId}`); + }, + set(spaceId, value) { + setEntry(`${STORAGE_KEYS.SPACE_BOARD_VIEW_SCROLL}:${spaceId}`, value); + }, + clear(spaceId) { + sessionStorage.removeItem(`${STORAGE_KEYS.SPACE_BOARD_VIEW_SCROLL}:${spaceId}`); + }, +}; + +const userProjectsViewScroll = { + get() { + return getEntry(`${STORAGE_KEYS.USER_PROJECTS_VIEW_SCROLL}`); + }, + set(value) { + setEntry(`${STORAGE_KEYS.USER_PROJECTS_VIEW_SCROLL}`, value); + }, + clear() { + sessionStorage.removeItem(`${STORAGE_KEYS.USER_PROJECTS_VIEW_SCROLL}`); + }, +}; + export function useSession() { return { currentView, @@ -78,5 +104,7 @@ export function useSession() { gedFilesTab, gedTargetFolder, projectModelTab, + spaceBoardViewScroll, + userProjectsViewScroll, }; } diff --git a/src/views/space-board/SpaceBoard.vue b/src/views/space-board/SpaceBoard.vue index 5fcf5c428..e75f4667e 100644 --- a/src/views/space-board/SpaceBoard.vue +++ b/src/views/space-board/SpaceBoard.vue @@ -78,13 +78,16 @@ diff --git a/src/components/specific/users/user-favorites-manager/favorite-space-card/FavoriteSpaceCard.vue b/src/components/specific/users/user-favorites-manager/favorite-space-card/FavoriteSpaceCard.vue index ed13d6526..128a7b5e4 100644 --- a/src/components/specific/users/user-favorites-manager/favorite-space-card/FavoriteSpaceCard.vue +++ b/src/components/specific/users/user-favorites-manager/favorite-space-card/FavoriteSpaceCard.vue @@ -17,7 +17,7 @@
- {{ nbProjects + " " + $t("t.projects") }} + {{ projectsCount[space.id] + " " + $t("t.projects") }}
@@ -27,7 +27,6 @@