From fd4f921b254ac17f85291f9f4c60553b0c7b00f1 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 18 Mar 2025 13:33:25 +0100 Subject: [PATCH] feat: display app not installed error --- .../resources/AppLink/AppLink.stories.tsx | 14 ++++++++++++++ site/src/modules/resources/AppLink/AppLink.tsx | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/site/src/modules/resources/AppLink/AppLink.stories.tsx b/site/src/modules/resources/AppLink/AppLink.stories.tsx index 0052a40c4606d..db6fbf02c69da 100644 --- a/site/src/modules/resources/AppLink/AppLink.stories.tsx +++ b/site/src/modules/resources/AppLink/AppLink.stories.tsx @@ -8,6 +8,7 @@ import { MockWorkspaceApp, MockWorkspaceProxies, } from "testHelpers/entities"; +import { withGlobalSnackbar } from "testHelpers/storybook"; import { AppLink } from "./AppLink"; const meta: Meta = { @@ -72,6 +73,19 @@ export const ExternalApp: Story = { }, }; +export const ExternalAppNotInstalled: Story = { + decorators: [withGlobalSnackbar], + args: { + workspace: MockWorkspace, + app: { + ...MockWorkspaceApp, + external: true, + url: "foobar-foobaz://open-me", + }, + agent: MockWorkspaceAgent, + }, +}; + export const SharingLevelOwner: Story = { args: { workspace: MockWorkspace, diff --git a/site/src/modules/resources/AppLink/AppLink.tsx b/site/src/modules/resources/AppLink/AppLink.tsx index e9d5f7d59561b..3dea2fd7c4bab 100644 --- a/site/src/modules/resources/AppLink/AppLink.tsx +++ b/site/src/modules/resources/AppLink/AppLink.tsx @@ -5,7 +5,9 @@ import Link from "@mui/material/Link"; import Tooltip from "@mui/material/Tooltip"; import { API } from "api/api"; import type * as TypesGen from "api/typesGenerated"; +import { displayError } from "components/GlobalSnackbar/utils"; import { useProxy } from "contexts/ProxyContext"; +import { useEffect } from "react"; import { type FC, type MouseEvent, useState } from "react"; import { createAppLinkHref } from "utils/apps"; import { generateRandomString } from "utils/random"; @@ -152,6 +154,20 @@ export const AppLink: FC = ({ app, workspace, agent }) => { url = href.replaceAll(magicTokenString, key.key); setFetchingSessionToken(false); } + + // When browser recognizes the protocol and is able to navigate to the app, + // it will blur away, and will stop the timer. Otherwise, + // an error message will be displayed. + const openAppExternallyFailedTimeout = 500; + const openAppExternallyFailed = setTimeout(() => { + displayError( + `${app.display_name !== "" ? app.display_name : app.slug} must be installed first.`, + ); + }, openAppExternallyFailedTimeout); + window.addEventListener("blur", () => { + clearTimeout(openAppExternallyFailed); + }); + window.location.href = url; return; }