From 9371457d0d44057d794f8fcd507439cfa4d49745 Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Wed, 21 May 2025 21:03:58 +0530 Subject: [PATCH] chore: display platform links in app header (#5091) --- .../hoppscotch-common/assets/icons/apple.svg | 1 + .../hoppscotch-common/assets/icons/linux.svg | 1 + .../assets/icons/windows.svg | 1 + packages/hoppscotch-common/locales/en.json | 7 ++ .../src/components/app/Header.vue | 79 +++++++++++--- .../src/platform/additionalLinks.ts | 8 ++ .../hoppscotch-common/src/platform/index.ts | 2 + .../src/services/additionalLinks.service.ts | 55 ++++++++++ packages/hoppscotch-selfhost-web/src/main.ts | 2 + .../headerDownloadableLinks.service.ts | 101 ++++++++++++++++++ 10 files changed, 240 insertions(+), 17 deletions(-) create mode 100644 packages/hoppscotch-common/assets/icons/apple.svg create mode 100644 packages/hoppscotch-common/assets/icons/linux.svg create mode 100644 packages/hoppscotch-common/assets/icons/windows.svg create mode 100644 packages/hoppscotch-common/src/platform/additionalLinks.ts create mode 100644 packages/hoppscotch-common/src/services/additionalLinks.service.ts create mode 100644 packages/hoppscotch-selfhost-web/src/services/headerDownloadableLinks.service.ts diff --git a/packages/hoppscotch-common/assets/icons/apple.svg b/packages/hoppscotch-common/assets/icons/apple.svg new file mode 100644 index 00000000..6e9303c3 --- /dev/null +++ b/packages/hoppscotch-common/assets/icons/apple.svg @@ -0,0 +1 @@ +Apple \ No newline at end of file diff --git a/packages/hoppscotch-common/assets/icons/linux.svg b/packages/hoppscotch-common/assets/icons/linux.svg new file mode 100644 index 00000000..393e95e8 --- /dev/null +++ b/packages/hoppscotch-common/assets/icons/linux.svg @@ -0,0 +1 @@ +Linux \ No newline at end of file diff --git a/packages/hoppscotch-common/assets/icons/windows.svg b/packages/hoppscotch-common/assets/icons/windows.svg new file mode 100644 index 00000000..92b6d8cb --- /dev/null +++ b/packages/hoppscotch-common/assets/icons/windows.svg @@ -0,0 +1 @@ +Windows \ No newline at end of file diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index cf565fdc..4b8cf526 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -118,6 +118,13 @@ "add_pfx_or_pkcs_file": "Add PFX/PKCS12 File" }, "app": { + "additional_links": { + "macOS": "macOS", + "windows": "Windows", + "linux": "Linux", + "web_app": "Web App", + "cli": "CLI" + }, "chat_with_us": "Chat with us", "contact_us": "Contact us", "cookies": "Cookies", diff --git a/packages/hoppscotch-common/src/components/app/Header.vue b/packages/hoppscotch-common/src/components/app/Header.vue index 705b5b31..6d72a9a3 100644 --- a/packages/hoppscotch-common/src/components/app/Header.vue +++ b/packages/hoppscotch-common/src/components/app/Header.vue @@ -59,14 +59,47 @@ class="col-span-2 flex items-center justify-between space-x-2" >
- + + + + + (null) : ref(null) +const downloadableLinksRef = + kernelMode === "web" ? ref(null) : ref(null) const isUserAdmin = ref(false) @@ -380,14 +415,6 @@ const workspaceSelectorFlagEnabled = computed( () => !!platform.platformFeatureFlags.workspaceSwitcherLogin?.value ) -/** - * Once the PWA code is initialized, this holds a method - * that can be called to show the user the installation - * prompt. - */ - -const showInstallButton = computed(() => !!pwaDefferedPrompt.value) - /** * Show the dashboard link if the user is not on the default cloud instance and is an admin */ @@ -420,6 +447,24 @@ const offlineBanner: BannerContent = { dismissible: true, } +const additionalLinks = useService(AdditionalLinksService) + +platform.additionalLinks?.forEach((linkSet) => { + useService(linkSet) +}) + +const downloadableLinks = computed(() => { + if (kernelMode !== "web") return null + + const headerDownloadableLink = additionalLinks?.getLinkSet( + "HEADER_DOWNLOADABLE_LINKS" + ) + + if (!headerDownloadableLink) return null + + return headerDownloadableLink.getLinks().value +}) + // Show the offline banner if the app is offline const network = reactive(useNetwork()) const isOnline = computed(() => network.isOnline) diff --git a/packages/hoppscotch-common/src/platform/additionalLinks.ts b/packages/hoppscotch-common/src/platform/additionalLinks.ts new file mode 100644 index 00000000..38ba4e6f --- /dev/null +++ b/packages/hoppscotch-common/src/platform/additionalLinks.ts @@ -0,0 +1,8 @@ +import { Container, ServiceClassInstance } from "dioc" +import { AdditionalLinksService } from "~/services/additionalLinks.service" + +export type AdditionalLinksPlatformDef = Array< + ServiceClassInstance & { + new (c: Container): AdditionalLinksService + } +> diff --git a/packages/hoppscotch-common/src/platform/index.ts b/packages/hoppscotch-common/src/platform/index.ts index 765782bd..b79e475b 100644 --- a/packages/hoppscotch-common/src/platform/index.ts +++ b/packages/hoppscotch-common/src/platform/index.ts @@ -20,6 +20,7 @@ import { UIPlatformDef } from "./ui" import { BackendPlatformDef } from "./backend" import { OrganizationPlatformDef } from "./organization" import { KernelIO } from "./kernel-io" +import { AdditionalLinksPlatformDef } from "./additionalLinks" export type PlatformDef = { ui?: UIPlatformDef @@ -69,6 +70,7 @@ export type PlatformDef = { experiments?: ExperimentsPlatformDef backend: BackendPlatformDef organization?: OrganizationPlatformDef + additionalLinks?: AdditionalLinksPlatformDef } export let platform: PlatformDef diff --git a/packages/hoppscotch-common/src/services/additionalLinks.service.ts b/packages/hoppscotch-common/src/services/additionalLinks.service.ts new file mode 100644 index 00000000..827d3568 --- /dev/null +++ b/packages/hoppscotch-common/src/services/additionalLinks.service.ts @@ -0,0 +1,55 @@ +import { Service } from "dioc" +import { Component, ComputedRef, Ref } from "vue" +import { getI18n } from "~/modules/i18n" + +export type Link = { + id: string + text: (t: ReturnType) => string + icon: Component + action: { type: "link"; href: string } | { type: "custom"; do: () => void } + show?: ComputedRef +} + +// The possible links IDs +type LinkID = "HEADER_DOWNLOADABLE_LINKS" | string + +export interface AdditionalLinkSet { + linkSetID: LinkID + getLinks: () => Ref +} + +/** + * Service to manage additional links in the app + * This service is used to register and retrieve link sets + * that can be displayed in the app's UI. + * Each link set can contain multiple links, each with its own action. + * The links can be displayed in different parts of the app, such as the header or footer. + */ +export class AdditionalLinksService extends Service { + public static readonly ID = "ADDITIONAL_LINKS_SERVICE" + + private additionalLinkSets: Map = new Map() + + /** + * Registers a link set with the LinksService + * @param linkSet The link set to register + */ + public registerAdditionalSet(linkSet: AdditionalLinkSet) { + this.additionalLinkSets.set(linkSet.linkSetID, linkSet) + } + + /** + * Gets all registered link sets + */ + public getAllLinkSets(): IterableIterator<[string, AdditionalLinkSet]> { + return this.additionalLinkSets.entries() + } + + /** + * Gets a link set by its ID + * @param linkSetID The ID of the link set to get + */ + public getLinkSet(linkSetID: LinkID): AdditionalLinkSet | undefined { + return this.additionalLinkSets.get(linkSetID) + } +} diff --git a/packages/hoppscotch-selfhost-web/src/main.ts b/packages/hoppscotch-selfhost-web/src/main.ts index 08f73c61..39927b1f 100644 --- a/packages/hoppscotch-selfhost-web/src/main.ts +++ b/packages/hoppscotch-selfhost-web/src/main.ts @@ -26,6 +26,7 @@ import { AgentKernelInterceptorService } from "@hoppscotch/common/platform/std/k import { ProxyKernelInterceptorService } from "@hoppscotch/common/platform/std/kernel-interceptors/proxy" import { ExtensionKernelInterceptorService } from "@hoppscotch/common/platform/std/kernel-interceptors/extension" import { BrowserKernelInterceptorService } from "@hoppscotch/common/platform/std/kernel-interceptors/browser" +import { HeaderDownloadableLinksService } from "./services/headerDownloadableLinks.service" type Platform = "web" | "desktop" @@ -98,6 +99,7 @@ async function initApp() { }, infra: InfraPlatform, backend: stdBackendDef, + additionalLinks: [HeaderDownloadableLinksService], }) if (kernelMode === "desktop") { diff --git a/packages/hoppscotch-selfhost-web/src/services/headerDownloadableLinks.service.ts b/packages/hoppscotch-selfhost-web/src/services/headerDownloadableLinks.service.ts new file mode 100644 index 00000000..b9dd9fe8 --- /dev/null +++ b/packages/hoppscotch-selfhost-web/src/services/headerDownloadableLinks.service.ts @@ -0,0 +1,101 @@ +import { Service } from "dioc" +import { computed, markRaw, Ref, ref } from "vue" +import { installPWA, pwaDefferedPrompt } from "@hoppscotch/common/modules/pwa" +import IconGlobe from "~icons/lucide/globe" +import IconCLI from "~icons/lucide/square-terminal" +import IconApple from "~icons/hopp/apple" +import IconWindows from "~icons/hopp/windows" +import IconLinux from "~icons/hopp/linux" + +import { + AdditionalLinkSet, + AdditionalLinksService, + Link, +} from "@hoppscotch/common/services/additionalLinks.service" + +const macOS: Link = { + id: "whats-new", + text: (t) => t("app.additional_links.macOS"), + icon: markRaw(IconApple), + action: { + type: "link", + href: "https://hoppscotch.com/download?platform=macOS", + }, +} + +const windows: Link = { + id: "windows", + text: (t) => t("app.additional_links.windows"), + icon: markRaw(IconWindows), + action: { + type: "link", + href: "https://hoppscotch.com/download?platform=windows", + }, +} + +const linux: Link = { + id: "linux", + text: (t) => t("app.additional_links.linux"), + icon: markRaw(IconLinux), + action: { + type: "link", + href: "https://hoppscotch.com/download?platform=linux", + }, +} + +const pwa: Link = { + id: "pwa", + text: (t) => t("app.additional_links.web_app"), + icon: IconGlobe, + action: { + type: "custom", + do: () => { + installPWA() + }, + }, + show: computed(() => !!pwaDefferedPrompt.value), +} + +const cli: Link = { + id: "cli", + text: (t) => t("app.additional_links.cli"), + icon: IconCLI, + action: { + type: "link", + href: "https://docs.hoppscotch.io/documentation/clients/cli/overview", + }, +} + +/** + * Service to manage the downloadable links in the app header. + */ +export class HeaderDownloadableLinksService + extends Service + implements AdditionalLinkSet +{ + public static readonly ID = "HEADER_DOWNLOADABLE_LINKS_SERVICE" + public readonly linkSetID = "HEADER_DOWNLOADABLE_LINKS" + + private readonly additionalLinkSet = this.bind(AdditionalLinksService) + + /** + * List of downloadable links to be shown in the header + * This includes showing the link to the desktop app, PWA, CLI. + */ + private headerDownloadableLinks = ref([ + macOS, + windows, + linux, + pwa, + cli, + ]) + + override onServiceInit() { + this.additionalLinkSet.registerAdditionalSet(this) + } + + getLinks(): Ref { + // @ts-expect-error show type not recognizing ComputedRef + return this.headerDownloadableLinks + } +}