diff --git a/frontend/src/routes/Embed.tsx b/frontend/src/routes/Embed.tsx index 3a6f1930d..b389664a5 100644 --- a/frontend/src/routes/Embed.tsx +++ b/frontend/src/routes/Embed.tsx @@ -1,4 +1,4 @@ -import { ReactNode, Suspense } from "react"; +import { ReactNode, Suspense, useState } from "react"; import { LuFrown, LuAlertTriangle } from "react-icons/lu"; import { Translation, useTranslation } from "react-i18next"; import { @@ -19,7 +19,7 @@ import { EmbedQuery } from "./__generated__/EmbedQuery.graphql"; import { EmbedDirectOpencastQuery } from "./__generated__/EmbedDirectOpencastQuery.graphql"; import { EmbedEventData$key } from "./__generated__/EmbedEventData.graphql"; import { PlayerContextProvider } from "../ui/player/PlayerContext"; -import { PreviewPlaceholder } from "./Video"; +import { AuthenticatedDataContext, AuthorizedData, PreviewPlaceholder } from "./Video"; export const EmbedVideoRoute = makeRoute({ url: ({ videoId }: { videoId: string }) => `/~embed/!v/${keyOfId(videoId)}`, @@ -143,6 +143,7 @@ const Embed: React.FC = ({ query, queryRef }) => { fragmentRef.event, ); const { t } = useTranslation(); + const [authenticatedData, setAuthenticatedData] = useState(null); if (!event) { return @@ -177,7 +178,9 @@ const Embed: React.FC = ({ query, queryRef }) => { return authorizedData ? - : ; + : + ; + ; }; export const BlockEmbedRoute = makeRoute({ diff --git a/frontend/src/routes/Realm.tsx b/frontend/src/routes/Realm.tsx index d0faed771..306447f2f 100644 --- a/frontend/src/routes/Realm.tsx +++ b/frontend/src/routes/Realm.tsx @@ -27,6 +27,7 @@ import { COLORS } from "../color"; import { useMenu } from "../layout/MenuState"; import { ManageNav } from "./manage"; import { BREAKPOINT as NAV_BREAKPOINT } from "../layout/Navigation"; +import { AuthorizedData, AuthenticatedDataContext } from "./Video"; // eslint-disable-next-line @typescript-eslint/quotes @@ -145,6 +146,7 @@ const RealmPage: React.FC = ({ realm }) => { const { t } = useTranslation(); const siteTitle = useTranslatedConfig(CONFIG.siteTitle); const breadcrumbs = realmBreadcrumbs(t, realm.ancestors); + const [authenticatedData, setAuthenticatedData] = useState(null); const title = realm.isMainRoot ? siteTitle : realm.name; useTitle(title, realm.isMainRoot); @@ -166,9 +168,11 @@ const RealmPage: React.FC = ({ realm }) => { {realm.isUserRealm && } )} - {realm.blocks.length === 0 && realm.isMainRoot - ? - : } + + {realm.blocks.length === 0 && realm.isMainRoot + ? + : } + ; }; diff --git a/frontend/src/routes/Video.tsx b/frontend/src/routes/Video.tsx index bb9cc950c..5fb9e06d7 100644 --- a/frontend/src/routes/Video.tsx +++ b/frontend/src/routes/Video.tsx @@ -1,4 +1,14 @@ -import React, { ReactElement, ReactNode, useEffect, useRef, useState } from "react"; +import React, { + createContext, + Dispatch, + ReactElement, + ReactNode, + SetStateAction, + useContext, + useEffect, + useRef, + useState, +} from "react"; import { graphql, GraphQLTaggedNode, PreloadedQuery, useFragment } from "react-relay/hooks"; import { useTranslation } from "react-i18next"; import { fetchQuery, OperationType } from "relay-runtime"; @@ -9,6 +19,7 @@ import { QRCodeCanvas } from "qrcode.react"; import { match, unreachable, screenWidthAtMost, screenWidthAbove, useColorScheme, Floating, FloatingContainer, FloatingTrigger, WithTooltip, Card, Button, ProtoButton, + bug, } from "@opencast/appkit"; import { VideoObject, WithContext } from "schema-dts"; @@ -453,6 +464,13 @@ export const authorizedDataQuery = graphql` // ===== Components // =========================================================================================== +export type AuthorizedData = VideoAuthorizedDataQuery$data["authorizedEvent"]; +type AuthenticatedDataContext = { + authenticatedData: AuthorizedData; + setAuthenticatedData: Dispatch>; +} +export const AuthenticatedDataContext = createContext(null); + type Props = { eventRef: NonNullable; realmRef: NonNullable; @@ -465,6 +483,7 @@ const VideoPage: React.FC = ({ eventRef, realmRef, playlistRef, basePath const rerender = useForceRerender(); const event = useFragment(eventFragment, eventRef); const realm = useFragment(realmFragment, realmRef); + const [authenticatedData, setAuthenticatedData] = useState(null); if (event.__typename === "NotAllowed") { return ; @@ -520,34 +539,36 @@ const VideoPage: React.FC = ({ eventRef, realmRef, playlistRef, basePath return <> - - {authorizedData - ? - : - } - - + + + {authorizedData + ? + : + } + + -
+
- {playlistRef - ? - : event.series && - } + {playlistRef + ? + : event.series && + } + ; }; @@ -575,7 +596,6 @@ export const PreviewPlaceholder: React.FC = ({ event, embe
; }; -export type AuthorizedData = VideoAuthorizedDataQuery$data["authorizedEvent"]; export const CREDENTIALS_STORAGE_KEY = "tobira-video-credentials-"; const ProtectedPlayer: React.FC = ({ event, embedded }) => { @@ -584,7 +604,7 @@ const ProtectedPlayer: React.FC = ({ event, embedded }) => const user = useUser(); const [authState, setAuthState] = useState("idle"); const [authError, setAuthError] = useState(null); - const [authData, setAuthData] = useState(null); + const authenticatedDataContext = useContext(AuthenticatedDataContext); const embeddedStyles = { height: "100%", @@ -611,8 +631,15 @@ const ProtectedPlayer: React.FC = ({ event, embedded }) => return; } + if (authenticatedDataContext) { + authenticatedDataContext.setAuthenticatedData({ + authorizedData: authorizedEvent.authorizedData, + }); + } else { + bug("Authenticated data context is not initialized"); + } + setAuthError(null); - setAuthData({ authorizedData: authorizedEvent.authorizedData }); setAuthState("success"); // To make the authentication "sticky", the credentials are stored in browser @@ -646,10 +673,10 @@ const ProtectedPlayer: React.FC = ({ event, embedded }) => }); }; - return authData?.authorizedData && event.syncedData + return authenticatedDataContext?.authenticatedData?.authorizedData && event.syncedData ? : (