diff --git a/extension/app/background.ts b/extension/app/background.ts index 1e7db061936a..d7e0ee25dac7 100644 --- a/extension/app/background.ts +++ b/extension/app/background.ts @@ -1,3 +1,6 @@ +import type { PendingUpdate } from "@extension/lib/storage"; +import { savePendingUpdate } from "@extension/lib/storage"; + import { AUTH0_AUDIENCE, AUTH0_CLIENT_DOMAIN, @@ -14,6 +17,17 @@ import { generatePKCE } from "./src/lib/utils"; const log = console.error; +/** + * Listener for force update mechanism. + */ +chrome.runtime.onUpdateAvailable.addListener(async (details) => { + const pendingUpdate: PendingUpdate = { + version: details.version, + detectedAt: Date.now(), + }; + await savePendingUpdate(pendingUpdate); +}); + /** * Listener to open/close the side panel when the user clicks on the extension icon. */ diff --git a/extension/app/src/components/auth/ProtectedRoute.tsx b/extension/app/src/components/auth/ProtectedRoute.tsx index 52636609c81a..9c144f7da899 100644 --- a/extension/app/src/components/auth/ProtectedRoute.tsx +++ b/extension/app/src/components/auth/ProtectedRoute.tsx @@ -1,9 +1,15 @@ -import { Spinner } from "@dust-tt/sparkle"; +import { + Button, + LogoHorizontalColorLogo, + Page, + Spinner, +} from "@dust-tt/sparkle"; import type { LightWorkspaceType } from "@dust-tt/types"; import { useAuth } from "@extension/components/auth/AuthProvider"; import type { StoredUser } from "@extension/lib/storage"; +import { getPendingUpdate } from "@extension/lib/storage"; import type { ReactNode } from "react"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; type ProtectedRouteProps = { @@ -27,6 +33,7 @@ export const ProtectedRoute = ({ children }: ProtectedRouteProps) => { } = useAuth(); const navigate = useNavigate(); + const [isLatestVersion, setIsLatestVersion] = useState(true); useEffect(() => { if (!isAuthenticated || !isUserSetup || !user || !workspace) { @@ -35,6 +42,26 @@ export const ProtectedRoute = ({ children }: ProtectedRouteProps) => { } }, [navigate, isLoading, isAuthenticated, isUserSetup, user, workspace]); + const checkIsLatestVersion = async () => { + const pendingUpdate = await getPendingUpdate(); + if (!pendingUpdate) { + return null; + } + if (pendingUpdate.version > chrome.runtime.getManifest().version) { + setIsLatestVersion(false); + } + }; + + useEffect(() => { + void checkIsLatestVersion(); + + chrome.storage.local.onChanged.addListener((changes) => { + if (changes.pendingUpdate) { + void checkIsLatestVersion(); + } + }); + }, []); + if (isLoading || !isAuthenticated || !isUserSetup || !user || !workspace) { return (
@@ -45,6 +72,26 @@ export const ProtectedRoute = ({ children }: ProtectedRouteProps) => { ); } + if (!isLatestVersion) { + return ( +
+
+
+ + +
+ +
+
+ ); + } + return (
{typeof children === "function" diff --git a/extension/app/src/components/conversation/AgentMessage.tsx b/extension/app/src/components/conversation/AgentMessage.tsx index 49cf4c86aaf6..e96b9137fbfc 100644 --- a/extension/app/src/components/conversation/AgentMessage.tsx +++ b/extension/app/src/components/conversation/AgentMessage.tsx @@ -8,7 +8,6 @@ import type { AgentMessagePublicType, AgentMessageSuccessEvent, GenerationTokensEvent, - HeartbeatEvent, LightWorkspaceType, RetrievalActionPublicType, RetrievalDocumentPublicType, diff --git a/extension/app/src/css/custom.css b/extension/app/src/css/custom.css index 950d2b362014..cf1ae72f0189 100644 --- a/extension/app/src/css/custom.css +++ b/extension/app/src/css/custom.css @@ -1,4 +1,8 @@ +html { + font-size: 14px; +} + body { font-family: darkmode-off-cc, sans-serif; - font-size: 14px; + font-size: 100%; } diff --git a/extension/app/src/lib/storage.ts b/extension/app/src/lib/storage.ts index bb4596247efd..3fe457b2b687 100644 --- a/extension/app/src/lib/storage.ts +++ b/extension/app/src/lib/storage.ts @@ -105,6 +105,25 @@ export const getStoredUser = async (): Promise => { return result.user ?? null; }; +/** + * Store version for force update. + */ + +export type PendingUpdate = { + version: string; + detectedAt: number; +}; +export const savePendingUpdate = async ( + pendingUpdate: PendingUpdate +): Promise => { + await chrome.storage.local.set({ pendingUpdate }); + return pendingUpdate; +}; +export const getPendingUpdate = async (): Promise => { + const result = await chrome.storage.local.get(["pendingUpdate"]); + return result.pendingUpdate ?? null; +}; + /** * Clear all stored data. */