From 39a7b47e15fbf36c8122d9f5b3c1d23f1579e962 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 22 Oct 2026 15:36:35 +0200 Subject: [PATCH 1/2] fix(textures): Fix import textures --- app/components/FileUploader.vue | 2 +- app/components/Viewer/Options/TextureItem.vue | 7 ++++--- internal/stores/data_style/when_defined.ts | 6 +++--- .../data_style/mesh/polygons.nuxt.test.ts | 18 ++++++++++++++++++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/app/components/FileUploader.vue b/app/components/FileUploader.vue index 4ff65338..13c3ed3f 100644 --- a/app/components/FileUploader.vue +++ b/app/components/FileUploader.vue @@ -176,7 +176,7 @@ watch( @folders-ignored="warnFoldersIgnored" /> - + Selected files diff --git a/app/components/Viewer/Options/TextureItem.vue b/app/components/Viewer/Options/TextureItem.vue index 823202f4..ec976c5d 100644 --- a/app/components/Viewer/Options/TextureItem.vue +++ b/app/components/Viewer/Options/TextureItem.vue @@ -95,8 +95,9 @@ watch(textureId, (value) => { diff --git a/internal/stores/data_style/when_defined.ts b/internal/stores/data_style/when_defined.ts index 9d60eaf9..0c6d573b 100644 --- a/internal/stores/data_style/when_defined.ts +++ b/internal/stores/data_style/when_defined.ts @@ -1,9 +1,9 @@ -// Style values read back from the database may be missing: skip the viewer request rather than send an invalid one. +// Style values read back from the database may be missing (or null by default, e.g. textures): skip the viewer request rather than send an invalid one. async function whenDefined( - value: Value | undefined, + value: Value | null | undefined, apply: (value: Value) => Promise, ): Promise { - if (value === undefined) { + if (value === undefined || value === null) { return undefined; } const result = await apply(value); diff --git a/tests/integration/stores/data_style/mesh/polygons.nuxt.test.ts b/tests/integration/stores/data_style/mesh/polygons.nuxt.test.ts index 63814111..8aebae06 100644 --- a/tests/integration/stores/data_style/mesh/polygons.nuxt.test.ts +++ b/tests/integration/stores/data_style/mesh/polygons.nuxt.test.ts @@ -249,6 +249,24 @@ describe("mesh polygons", () => { expect(dataStyleStore.meshPolygonsActiveColoring(id)).toBe(coloringName); expect(viewerStore.status).toBe(Status.CONNECTED); }); + + test("coloring textures without textures", async () => { + const dataStyleStore = useDataStyleStore(); + const viewerStore = useViewerStore(); + const spy = vi.spyOn(viewerStore, "request"); + const coloringName = "textures"; + const result = dataStyleStore.setMeshPolygonsActiveColoring(id, coloringName); + expect(result).toBeInstanceOf(Promise); + await result; + expect(spy).not.toHaveBeenCalledWith( + // oxlint-disable-next-line no-unsafe-assignment -- expect.objectContaining is untyped by design, this is a vitest matcher. + expect.objectContaining({ schema: viewer_schemas.opengeodeweb_viewer.mesh.apply_textures }), + // oxlint-disable-next-line no-unsafe-assignment -- expect.anything is untyped by design, this is a vitest matcher. + expect.anything(), + ); + expect(dataStyleStore.meshPolygonsActiveColoring(id)).toBe(coloringName); + expect(viewerStore.status).toBe(Status.CONNECTED); + }); }); test("polygons apply default style", async () => { From 3b3fa7220dd112cdb0382b982b1bbe3d96e7f378 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 22 Oct 2026 16:57:17 +0200 Subject: [PATCH 2/2] rework with textures no longer returns null --- app/utils/default_styles/meshes.ts | 14 ++++++-------- internal/stores/data_style/mesh/cells/index.ts | 10 ++++++---- internal/stores/data_style/mesh/polygons/index.ts | 10 ++++++---- internal/stores/data_style/when_defined.ts | 6 +++--- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/app/utils/default_styles/meshes.ts b/app/utils/default_styles/meshes.ts index 3c80814c..ee068eb2 100644 --- a/app/utils/default_styles/meshes.ts +++ b/app/utils/default_styles/meshes.ts @@ -1,3 +1,5 @@ +import type { MeshApplyTexturesParams } from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_typed_schemas.js"; + import { type AttributeConfig, CELLS_DEFAULT_COLOR, @@ -32,13 +34,11 @@ interface MeshEdgesColoring extends MeshComponentColoring { interface MeshCellsColoring extends MeshComponentColoring { cell: AttributeConfig; - // oxlint-disable-next-line unicorn/no-null - textures: null; + textures: MeshApplyTexturesParams["textures"]; } interface MeshPolygonsColoring extends MeshComponentColoring { - // oxlint-disable-next-line unicorn/no-null - textures: null; + textures: MeshApplyTexturesParams["textures"]; polygon: AttributeConfig; } @@ -157,8 +157,7 @@ function meshCellsDefaultStyle( active: MESH_DEFAULT_ACTIVE_COLORING, cell: emptyAttributeConfig(), constant, - // oxlint-disable-next-line unicorn/no-null - textures: null, + textures: [], vertex: emptyAttributeConfig(), }, }; @@ -173,8 +172,7 @@ function meshPolygonsDefaultStyle( coloring: { active: MESH_DEFAULT_ACTIVE_COLORING, constant, - // oxlint-disable-next-line unicorn/no-null - textures: null, + textures: [], polygon: emptyAttributeConfig(), vertex: emptyAttributeConfig(), }, diff --git a/internal/stores/data_style/mesh/cells/index.ts b/internal/stores/data_style/mesh/cells/index.ts index 439764da..772db939 100644 --- a/internal/stores/data_style/mesh/cells/index.ts +++ b/internal/stores/data_style/mesh/cells/index.ts @@ -68,10 +68,12 @@ async function setMeshCellsActiveColoring( return result; } if (type === "textures") { - const result = await whenDefined(deps.texturesStyle.meshCellsTextures(id), async (value) => { - const applied = await deps.texturesStyle.setMeshCellsTextures(id, value); - return applied; - }); + const textures = deps.texturesStyle.meshCellsTextures(id); + // The viewer requires at least one texture: nothing to apply until one is picked. + if (textures === undefined || textures.length === 0) { + return undefined; + } + const result = await deps.texturesStyle.setMeshCellsTextures(id, textures); return result; } if (type === "vertex") { diff --git a/internal/stores/data_style/mesh/polygons/index.ts b/internal/stores/data_style/mesh/polygons/index.ts index 55c57388..78d26c04 100644 --- a/internal/stores/data_style/mesh/polygons/index.ts +++ b/internal/stores/data_style/mesh/polygons/index.ts @@ -78,10 +78,12 @@ async function setMeshPolygonsActiveColoring( return result; } if (type === "textures") { - const result = await whenDefined(deps.texturesStyle.meshPolygonsTextures(id), async (value) => { - const applied = await deps.texturesStyle.setMeshPolygonsTextures(id, value); - return applied; - }); + const textures = deps.texturesStyle.meshPolygonsTextures(id); + // The viewer requires at least one texture: nothing to apply until one is picked. + if (textures === undefined || textures.length === 0) { + return undefined; + } + const result = await deps.texturesStyle.setMeshPolygonsTextures(id, textures); return result; } if (type === "vertex") { diff --git a/internal/stores/data_style/when_defined.ts b/internal/stores/data_style/when_defined.ts index 0c6d573b..9d60eaf9 100644 --- a/internal/stores/data_style/when_defined.ts +++ b/internal/stores/data_style/when_defined.ts @@ -1,9 +1,9 @@ -// Style values read back from the database may be missing (or null by default, e.g. textures): skip the viewer request rather than send an invalid one. +// Style values read back from the database may be missing: skip the viewer request rather than send an invalid one. async function whenDefined( - value: Value | null | undefined, + value: Value | undefined, apply: (value: Value) => Promise, ): Promise { - if (value === undefined || value === null) { + if (value === undefined) { return undefined; } const result = await apply(value);