From 281201ee756d730c9140f9b92fc929613d51cb1c Mon Sep 17 00:00:00 2001 From: Chen Junda Date: Sat, 10 Aug 2024 15:01:15 +0800 Subject: [PATCH 1/5] implement autorefresh --- libs/web/src/extensions/UiExtensionStore.ts | 5 +- libs/web/src/extensions/common.ts | 9 +- libs/web/src/extensions/manifests.ts | 12 +- libs/web/src/extensions/navbarLinks.tsx | 7 +- libs/web/src/extensions/navigations.tsx | 4 +- libs/web/src/layouts/base/BaseLayout.tsx | 68 ++--------- .../src/layouts/base/header/components.tsx | 3 + libs/web/src/layouts/base/header/index.tsx | 115 +++++++++++++++++- libs/web/src/layouts/icon.tsx | 1 + 9 files changed, 153 insertions(+), 71 deletions(-) diff --git a/libs/web/src/extensions/UiExtensionStore.ts b/libs/web/src/extensions/UiExtensionStore.ts index 6603474453..b209fc14f5 100644 --- a/libs/web/src/extensions/UiExtensionStore.ts +++ b/libs/web/src/extensions/UiExtensionStore.ts @@ -13,7 +13,7 @@ import { UiExtensionConfigSchema } from "@scow/config/build/uiExtensions"; import { useCallback } from "react"; import { useAsync } from "react-async"; -import { fetchExtensionManifests } from "src/extensions/manifests"; +import { ExtensionManifestsSchema, fetchExtensionManifests } from "src/extensions/manifests"; const fetchManifestsWithErrorHandling = (url: string, name?: string): Promise => fetchExtensionManifests(url) @@ -21,7 +21,8 @@ const fetchManifestsWithErrorHandling = (url: string, name?: string): Promise { console.error(`Error fetching extension manifests. ${e}`); return undefined; }); export interface ExtensionManifestWithUrl { - url: string; manifests: Awaited> + url: string; + manifests: ExtensionManifestsSchema; name?: string; } diff --git a/libs/web/src/extensions/common.ts b/libs/web/src/extensions/common.ts index 06d7e13124..f5d19843a1 100644 --- a/libs/web/src/extensions/common.ts +++ b/libs/web/src/extensions/common.ts @@ -12,13 +12,13 @@ import { z } from "zod"; -export const ScowExtensionRouteContext = z.object({ +export const ExtensionRouteQuery = z.object({ scowUserToken: z.string().optional(), scowDark: z.enum(["true", "false"]), scowLangId: z.string(), }); -export type ScowExtensionRouteContext = z.infer; +export type ExtensionRouteQuery = z.infer; export function isUrl(input: string): boolean { try { @@ -29,3 +29,8 @@ export function isUrl(input: string): boolean { } } +export const getExtensionRouteQuery = (dark: boolean, languageId: string, userToken?: string) => ({ + scowDark: dark ? "true" : "false", + scowLangId: languageId, + scowUserToken: userToken, +}) as ExtensionRouteQuery; diff --git a/libs/web/src/extensions/manifests.ts b/libs/web/src/extensions/manifests.ts index 6477e7321b..e67f21bde3 100644 --- a/libs/web/src/extensions/manifests.ts +++ b/libs/web/src/extensions/manifests.ts @@ -15,7 +15,17 @@ import { z } from "zod"; export const CommonExtensionManifestsSchema = z.object({ rewriteNavigations: z.boolean().default(false), - navbarLinks: z.boolean().default(false), + + navbarLinks: z.union([ + z.boolean(), + z.object({ + enabled: z.boolean().default(false), + autoRefresh: z.optional(z.object({ + intervalMs: z.number(), + })), + }), + ]).default(false), + }); export const ExtensionManifestsSchema = z.object({ diff --git a/libs/web/src/extensions/navbarLinks.tsx b/libs/web/src/extensions/navbarLinks.tsx index 1854247807..72bb78e4bb 100644 --- a/libs/web/src/extensions/navbarLinks.tsx +++ b/libs/web/src/extensions/navbarLinks.tsx @@ -10,7 +10,7 @@ * See the Mulan PSL v2 for more details. */ -import { ScowExtensionRouteContext } from "src/extensions/common"; +import { ExtensionRouteQuery } from "src/extensions/common"; import { defineExtensionRoute } from "src/extensions/routes"; import { z } from "zod"; @@ -23,6 +23,9 @@ export const NavbarLink = z.object({ })), openInNewPage: z.boolean().default(true), priority: z.number().default(0), + autoRefresh: z.optional(z.object({ + intervalMs: z.number(), + })), }); export type NavbarLink = z.infer; @@ -30,7 +33,7 @@ export type NavbarLink = z.infer; export const navbarLinksRoute = (from: "portal" | "mis") => defineExtensionRoute({ path: `/${from}/navbarLinks`, method: "POST" as const, - query: ScowExtensionRouteContext, + query: ExtensionRouteQuery, responses: { 200: z.object({ navbarLinks: z.optional(z.array(NavbarLink)), diff --git a/libs/web/src/extensions/navigations.tsx b/libs/web/src/extensions/navigations.tsx index a6ce70be56..2f91b7d5a9 100644 --- a/libs/web/src/extensions/navigations.tsx +++ b/libs/web/src/extensions/navigations.tsx @@ -12,7 +12,7 @@ import { LinkOutlined } from "@ant-design/icons"; import { join } from "path"; -import { isUrl, ScowExtensionRouteContext } from "src/extensions/common"; +import { ExtensionRouteQuery,isUrl } from "src/extensions/common"; import { defineExtensionRoute } from "src/extensions/routes"; import { NavItemProps } from "src/layouts/base/types"; import { NavIcon } from "src/layouts/icon"; @@ -40,7 +40,7 @@ export const NavItem = BaseNavItem.extend({ export const rewriteNavigationsRoute = (from: "portal" | "mis") => defineExtensionRoute({ path: `/${from}/rewriteNavigations`, method: "POST" as const, - query: ScowExtensionRouteContext, + query: ExtensionRouteQuery, body: z.object({ navs: z.array(NavItem) as z.ZodType, }), diff --git a/libs/web/src/layouts/base/BaseLayout.tsx b/libs/web/src/layouts/base/BaseLayout.tsx index a597aa3371..e9845562b1 100644 --- a/libs/web/src/layouts/base/BaseLayout.tsx +++ b/libs/web/src/layouts/base/BaseLayout.tsx @@ -12,15 +12,12 @@ "use client"; -import { LinkOutlined } from "@ant-design/icons"; import { arrayContainsElement } from "@scow/utils"; import { Grid, Layout } from "antd"; import { useRouter } from "next/router"; -import { join } from "path"; import React, { PropsWithChildren, useCallback, useMemo, useState } from "react"; import { useAsync } from "react-async"; -import { isUrl, ScowExtensionRouteContext } from "src/extensions/common"; -import { NavbarLink, navbarLinksRoute } from "src/extensions/navbarLinks"; +import { getExtensionRouteQuery } from "src/extensions/common"; import { fromNavItemProps, rewriteNavigationsRoute, toNavItemProps } from "src/extensions/navigations"; import { callExtensionRoute } from "src/extensions/routes"; import { UiExtensionStoreData } from "src/extensions/UiExtensionStore"; @@ -30,7 +27,6 @@ import { match } from "src/layouts/base/matchers"; import { SideNav } from "src/layouts/base/SideNav"; import { NavItemProps, UserInfo, UserLink } from "src/layouts/base/types"; import { useDarkMode } from "src/layouts/darkMode"; -import { NavIcon } from "src/layouts/icon"; import { styled } from "styled-components"; // import logo from "src/assets/logo-no-text.svg"; const { useBreakpoint } = Grid; @@ -93,11 +89,11 @@ export const BaseLayout: React.FC> = ({ ? extensionStoreData : extensionStoreData ? [extensionStoreData] : [], [extensionStoreData]); - const routeQuery = useMemo(() => ({ - scowDark: dark.dark ? "true" : "false", - scowLangId: languageId, - scowUserToken: user?.token, - }) as ScowExtensionRouteContext, [dark.dark, languageId, user?.token]); + const routeQuery = useMemo(() => getExtensionRouteQuery( + dark.dark, + languageId, + user?.token, + ), [dark.dark, languageId, user?.token]); const { data: finalRoutesData } = useAsync({ promiseFn: useCallback(async () => { @@ -132,56 +128,11 @@ export const BaseLayout: React.FC> = ({ const hasSidebar = arrayContainsElement(sidebarRoutes); - // navbar links - const { data: extensionNavbarLinks } = useAsync({ - promiseFn: useCallback(async () => { - if (extensions.length === 0) { return undefined; } - - const result = await Promise.all(extensions.map(async (extension) => { - const resp = await callExtensionRoute(navbarLinksRoute(from), routeQuery, {}, extension.url) - .catch((e) => { - console.warn(`Failed to call navbarLinks of extension ${extension.name ?? extension.url}. Error: `, e); - return { 200: { navbarLinks: [] as NavbarLink[] } }; - }); - - if (resp[200]) { - return resp[200].navbarLinks?.map((x) => { - - if (!isUrl(x.path)) { - const parts = ["/extensions"]; - - if (extension.name) { - parts.push(extension.name); - } - - parts.push(x.path); - x.path = join(...parts); - } - - return x; - }); - } - })); - - const filtered = result.flat().filter((x) => x) as NavbarLink[]; - - // order by priority and index. sort is stable, index is preserved - filtered.sort((a, b) => { - return b.priority - a.priority; - }); - - return filtered.map((x) => ({ - href: x.path, - text: x.text, - icon: x.icon ? : , - }satisfies HeaderNavbarLink)); - - }, [from, routeQuery, extensions]), - }); - return (
> = ({ userLinks={userLinks} languageId={languageId} right={headerRightContent} - navbarLinks={[...extensionNavbarLinks ?? [], ...headerNavbarLinks ?? []]} + staticNavbarLinks={headerNavbarLinks} + from={from} /> { diff --git a/libs/web/src/layouts/base/header/components.tsx b/libs/web/src/layouts/base/header/components.tsx index 21aa5736a1..1319e91803 100644 --- a/libs/web/src/layouts/base/header/components.tsx +++ b/libs/web/src/layouts/base/header/components.tsx @@ -58,6 +58,9 @@ interface JumpToAnotherLinkProps { text: string; hideText?: boolean; crossSystem?: boolean; + autoRefresh?: { + intervalMs: number; + } } export const JumpToAnotherLink: React.FC = ({ href, icon, text, hideText, crossSystem }) => { diff --git a/libs/web/src/layouts/base/header/index.tsx b/libs/web/src/layouts/base/header/index.tsx index 9a0e09964c..276db4f2fc 100644 --- a/libs/web/src/layouts/base/header/index.tsx +++ b/libs/web/src/layouts/base/header/index.tsx @@ -10,15 +10,22 @@ * See the Mulan PSL v2 for more details. */ -import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons"; +import { LinkOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons"; import { Space } from "antd"; -import React from "react"; +import { join } from "path"; +import React, { useCallback, useState } from "react"; +import { useAsync } from "react-async"; +import { ExtensionRouteQuery, isUrl } from "src/extensions/common"; +import { NavbarLink, navbarLinksRoute } from "src/extensions/navbarLinks"; +import { callExtensionRoute } from "src/extensions/routes"; +import { ExtensionManifestWithUrl } from "src/extensions/UiExtensionStore"; import { antdBreakpoints } from "src/layouts/base/constants"; import { BigScreenMenu } from "src/layouts/base/header/BigScreenMenu"; import { HeaderItem, JumpToAnotherLink } from "src/layouts/base/header/components"; import { Logo } from "src/layouts/base/header/Logo"; import { UserIndicator } from "src/layouts/base/header/UserIndicator"; import { NavItemProps, UserInfo, UserLink } from "src/layouts/base/types"; +import { NavIcon } from "src/layouts/icon"; import { styled } from "styled-components"; interface ComponentProps { @@ -85,9 +92,17 @@ interface Props { userLinks?: UserLink[]; languageId: string; right?: React.ReactNode; - navbarLinks?: HeaderNavbarLink[]; + staticNavbarLinks?: HeaderNavbarLink[]; + extensions: ExtensionManifestWithUrl[]; + from: "mis" | "portal"; + routeQuery: ExtensionRouteQuery; } +interface SourcedHeaderNavbarLink { + link: HeaderNavbarLink; + extension: ExtensionManifestWithUrl; + priority: number; +}; export const Header: React.FC = ({ hasSidebar, routes, @@ -95,9 +110,36 @@ export const Header: React.FC = ({ pathname, user, logout, basePath, userLinks, languageId, - navbarLinks, right, + staticNavbarLinks, right, + extensions, from, routeQuery, }) => { + const [links, setLinks] = useState([]); + + const onFetched = (extension: ExtensionManifestWithUrl) => (data: NavbarLink[]) => { + setLinks((links) => { + + // remove all existing links from the same extension + links = links.filter((x) => x.extension !== extension); + + // append newly got links + links.push(...data.map((x) => ({ link: { + href: x.path, + text: x.text, + icon: x.icon ? : , + }, extension, priority: x.priority }))); + + // order by priority and index. sort is stable, index is preserved + links.sort((a, b) => { + return b.priority - a.priority; + }); + + return links; + }); + }; + + const navbarLinks = [...links.map((x) => x.link), ...(staticNavbarLinks ?? [])]; + const hideLinkText = navbarLinks && navbarLinks.length >= 5; const navbarLinkComponents = navbarLinks?.map((x, i) => { @@ -116,6 +158,23 @@ export const Header: React.FC = ({ return ( + {extensions.map((extension) => { + const navbarLinksConfig = extension.manifests[from]?.navbarLinks; + + if (navbarLinksConfig === true || (typeof navbarLinksConfig === "object" && navbarLinksConfig?.enabled)) { + return ( + + ); + } else { + return undefined; + } + })} {hasSidebar @@ -148,3 +207,51 @@ export const Header: React.FC = ({ ); }; + + +interface FetcherProps { + extension: ExtensionManifestWithUrl; + from: "mis" | "portal"; + routeQuery: ExtensionRouteQuery; + onDataFetched: (links: NavbarLink[]) => void; +} + +const NavbarLinkFetcher = ({ extension, from, routeQuery, onDataFetched }: FetcherProps) => { + + const { reload } = useAsync({ + promiseFn: useCallback(async () => { + const resp = await callExtensionRoute(navbarLinksRoute(from), routeQuery, {}, extension.url) + .catch((e) => { + console.warn(`Failed to call navbarLinks of extension ${extension.name ?? extension.url}. Error: `, e); + return { 200: { navbarLinks: [] as NavbarLink[] } }; + }); + + const data = resp[200]?.navbarLinks?.map((x) => { + + if (!isUrl(x.path)) { + const parts = ["/extensions"]; + + if (extension.name) { + parts.push(extension.name); + } + + parts.push(x.path); + x.path = join(...parts); + } + + return x; + }); + + onDataFetched(data ?? []); + + const navbarLinksConfig = extension.manifests[from]?.navbarLinks; + + if (typeof navbarLinksConfig === "object" && navbarLinksConfig?.enabled && navbarLinksConfig.autoRefresh) { + setTimeout(reload, navbarLinksConfig.autoRefresh.intervalMs); + } + + }, [from, routeQuery, extension]), + }); + + return <>; +}; diff --git a/libs/web/src/layouts/icon.tsx b/libs/web/src/layouts/icon.tsx index d159bddbb7..77c511ad0c 100644 --- a/libs/web/src/layouts/icon.tsx +++ b/libs/web/src/layouts/icon.tsx @@ -18,6 +18,7 @@ import { useDarkMode } from "./darkMode"; interface Props { src: any; alt?: string; + autoRefreshIntervalMs?: number; } export function NavIcon({ src, alt = "" }: Props) { From bca333f342ed3ef069c83b54bc166a96648a69eb Mon Sep 17 00:00:00 2001 From: Chen Junda Date: Sat, 10 Aug 2024 15:11:11 +0800 Subject: [PATCH 2/5] add docs --- .changeset/bright-cameras-matter.md | 6 ++++ docs/docs/integration/ui-extension/develop.md | 29 +++++++++++++------ libs/web/src/extensions/manifests.ts | 1 + libs/web/src/layouts/base/header/index.tsx | 3 +- 4 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 .changeset/bright-cameras-matter.md diff --git a/.changeset/bright-cameras-matter.md b/.changeset/bright-cameras-matter.md new file mode 100644 index 0000000000..b58c9caa23 --- /dev/null +++ b/.changeset/bright-cameras-matter.md @@ -0,0 +1,6 @@ +--- +"@scow/lib-web": patch +"@scow/docs": patch +--- + +UI扩展增加导航栏链接自动刷新功能 diff --git a/docs/docs/integration/ui-extension/develop.md b/docs/docs/integration/ui-extension/develop.md index 6fa6f70cc8..dc57250c4f 100644 --- a/docs/docs/integration/ui-extension/develop.md +++ b/docs/docs/integration/ui-extension/develop.md @@ -41,16 +41,19 @@ UI扩展的功能应实现为标准的网页。当访问SCOW的扩展路径时 对于此接口,您需要返回如下类型的JSON内容: -| JSON属性路径 | 类型 | 是否必须 | 解释 | -| --------------------------- | ------ | -------- | --------------------------------------------------- | -| `portal` | 对象 | 否 | 关于门户系统的配置 | -| `portal.rewriteNavigations` | 布尔值 | 否 | 是否重写门户系统的导航项。默认为`false` | -| `portal.navbarLinks` | 布尔值 | 否 | 是否在门户系统中增加导航栏右侧的链接。默认为`false` | -| `mis` | 对象 | 否 | 关于管理系统的配置 | -| `mis.rewriteNavigations` | 布尔值 | 否 | 是否重写管理系统的导航项。默认为`false` | -| `mis.navbarLinks` | 布尔值 | 否 | 是否在管理系统中增加导航栏右侧的链接。默认为`false` | +| JSON属性路径 | 类型 | 是否必须 | 解释 | +| ------------------------------------------- | -------------- | -------- | ------------------------------------------------------ | +| `portal` | 对象 | 否 | 关于门户系统的配置 | +| `portal.rewriteNavigations` | 布尔值 | 否 | 是否重写门户系统的导航项。默认为`false` | +| `portal.navbarLinks` | 布尔值或者对象 | 否 | 是否在门户系统中增加导航栏右侧的链接。默认为`false` | +| `portal.navbarLinks.enabled` | 布尔值 | 否 | 是否在管理系统中增加导航栏右侧的链接。默认为`false` | +| `portal.navbarLinks.autoRefresh` | 对象 | 否 | 是否定时自动刷新导航栏右侧的链接。不设置为不定时刷新。 | +| `portal.navbarLinks.autoRefresh.enabled` | 布尔值 | 否 | 是否定时自动刷新导航栏右侧的链接。默认为`false` | +| `portal.navbarLinks.autoRefresh.intervalMs` | 数字 | 否 | 定时刷新导航栏右侧的链接的间隔,单位`ms` | -例如,您可以返回如下类型的JSON,表示在门户系统中重写导航项并增加导航栏右侧的链接,在管理系统中不重写管理系统的导航项,也不增加导航栏右侧的链接。 +将路径中的`portal`改为`mis`即设置管理系统的配置,门户系统和管理系统的配置完全一致。 + +例如,您可以返回如下类型的JSON,表示在门户系统中重写导航项并增加导航栏右侧的链接,在管理系统中不重写管理系统的导航项,增加导航栏右侧的链接,并且开启自动刷新,每5s自动刷新一次。 ```json { @@ -60,6 +63,13 @@ UI扩展的功能应实现为标准的网页。当访问SCOW的扩展路径时 }, "mis": { "rewriteNavigations": false, + "navbarLinks": { + "enabled": true, + "autoRefresh": { + "enabled": true, + "intervalMs": 5000, + } + } } } ``` @@ -161,6 +171,7 @@ SCOW在调用接口时,会将[上下文参数](#上下文参数)作为查询 #### 其他注意事项 - 当右上角导航栏链接数量**大于等于5个**,或者屏幕宽度小于**768px**时,所有导航栏链接将会仅显示图标。 +- 如果您开启了导航栏链接自动刷新功能,并希望每次都重新加载图标,请确保每次返回的图标的链接要有变化,否则浏览器缓存将不会重新刷新图标。 ## 扩展消息 diff --git a/libs/web/src/extensions/manifests.ts b/libs/web/src/extensions/manifests.ts index e67f21bde3..61ac746138 100644 --- a/libs/web/src/extensions/manifests.ts +++ b/libs/web/src/extensions/manifests.ts @@ -21,6 +21,7 @@ export const CommonExtensionManifestsSchema = z.object({ z.object({ enabled: z.boolean().default(false), autoRefresh: z.optional(z.object({ + enabled: z.boolean().default(false), intervalMs: z.number(), })), }), diff --git a/libs/web/src/layouts/base/header/index.tsx b/libs/web/src/layouts/base/header/index.tsx index 276db4f2fc..28951c01e3 100644 --- a/libs/web/src/layouts/base/header/index.tsx +++ b/libs/web/src/layouts/base/header/index.tsx @@ -246,7 +246,8 @@ const NavbarLinkFetcher = ({ extension, from, routeQuery, onDataFetched }: Fetch const navbarLinksConfig = extension.manifests[from]?.navbarLinks; - if (typeof navbarLinksConfig === "object" && navbarLinksConfig?.enabled && navbarLinksConfig.autoRefresh) { + if (typeof navbarLinksConfig === "object" + && navbarLinksConfig?.enabled && navbarLinksConfig.autoRefresh?.enabled) { setTimeout(reload, navbarLinksConfig.autoRefresh.intervalMs); } From ac6bd58d4eccd25912e41e0d96bfa39abe59c32b Mon Sep 17 00:00:00 2001 From: Chen Junda Date: Sat, 10 Aug 2024 15:14:33 +0800 Subject: [PATCH 3/5] remove unnecessary changes --- libs/web/src/layouts/base/header/components.tsx | 3 --- libs/web/src/layouts/icon.tsx | 1 - 2 files changed, 4 deletions(-) diff --git a/libs/web/src/layouts/base/header/components.tsx b/libs/web/src/layouts/base/header/components.tsx index 1319e91803..21aa5736a1 100644 --- a/libs/web/src/layouts/base/header/components.tsx +++ b/libs/web/src/layouts/base/header/components.tsx @@ -58,9 +58,6 @@ interface JumpToAnotherLinkProps { text: string; hideText?: boolean; crossSystem?: boolean; - autoRefresh?: { - intervalMs: number; - } } export const JumpToAnotherLink: React.FC = ({ href, icon, text, hideText, crossSystem }) => { diff --git a/libs/web/src/layouts/icon.tsx b/libs/web/src/layouts/icon.tsx index 77c511ad0c..d159bddbb7 100644 --- a/libs/web/src/layouts/icon.tsx +++ b/libs/web/src/layouts/icon.tsx @@ -18,7 +18,6 @@ import { useDarkMode } from "./darkMode"; interface Props { src: any; alt?: string; - autoRefreshIntervalMs?: number; } export function NavIcon({ src, alt = "" }: Props) { From f4c2d4965b070b10802058815b41d8e83ed67bd7 Mon Sep 17 00:00:00 2001 From: Chen Junda Date: Sat, 10 Aug 2024 15:16:59 +0800 Subject: [PATCH 4/5] use getExtensionRouteQuery for query --- libs/web/src/extensions/ExtensionPage.tsx | 16 ++++++---------- libs/web/src/extensions/common.ts | 2 +- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/libs/web/src/extensions/ExtensionPage.tsx b/libs/web/src/extensions/ExtensionPage.tsx index c581417cbf..96e835ac2a 100644 --- a/libs/web/src/extensions/ExtensionPage.tsx +++ b/libs/web/src/extensions/ExtensionPage.tsx @@ -14,6 +14,7 @@ import { joinWithUrl } from "@scow/utils"; import { useRouter } from "next/router"; import React, { useEffect, useRef } from "react"; import { Head } from "src/components/head"; +import { getExtensionRouteQuery } from "src/extensions/common"; import { extensionEvents } from "src/extensions/events"; import { ExtensionManifestWithUrl, UiExtensionStoreData } from "src/extensions/UiExtensionStore"; import { UserInfo } from "src/layouts/base/types"; @@ -79,17 +80,12 @@ export const ExtensionPage: React.FC = ({ const darkMode = useDarkMode(); - const query = new URLSearchParams( - Object.fromEntries(Object.entries(rest).filter(([_, val]) => typeof val === "string")) as Record, - ); - - if (user) { - query.set("scowUserToken", user.token); - } - - query.set("scowDark", darkMode.dark ? "true" : "false"); + const extensionQuery = getExtensionRouteQuery(darkMode.dark, currentLanguageId, user?.token); - query.set("scowLangId", currentLanguageId); + const query = new URLSearchParams({ + ...Object.fromEntries(Object.entries(rest).filter(([_, val]) => typeof val === "string")), + ...extensionQuery, + }); const url = joinWithUrl(config.url, "extensions", ...pathParts) + "?" + query.toString(); diff --git a/libs/web/src/extensions/common.ts b/libs/web/src/extensions/common.ts index f5d19843a1..74310f1bc9 100644 --- a/libs/web/src/extensions/common.ts +++ b/libs/web/src/extensions/common.ts @@ -32,5 +32,5 @@ export function isUrl(input: string): boolean { export const getExtensionRouteQuery = (dark: boolean, languageId: string, userToken?: string) => ({ scowDark: dark ? "true" : "false", scowLangId: languageId, - scowUserToken: userToken, + ...userToken ? { scowUserToken: userToken } : {}, }) as ExtensionRouteQuery; From 9b92e0af876ce03645aa6ece4cf8364de671d2b5 Mon Sep 17 00:00:00 2001 From: Chen Junda Date: Mon, 12 Aug 2024 21:19:58 +0800 Subject: [PATCH 5/5] remove unnecessary prop --- libs/web/src/layouts/base/header/BigScreenMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/web/src/layouts/base/header/BigScreenMenu.tsx b/libs/web/src/layouts/base/header/BigScreenMenu.tsx index be1784e344..9e31f9733f 100644 --- a/libs/web/src/layouts/base/header/BigScreenMenu.tsx +++ b/libs/web/src/layouts/base/header/BigScreenMenu.tsx @@ -45,7 +45,7 @@ interface Props { } export const BigScreenMenu: React.FC = ({ - routes, className, pathname, activeKeys, + routes, className, activeKeys, }) => { const router = useRouter();