From d20d12fd4c805c0bb0e6ab6cb526683aa0985cbd Mon Sep 17 00:00:00 2001 From: EinfachMxrc <94084058+EinfachMxrc@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:18:38 +0200 Subject: [PATCH] Fix PowerPoint slide sync detection --- apps/powerpoint-addin/src/App.tsx | 3 +- apps/powerpoint-addin/src/lib/officeBridge.ts | 64 +++++++++++++++---- apps/powerpoint-addin/tsconfig.json | 2 +- apps/web/src/lib/powerpoint/officeBridge.ts | 2 +- 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/apps/powerpoint-addin/src/App.tsx b/apps/powerpoint-addin/src/App.tsx index 147f8dc..f30e584 100644 --- a/apps/powerpoint-addin/src/App.tsx +++ b/apps/powerpoint-addin/src/App.tsx @@ -1,5 +1,6 @@ import { useEffect, useState, useRef } from "react"; import { useMutation, useQuery } from "convex/react"; +import type { Id } from "../../../convex/_generated/dataModel"; import { useAddinStore } from "./store/addinStore"; import { initOfficeBridge, @@ -73,7 +74,7 @@ export function App({ convexReady }: AppProps) { try { await setCurrentSlide({ token: store.presenterToken, - sessionId: store.sessionId, + sessionId: store.sessionId as Id<"presentationSessions">, slideNumber, totalSlides, presentationTitle: title, diff --git a/apps/powerpoint-addin/src/lib/officeBridge.ts b/apps/powerpoint-addin/src/lib/officeBridge.ts index 66faf1b..54b3b99 100644 --- a/apps/powerpoint-addin/src/lib/officeBridge.ts +++ b/apps/powerpoint-addin/src/lib/officeBridge.ts @@ -40,7 +40,9 @@ let _lastReportedSlide: number | null = null; /** Debounce timer for DocumentSelectionChanged */ let _debounceTimer: ReturnType | null = null; +let _pollTimer: ReturnType | null = null; const DEBOUNCE_MS = 250; +const POLL_MS = 1000; /** * Returns true if Office.js is available in this environment. @@ -70,6 +72,8 @@ export function initOfficeBridge(callbacks: OfficeBridgeCallbacks): Promise { if (result.status === Office.AsyncResultStatus.Failed) { callbacks.onError("Automatischer Sync nicht verfügbar. Manueller Modus aktiv."); + callbacks.onModeChange("hybrid"); resolve("hybrid"); } else { + callbacks.onModeChange("auto"); + try { + Office.context.document.addHandlerAsync( + Office.EventType.ActiveViewChanged, + handleActiveViewChanged + ); + } catch { + // Ignore optional view change registration failures. + } resolve("auto"); } } ); } catch { callbacks.onError("Slide-Erkennung nicht verfügbar. Manueller Modus aktiv."); + callbacks.onModeChange("hybrid"); resolve("hybrid"); } }); @@ -101,21 +116,38 @@ export function initOfficeBridge(callbacks: OfficeBridgeCallbacks): Promise { - getCurrentSlideInfo() - .then((info) => { - if (!info || !_callbacks) return; - // Only fire if slide actually changed - if (info.slideNumber !== _lastReportedSlide) { - _lastReportedSlide = info.slideNumber; - _callbacks.onSlideChange(info); - } - }) - .catch((e) => { - _callbacks?.onError(`Folien-Update fehlgeschlagen: ${e}`); - }); + void syncCurrentSlide(); }, DEBOUNCE_MS); } +function handleActiveViewChanged() { + handleSelectionChanged(); +} + +async function syncCurrentSlide() { + try { + const info = await getCurrentSlideInfo(); + if (!info || !_callbacks) return; + + if (info.slideNumber !== _lastReportedSlide) { + _lastReportedSlide = info.slideNumber; + _callbacks.onSlideChange(info); + } + } catch (e) { + _callbacks?.onError(`Folien-Update fehlgeschlagen: ${e}`); + } +} + +function startPolling() { + if (_pollTimer) { + clearInterval(_pollTimer); + } + + _pollTimer = setInterval(() => { + void syncCurrentSlide(); + }, POLL_MS); +} + /** * Get current slide info from Office.js. * Uses getSlideCountAsync() for total slides (public API, not private property). @@ -181,12 +213,20 @@ export function destroyOfficeBridge() { clearTimeout(_debounceTimer); _debounceTimer = null; } + if (_pollTimer) { + clearInterval(_pollTimer); + _pollTimer = null; + } if (!isOfficeInitialized || !isOfficeAvailable()) return; try { Office.context.document.removeHandlerAsync( Office.EventType.DocumentSelectionChanged, { handler: handleSelectionChanged } ); + Office.context.document.removeHandlerAsync( + Office.EventType.ActiveViewChanged, + { handler: handleActiveViewChanged } + ); } catch { // ignore } diff --git a/apps/powerpoint-addin/tsconfig.json b/apps/powerpoint-addin/tsconfig.json index 83a3748..de8ea86 100644 --- a/apps/powerpoint-addin/tsconfig.json +++ b/apps/powerpoint-addin/tsconfig.json @@ -5,7 +5,7 @@ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "lib": ["ES2022", "DOM", "DOM.Iterable"], - "types": ["office-js"] + "types": ["office-js", "vite/client"] }, "include": ["src"] } diff --git a/apps/web/src/lib/powerpoint/officeBridge.ts b/apps/web/src/lib/powerpoint/officeBridge.ts index f4cc3ad..1b4f045 100644 --- a/apps/web/src/lib/powerpoint/officeBridge.ts +++ b/apps/web/src/lib/powerpoint/officeBridge.ts @@ -155,7 +155,7 @@ export function getCurrentSlideInfo(): Promise { return; } - const slideNumber = slides[0].index; + const slideNumber = slides[0].index + 1; (Office.context.document as any).getSlideCountAsync( (countResult: { status: string; value: number }) => {