From 4d628a4679e35f9b8531b09110762cfdf73c865e Mon Sep 17 00:00:00 2001 From: hyrious Date: Mon, 26 Jun 2023 13:58:40 +0800 Subject: [PATCH] feat(project): add replay room url protocol (#1955) * feat(project): add replay room url protocol * refactor logic --- .../main-app/src/bootup/init-url-protocol.ts | 11 +++++++- .../src/stores/url-protocol-store.ts | 17 ++++++++++++ .../src/tasks/init-url-protocol.ts | 7 +++++ .../src/utils/hooks/use-url-app-launcher.ts | 16 +++++++++--- .../src/AppRoutes/AppRouteContainer.tsx | 3 ++- .../src/AppRoutes/WeChatRedirect.tsx | 26 ++++++++++++++----- packages/flat-pages/src/ReplayPage/index.tsx | 26 ++++++++++++++++--- packages/flat-types/src/ipc/index.ts | 5 ++++ 8 files changed, 94 insertions(+), 17 deletions(-) diff --git a/desktop/main-app/src/bootup/init-url-protocol.ts b/desktop/main-app/src/bootup/init-url-protocol.ts index 886c69abd23..089edd3d126 100644 --- a/desktop/main-app/src/bootup/init-url-protocol.ts +++ b/desktop/main-app/src/bootup/init-url-protocol.ts @@ -17,6 +17,15 @@ export default async (): Promise => { }); } }, + replayRoom: (args, innerWindow) => { + if (args.has("roomUUID") && args.has("ownerUUID") && args.has("roomType")) { + innerWindow.window.webContents.send("request-replay-room", { + roomUUID: args.get("roomUUID"), + ownerUUID: args.get("ownerUUID"), + roomType: args.get("roomType"), + }); + } + }, }); if (runtime.isMac) { @@ -106,7 +115,7 @@ class URLProtocolHandler { } } -type ActionNames = "active" | "joinRoom"; +type ActionNames = "active" | "joinRoom" | "replayRoom"; type ActionHandler = { [key in ActionNames]: (arg: URLSearchParams, innerWindow: CustomWindow) => void; }; diff --git a/desktop/renderer-app/src/stores/url-protocol-store.ts b/desktop/renderer-app/src/stores/url-protocol-store.ts index cc611091575..86dae8cdbf2 100644 --- a/desktop/renderer-app/src/stores/url-protocol-store.ts +++ b/desktop/renderer-app/src/stores/url-protocol-store.ts @@ -1,11 +1,14 @@ +import type { RoomType } from "@netless/flat-server-api"; import { makeAutoObservable } from "mobx"; export class URLProtocolStore { // Receive the roomUUID parameters from ipc that for join room. public toJoinRoomUUID: string | null; + public toReplayRoom: { roomUUID: string; ownerUUID: string; roomType: RoomType } | null; public constructor() { this.toJoinRoomUUID = null; + this.toReplayRoom = null; makeAutoObservable(this); } @@ -16,6 +19,20 @@ export class URLProtocolStore { public clearToJoinRoomUUID(): void { this.toJoinRoomUUID = null; } + + public updateToReplayRoom(roomUUID: string, ownerUUID: string, roomType: string): void { + if (this.isRoomType(roomType)) { + this.toReplayRoom = { roomUUID, ownerUUID, roomType }; + } + } + + private isRoomType(string: string): string is RoomType { + return string === "OneToOne" || string === "SmallClass" || string === "BigClass"; + } + + public clearToReplayRoom(): void { + this.toReplayRoom = null; + } } export const urlProtocolStore = new URLProtocolStore(); diff --git a/desktop/renderer-app/src/tasks/init-url-protocol.ts b/desktop/renderer-app/src/tasks/init-url-protocol.ts index 2fd1957a259..c83bb89fc41 100644 --- a/desktop/renderer-app/src/tasks/init-url-protocol.ts +++ b/desktop/renderer-app/src/tasks/init-url-protocol.ts @@ -12,6 +12,13 @@ const requestJoinRoom = (): void => { }); }; +const requestReplayRoom = (): void => { + ipcReceive("request-replay-room", ({ roomUUID, ownerUUID, roomType }) => { + urlProtocolStore.updateToReplayRoom(roomUUID, ownerUUID, roomType); + }); +}; + export const initURLProtocol = (): void => { requestJoinRoom(); + requestReplayRoom(); }; diff --git a/desktop/renderer-app/src/utils/hooks/use-url-app-launcher.ts b/desktop/renderer-app/src/utils/hooks/use-url-app-launcher.ts index 09b79dcecf1..40f1f48d0f6 100644 --- a/desktop/renderer-app/src/utils/hooks/use-url-app-launcher.ts +++ b/desktop/renderer-app/src/utils/hooks/use-url-app-launcher.ts @@ -38,7 +38,7 @@ export function useURLAppLauncher(): void { }; useAutoRun(() => { - if (!urlProtocolStore.toJoinRoomUUID) { + if (!urlProtocolStore.toJoinRoomUUID && !urlProtocolStore.toReplayRoom) { return; } @@ -47,10 +47,18 @@ export function useURLAppLauncher(): void { return; } - if (inClassRoom()?.roomUUID !== urlProtocolStore.toJoinRoomUUID) { - void joinRoomHandler(urlProtocolStore.toJoinRoomUUID, pushHistory); + if (urlProtocolStore.toJoinRoomUUID) { + if (inClassRoom()?.roomUUID !== urlProtocolStore.toJoinRoomUUID) { + void joinRoomHandler(urlProtocolStore.toJoinRoomUUID, pushHistory); + } + urlProtocolStore.clearToJoinRoomUUID(); + return; } - urlProtocolStore.clearToJoinRoomUUID(); + if (urlProtocolStore.toReplayRoom) { + pushHistory(RouteNameType.ReplayPage, urlProtocolStore.toReplayRoom); + urlProtocolStore.clearToReplayRoom(); + return; + } }); } diff --git a/packages/flat-pages/src/AppRoutes/AppRouteContainer.tsx b/packages/flat-pages/src/AppRoutes/AppRouteContainer.tsx index 64baaa19e60..8fc7924ee61 100644 --- a/packages/flat-pages/src/AppRoutes/AppRouteContainer.tsx +++ b/packages/flat-pages/src/AppRoutes/AppRouteContainer.tsx @@ -64,7 +64,8 @@ export const AppRouteContainer = observer(function AppRo const hasHeader = pageStore.name !== null && pageStore.name && routePages[pageStore.name].hasHeader; - if (isWeChatBrowser && !routeProps.location.pathname.startsWith("/join/")) { + const pathname = routeProps.location.pathname; + if (isWeChatBrowser && !pathname.startsWith("/join/") && !pathname.startsWith("/replay/")) { return ; } diff --git a/packages/flat-pages/src/AppRoutes/WeChatRedirect.tsx b/packages/flat-pages/src/AppRoutes/WeChatRedirect.tsx index 9076913c1c6..e634b79d3df 100644 --- a/packages/flat-pages/src/AppRoutes/WeChatRedirect.tsx +++ b/packages/flat-pages/src/AppRoutes/WeChatRedirect.tsx @@ -3,7 +3,7 @@ import "../JoinPage/style.less"; import logoSVG from "../JoinPage/icons/logo-sm.svg"; import openInBrowserSVG from "../JoinPage/icons/open-in-browser.svg"; -import React, { useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useLanguage, useTranslate } from "@netless/flat-i18n"; import { FLAT_DOWNLOAD_URL, @@ -12,9 +12,15 @@ import { SERVICE_URL, SERVICE_URL_CN, } from "../constants/process"; +import { isWeChatBrowser } from "../utils/user-agent"; + +export interface WeChatRedirectProps { + url?: string; + open?: boolean; +} // This is a simplified version of JoinPageMobile.tsx, it can not join room. -export const WeChatRedirect = (): React.ReactElement => { +export const WeChatRedirect = ({ url, open }: WeChatRedirectProps): React.ReactElement => { const t = useTranslate(); const language = useLanguage(); const [openCount, setOpenCount] = useState(0); @@ -22,15 +28,21 @@ export const WeChatRedirect = (): React.ReactElement => { const privacyURL = language.startsWith("zh") ? PRIVACY_URL_CN : PRIVACY_URL; const serviceURL = language.startsWith("zh") ? SERVICE_URL_CN : SERVICE_URL; - const openApp = (): void => { - window.location.href = "x-agora-flat-client://active"; - setOpenCount(openCount + 1); - }; + const openApp = useCallback((): void => { + window.location.href = url || "x-agora-flat-client://active"; + setOpenCount(count => count + 1); + }, [url]); const download = (): void => { window.location.href = FLAT_DOWNLOAD_URL; }; + useEffect(() => { + if (open) { + openApp(); + } + }, [open, openApp]); + return (
@@ -56,7 +68,7 @@ export const WeChatRedirect = (): React.ReactElement => { {t("service-policy")}
- {openCount > 0 && ( + {openCount > 0 && isWeChatBrowser && (
[open-in-browser] {t("open-in-browser")} diff --git a/packages/flat-pages/src/ReplayPage/index.tsx b/packages/flat-pages/src/ReplayPage/index.tsx index c97b7a75770..07f8d8c1c0c 100644 --- a/packages/flat-pages/src/ReplayPage/index.tsx +++ b/packages/flat-pages/src/ReplayPage/index.tsx @@ -2,13 +2,15 @@ import "./ReplayPage.less"; import classNames from "classnames"; import { observer } from "mobx-react-lite"; -import React, { useContext, useState } from "react"; +import React, { useContext, useMemo, useState } from "react"; import { RouteComponentProps, useHistory } from "react-router-dom"; +import { useWindowSize } from "react-use"; import { RoomType } from "@netless/flat-server-api"; import { LoadingPage, TopBar } from "flat-components"; -import ExitReplayConfirm from "../components/ExitReplayConfirm"; +import { WeChatRedirect } from "../AppRoutes/WeChatRedirect"; +import { ExitReplayConfirm } from "../components/ExitReplayConfirm"; import { WindowsSystemBtnContext } from "../components/StoreProvider"; import { useClassroomReplayStore } from "../utils/use-classroom-replay-store"; import { useLoginCheck } from "../utils/use-login-check"; @@ -16,6 +18,7 @@ import { ReplayList } from "./ReplayList"; import { ReplayVideo } from "./ReplayVideo"; import { ReplayWhiteboard } from "./ReplayWhiteboard"; import { withFlatServices } from "../components/FlatServicesContext"; +import { isWeChatBrowser } from "../utils/user-agent"; export type ReplayPageProps = RouteComponentProps<{ roomUUID: string; @@ -23,8 +26,23 @@ export type ReplayPageProps = RouteComponentProps<{ roomType: RoomType; }>; -export const ReplayPage = withFlatServices("whiteboard")( - observer(function ReplayPage({ match }) { +export const ReplayPage = observer(function ReplayPage({ ...props }) { + const { width } = useWindowSize(1080); + + const url = useMemo(() => { + const { roomUUID, ownerUUID, roomType } = props.match.params; + return `x-agora-flat-client://replayRoom?roomUUID=${roomUUID}&ownerUUID=${ownerUUID}&roomType=${roomType}`; + }, [props.match]); + + if (isWeChatBrowser || width < 480) { + return ; + } + + return ; +}); + +export const ReplayPageImpl = withFlatServices("whiteboard")( + observer(function ReplayPageImpl({ match }) { useLoginCheck(); const history = useHistory(); diff --git a/packages/flat-types/src/ipc/index.ts b/packages/flat-types/src/ipc/index.ts index 38599d32b53..3aca43156f3 100644 --- a/packages/flat-types/src/ipc/index.ts +++ b/packages/flat-types/src/ipc/index.ts @@ -56,4 +56,9 @@ export interface EmitEvents { "request-join-room": { roomUUID: string; }; + "request-replay-room": { + roomUUID: string; + ownerUUID: string; + roomType: string; + }; }