Repository: subugoe/tido
Pull request #595: Fix loading issues
Author: login: paulpestov
Created: 2025-04-01T21:03:04Z | Merged: 2025-04-02T12:51:09Z



Diff:
diff --git a/examples/config/4w-local.json b/examples/config/4w-local.json
index 28082590a..d82917c00 100644
--- a/examples/config/4w-local.json
+++ b/examples/config/4w-local.json
@@ -5,10 +5,7 @@
   },
   "panels": [
     {
-      "entrypoint": {
-        "url": "http://localhost:8181/4w/reproduction/collection.json",
-        "type": "collection"
-      },
+      "collection": "http://localhost:8181/4w/reproduction/collection.json",
       "manifestIndex": 0
     }
   ]
diff --git a/examples/config/default.json b/examples/config/default.json
new file mode 100644
index 000000000..d82917c00
--- /dev/null
+++ b/examples/config/default.json
@@ -0,0 +1,12 @@
+{
+  "globalTree": true,
+  "colors": {
+    "primary": "#1E40AF"
+  },
+  "panels": [
+    {
+      "collection": "http://localhost:8181/4w/reproduction/collection.json",
+      "manifestIndex": 0
+    }
+  ]
+}
diff --git a/src/App.tsx b/src/App.tsx
index ccc44bf94..0f87de61e 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -14,8 +14,7 @@ interface AppProps {
 }
 
 const App: FC<AppProps> = ({ customConfig }) => {
-  const addCustomConfig = useConfigStore((state) => state.addCustomConfig)
-  addCustomConfig(customConfig)
+  useConfigStore.getState().addCustomConfig(customConfig)
 
   const collections = useDataStore(state => state.collections)
   const setTreeNodes = useDataStore(state => state.setTreeNodes)
diff --git a/src/components/OpenSeaDragonViewer.tsx b/src/components/OpenSeaDragonViewer.tsx
index 5438c2bff..e5dc84f7c 100644
--- a/src/components/OpenSeaDragonViewer.tsx
+++ b/src/components/OpenSeaDragonViewer.tsx
@@ -3,19 +3,18 @@ import OpenSeadragon from 'openseadragon'
 
 import { usePanel } from '@/contexts/PanelContext'
 
-import { usePanelStore } from '@/store/PanelStore.tsx'
-
 import ImageActionButtons from '@/components/ImageActionButtons'
 
 const OpenSeaDragonViewer: FC = () => {
-  const { panelId } = usePanel()
+  const { panelId, panelState } = usePanel()
 
-  const imageUrl = usePanelStore((state) => state.panels[panelId].item.image?.id)
+  const imageUrl = panelState?.item?.image?.id
 
   let viewer: OpenSeadragon.Viewer | null = null
   const viewerId = 'viewer-' + panelId
 
   useEffect(() => {
+    if (!imageUrl) return
     viewer = OpenSeadragon({
       id: viewerId,
       prefixUrl:
diff --git a/src/components/panel/CollectionTitle.tsx b/src/components/panel/CollectionTitle.tsx
index 99fd35fa4..8bed55f36 100644
--- a/src/components/panel/CollectionTitle.tsx
+++ b/src/components/panel/CollectionTitle.tsx
@@ -12,8 +12,11 @@ const CollectionTitle: FC = () => {
   return (
     <>
       { !collection && <Skeleton className="t-w-[100px] t-h-6" /> }
-      {collection && <div className="t-text-sm t-bg-gray-200 t-px-2 t-py-1 t-rounded-md t-font-semibold">
-        {collection.title[0].title }
+      {collection && <div
+        className="t-text-sm t-bg-gray-200 t-px-2 t-py-1 t-rounded-md t-font-semibold t-truncate t-max-w-[200px]"
+        title={collection.title[0].title}
+      >
+        { collection.title[0].title }
       </div>}
     </>
 
diff --git a/src/components/panel/Preview.tsx b/src/components/panel/Preview.tsx
index e012dd9d0..112373a6c 100644
--- a/src/components/panel/Preview.tsx
+++ b/src/components/panel/Preview.tsx
@@ -21,7 +21,7 @@ const Preview: FC<PreviewProps> = ({ previewA, previewB, mode, setMode }) => {
     <div
       className="t-z-20 t-absolute t-border-[2px] t-border-zinc-800 t-p-[1px] t-w-20 t-h-24 t-overflow-hidden t-bottom-12 t-right-12 hover:t-brightness-[85%] t-transition-all"
       onClick={() => updateMode()}>
-      <div className="hover:t-cursor-pointer t-bg-white">
+      <div className="hover:t-cursor-pointer t-bg-white t-w-full t-h-full t-flex t-justify-center t-items-center">
         {mode === 'A' ? previewA : previewB  }
       </div>
     </div>
diff --git a/src/components/panel/TextRenderer.tsx b/src/components/panel/TextRenderer.tsx
index 3f6212f04..05d31c24d 100644
--- a/src/components/panel/TextRenderer.tsx
+++ b/src/components/panel/TextRenderer.tsx
@@ -19,7 +19,7 @@ const TextRenderer: FC<Props> = ({ htmlString }) => {
     scrollContainer.innerHTML = htmlString
   }, [htmlString])
 
-  return <div data-panel={panelId} ref={ref} className="t-h-full t-overflow-auto" />
+  return <div data-panel={panelId} ref={ref} className="t-h-full t-relative t-overflow-auto" />
 }
 
 export default TextRenderer
diff --git a/src/components/panel/views/PanelCentralContent.tsx b/src/components/panel/views/PanelCentralContent.tsx
index fa23e82ca..c1ab21803 100644
--- a/src/components/panel/views/PanelCentralContent.tsx
+++ b/src/components/panel/views/PanelCentralContent.tsx
@@ -14,6 +14,8 @@ import { Skeleton } from '@/components/ui/skeleton.tsx'
 import EmptyMessage from '@/components/panel/EmptyMessage.tsx'
 import { apiRequest } from '@/utils/api.ts'
 
+
+
 const PanelCentralContent: FC = () => {
   const { panelId, panelState, loading } = usePanel()
 
@@ -24,6 +26,11 @@ const PanelCentralContent: FC = () => {
 
   const [error, setError] = useState<string | null>(null)
 
+  function getContentUrlByType(type: string | undefined) {
+    if (!type) return undefined
+    return panelState?.item?.content.find(c => c.type.includes(type))?.url
+  }
+
   useEffect(() => {
     async function updateText(contentUrl: string) {
       try {
@@ -35,7 +42,8 @@ const PanelCentralContent: FC = () => {
       }
     }
 
-    const contentUrl = panelState?.item?.content[0].url ?? null
+    if (!panelState?.contentTypes.length) return
+    const contentUrl = getContentUrlByType(panelState?.contentTypes[activeContentTypeIndex].toLowerCase())
     if (contentUrl) updateText(contentUrl)
   }, [panelState, activeContentTypeIndex])
 
diff --git a/src/components/panel/views/TextViewOne.tsx b/src/components/panel/views/TextViewOne.tsx
index 91037ad3c..7688c28fc 100644
--- a/src/components/panel/views/TextViewOne.tsx
+++ b/src/components/panel/views/TextViewOne.tsx
@@ -3,6 +3,7 @@ import PreviewTextRenderer from '@/components/panel/PreviewTextRenderer.tsx'
 import TextRenderer from '@/components/panel/TextRenderer.tsx'
 import OpenSeaDragonViewer from '@/components/OpenSeaDragonViewer.tsx'
 import Preview from '@/components/panel/Preview.tsx'
+import { Image } from 'lucide-react'
 
 import { usePanel } from '@/contexts/PanelContext'
 
@@ -21,7 +22,7 @@ const TextViewOne: FC<TextViewOneProps> = ({ textHtml }) => {
     <div className="t-flex-1 t-overflow-hidden">
       { previewMode === 'A' ? <TextRenderer htmlString={textHtml} /> : <OpenSeaDragonViewer />}
       <Preview
-        previewA={<img src={imageUrl} alt={'image of Digitalisat'} />}
+        previewA={imageUrl ? <img src={imageUrl} alt={'image of Digitalisat'} /> : <div className="t-text-gray-300"><Image size={64} /></div>}
         previewB={<PreviewTextRenderer htmlString={textHtml} />}
         mode={previewMode}
         setMode={setMode}
diff --git a/src/contexts/PanelContext.tsx b/src/contexts/PanelContext.tsx
index 453145d6e..4dc773bef 100644
--- a/src/contexts/PanelContext.tsx
+++ b/src/contexts/PanelContext.tsx
@@ -48,7 +48,7 @@ const PanelProvider: FC<PanelProviderProps> = ({ children, panelConfig, index })
     if (!panelId) return
     try {
       setLoading(true)
-      const collection = await getCollection(panelConfig.entrypoint.url)
+      const collection = await getCollection(panelConfig.collection)
       const manifest = await apiRequest<Manifest>(collection.sequence[panelConfig.manifestIndex ?? 0].id)
       const item = await apiRequest<Item>(manifest.sequence[panelConfig.itemIndex ?? 0].id)
       const contentTypes: string[] = getContentTypes(item.content)
diff --git a/src/store/ConfigStore.tsx b/src/store/ConfigStore.tsx
index b526e1233..37aa26e10 100644
--- a/src/store/ConfigStore.tsx
+++ b/src/store/ConfigStore.tsx
@@ -8,7 +8,9 @@ interface ConfigStoreType {
 }
 
 export const useConfigStore = create<ConfigStoreType>((set, get) => ({
-  config: {},
+  config: {
+    container: '#app'
+  },
   addCustomConfig: (customConfig: Config) => {
     set({ config: customConfig })
   },
diff --git a/src/store/PanelStore.tsx b/src/store/PanelStore.tsx
index 0337705bd..65e4ca5a1 100644
--- a/src/store/PanelStore.tsx
+++ b/src/store/PanelStore.tsx
@@ -27,6 +27,7 @@ function getDefaultPanelState(id: string, index: number): PanelState {
     collectionId: null,
     item: null,
     manifest: null,
+    contentTypes: [],
     contentIndex: 0,
     viewIndex: 0,
     activeTargetIndex: -1
diff --git a/src/types.d.ts b/src/types.d.ts
index 28c262360..e9e35f683 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -151,7 +151,7 @@ declare global {
     collectionId: string | null
     manifest: Manifest | null
     item: Item | null
-    contentTypes?: string[]
+    contentTypes: string[]
     contentIndex: number
     viewIndex: number
     imageUrl?: string
@@ -224,7 +224,7 @@ declare global {
   }
 
   interface PanelConfig {
-    entrypoint: Entrypoint
+    collection: string
     color?: Colors
     manifestIndex?: number
     itemIndex?: number
diff --git a/src/utils/panel.ts b/src/utils/panel.ts
index ca055e3a3..2357a08a0 100644
--- a/src/utils/panel.ts
+++ b/src/utils/panel.ts
@@ -40,19 +40,14 @@ export function getContentType(value: string): string {
 
 
 export function getContentTypes(content: Content[]): string[] {
-  const types: string[] = content.map((item) => {
-    if ('type' in item) return getContentType(item.type)
-    return 'missing'
-  })
-
-  return types
+  return content.filter(item => item.type && item.type.includes('text/html')).map((item) => getContentType(item.type))
 }
 
 export function getUniquePanels(panels: PanelConfig[] | undefined) {
   if (!panels) return []
 
   const uniquePanels = panels.filter((p, index, self) =>
-    index === self.findIndex((panel) => panel.entrypoint.url === p.entrypoint.url))
+    index === self.findIndex((panel) => panel.collection === p.collection))
 
   return uniquePanels
 }
diff --git a/tests/mocks/4w/reproduction/collection.json b/tests/mocks/4w/reproduction/collection.json
index 562090bba..cd94dd621 100644
--- a/tests/mocks/4w/reproduction/collection.json
+++ b/tests/mocks/4w/reproduction/collection.json
@@ -1 +1 @@
-{"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/collection.jsonld", "textapi": "1.4.0", "id": "http://localhost:8181/4w/collection.json", "title": [{"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/title.jsonld", "title": "Vier Wachen vernetzt: Digitale Edition eines mystischen Traktats des Sp\u00e4tmittelalters", "type": "main"}], "collector": [{"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/actor.jsonld", "role": "collector", "name": "Prof. Dr. Henrike Manuwald"}], "sequence": [{"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/einsiedeln_278_1040/manifest.json", "type": "manifest", "label": "Einsiedeln, 278 1040"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/klosterneuburg_cod_251/manifest.json", "type": "manifest", "label": "Kloster Neuburg, Cod. 251"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/muenchen_bsb_cgm_627/manifest.json", "type": "manifest", "label": "M\u00fcnchen BSB Cgm 627"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/muenchen_bsb_cgm_628/manifest.json", "type": "manifest", "label": "M\u00fcnchen BSB Cgm 628"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/nuernberg_stadtbibliothek_cent_vi_43m/manifest.json", "type": "manifest", "label": "N\u00fcrnberg, Stadtbibliothek Cent. VI, 43m"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/stuttgart_wuerttembergische_landesbibliothek_cod_theol_et_phil_2/manifest.json", "type": "manifest", "label": "Stuttgart WLB Cod. Theol. et Phil. 2"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/stuttgart_wuerttembergische_landesbibliothek_cod_theol_et_phil_8_13/manifest.json", "type": "manifest", "label": "Stuttgart WLB Cod. Theol. et Phil. 8,13"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/wolfenbuettel_hab_cod_151_noviss_4/manifest.json", "type": "manifest", "label": "Wolfenb\u00fcttel HAB Cod. 151 Noviss. 4"}]}
\ No newline at end of file
+{"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/collection.jsonld", "textapi": "1.4.0", "id": "http://localhost:8181/4w/reproduction/collection.json", "title": [{"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/title.jsonld", "title": "Vier Wachen vernetzt: Digitale Edition eines mystischen Traktats des Sp\u00e4tmittelalters", "type": "main"}], "collector": [{"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/actor.jsonld", "role": "collector", "name": "Prof. Dr. Henrike Manuwald"}], "sequence": [{"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/einsiedeln_278_1040/manifest.json", "type": "manifest", "label": "Einsiedeln, 278 1040"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/klosterneuburg_cod_251/manifest.json", "type": "manifest", "label": "Kloster Neuburg, Cod. 251"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/muenchen_bsb_cgm_627/manifest.json", "type": "manifest", "label": "M\u00fcnchen BSB Cgm 627"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/muenchen_bsb_cgm_628/manifest.json", "type": "manifest", "label": "M\u00fcnchen BSB Cgm 628"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/nuernberg_stadtbibliothek_cent_vi_43m/manifest.json", "type": "manifest", "label": "N\u00fcrnberg, Stadtbibliothek Cent. VI, 43m"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/stuttgart_wuerttembergische_landesbibliothek_cod_theol_et_phil_2/manifest.json", "type": "manifest", "label": "Stuttgart WLB Cod. Theol. et Phil. 2"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/stuttgart_wuerttembergische_landesbibliothek_cod_theol_et_phil_8_13/manifest.json", "type": "manifest", "label": "Stuttgart WLB Cod. Theol. et Phil. 8,13"}, {"@context": "https://gitlab.gwdg.de/subugoe/textapi/specs/-/raw/main/jsonld/sequence.jsonld", "id": "http://localhost:8181/4w/wolfenbuettel_hab_cod_151_noviss_4/manifest.json", "type": "manifest", "label": "Wolfenb\u00fcttel HAB Cod. 151 Noviss. 4"}]}
diff --git a/vite.config.js b/vite.config.js
index 3c3e127a8..7e85a588c 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -18,7 +18,8 @@ function removeAttributes(attributes = []) {
 }
 
 function injectProjectConfig(projectName) {
-  const config = projectName ? fs.readFileSync(`examples/config/${projectName}.json`, 'utf8') : null;
+
+  const config = fs.readFileSync(`examples/config/${projectName ?? 'default'}.json`, 'utf8');
   return {
     name: 'inject-project-config',
     transformIndexHtml(html) {


Reviews:
user: login: orlinmalkja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T11:55:37Z
user: login: orlinmalkja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T11:55:48Z
user: login: orlinmalkja
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T12:34:57Z

Review comments:
diff_hunk: @@ -24,6 +26,12 @@ const PanelCentralContent: FC = () => {
 
   const [error, setError] = useState<string | null>(null)
 
+  function getContentUrlByType(type: string | undefined) {
+    console.log(type)
path: src/components/panel/views/PanelCentralContent.tsx
user: login: orlinmalkja
body: console log
created_at: 2025-04-02T11:55:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,7 +43,9 @@ const PanelCentralContent: FC = () => {
       }
     }
 
-    const contentUrl = panelState?.item?.content[0].url ?? null
+    if (!panelState?.contentTypes.length) return
+    const contentUrl = getContentUrlByType(panelState?.contentTypes[activeContentTypeIndex].toLowerCase())
+    console.log(contentUrl)
path: src/components/panel/views/PanelCentralContent.tsx
user: login: orlinmalkja
body: console log
created_at: 2025-04-02T11:55:48Z
author_association: CONTRIBUTOR

PR comments:
user: login: paulpestov
created_at: 2025-04-02T12:12:10Z
body: removed
author_association: COLLABORATOR