diff --git a/apps/desktop-frontend/src/app/LeftMenuSidebar.tsx b/apps/desktop-frontend/src/app/LeftMenuSidebar.tsx index 3dea40f8..3b9f044d 100644 --- a/apps/desktop-frontend/src/app/LeftMenuSidebar.tsx +++ b/apps/desktop-frontend/src/app/LeftMenuSidebar.tsx @@ -51,10 +51,6 @@ export function LeftMenuSidebar() { // These are not centered onClick={handleLinkClick("/analyzer")} color={buttonColor("/analyzer")} - // sx={{ - // display: "flex", - // justifyContent: "center", - // }} > diff --git a/apps/desktop-frontend/src/app/RootSaga.ts b/apps/desktop-frontend/src/app/RootSaga.ts index f479724f..38ac2b27 100644 --- a/apps/desktop-frontend/src/app/RootSaga.ts +++ b/apps/desktop-frontend/src/app/RootSaga.ts @@ -3,6 +3,7 @@ import { API_BASE_URL } from "./backend/constants"; import { BackendState, stateChanged } from "./backend/slice"; import { SagaIterator } from "redux-saga"; import { push } from "connected-react-router"; +import { RewindTheater } from "@rewind/web-player/rewind"; function* waitForBackendState(state: BackendState): SagaIterator { while (true) { @@ -13,10 +14,12 @@ function* waitForBackendState(state: BackendState): SagaIterator { } } -function* watchForBackendReady(): SagaIterator { +function* watchForBackendReady(theater: RewindTheater): SagaIterator { + const { common, analyzer } = theater; yield call(waitForBackendState, "READY"); - // yield spawn(watchReplaysAdded, REWIND_WS_URL); yield put(push("/home")); // Theater + yield call(common.initialize.bind(common)); + yield call(analyzer.initialize.bind(analyzer)); } function* watchForBackendMissingSetup(): SagaIterator { @@ -52,10 +55,10 @@ function* busyPollBackendState(): SagaIterator { } } -export function createRewindRootSaga() { +export function createRewindRootSaga({ theater }: { theater: RewindTheater }) { return function* () { yield spawn(watchForBackendMissingSetup); - yield spawn(watchForBackendReady); + yield spawn(watchForBackendReady, theater); yield spawn(busyPollBackendState); }; } diff --git a/apps/desktop-frontend/src/app/store.tsx b/apps/desktop-frontend/src/app/store.tsx index 766215e0..1c86199a 100644 --- a/apps/desktop-frontend/src/app/store.tsx +++ b/apps/desktop-frontend/src/app/store.tsx @@ -8,6 +8,7 @@ import { createHashHistory } from "history"; import { connectRouter, routerMiddleware } from "connected-react-router"; import { rewindDesktopApi } from "./backend/api"; import { setupListeners } from "@reduxjs/toolkit/query"; +import { theater } from "./theater"; export const history = createHashHistory({}); @@ -35,7 +36,7 @@ const store = configureStore({ setupListeners(store.dispatch); -sagaMiddleware.run(createRewindRootSaga()); +sagaMiddleware.run(createRewindRootSaga({ theater })); export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; diff --git a/apps/desktop-frontend/src/app/theater.ts b/apps/desktop-frontend/src/app/theater.ts new file mode 100644 index 00000000..d04f4808 --- /dev/null +++ b/apps/desktop-frontend/src/app/theater.ts @@ -0,0 +1,4 @@ +import { createRewindTheater } from "@rewind/web-player/rewind"; + +const apiUrl = "http://localhost:7271"; +export const theater = createRewindTheater({ apiUrl }); diff --git a/apps/desktop-frontend/src/main.tsx b/apps/desktop-frontend/src/main.tsx index 90c97b51..bcfbed3c 100644 --- a/apps/desktop-frontend/src/main.tsx +++ b/apps/desktop-frontend/src/main.tsx @@ -9,6 +9,7 @@ import { RewindTheme, TheaterProvider } from "@rewind/feature-replay-viewer"; import { RewindApp } from "./app/RewindApp"; import { FrontendPreloadAPI } from "@rewind/electron/api"; import { CssBaseline, ThemeProvider } from "@mui/material"; +import { theater } from "./app/theater"; declare global { interface Window { @@ -22,8 +23,7 @@ ReactDOM.render( - {/*todo: just create theater outside and pass it in lol*/} - + diff --git a/apps/web/src/app/webTestApp.tsx b/apps/web/src/app/webTestApp.tsx index 30d906d8..654ac6c3 100644 --- a/apps/web/src/app/webTestApp.tsx +++ b/apps/web/src/app/webTestApp.tsx @@ -1,5 +1,5 @@ import { Box } from "@mui/material"; -import { Analyzer, useAnalysisApp, useTheater } from "@rewind/feature-replay-viewer"; +import { Analyzer, useAnalysisApp, useCommonManagers } from "@rewind/feature-replay-viewer"; import { useEffect } from "react"; import { SkinId } from "@rewind/web-player/rewind"; @@ -16,7 +16,7 @@ const chosenReplayId = centipede; const skin: SkinId = { source: "osu", name: "- # re;owoTuna v1.1 『Selyu』 # -" }; export function WebTestApp() { - const theater = useTheater(); + const theater = useCommonManagers(); const analyzer = useAnalysisApp(); useEffect(() => { // theater.changeSkin(skin); diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 17aabb3c..2ca1738c 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -4,13 +4,18 @@ import * as ReactDOM from "react-dom"; import { WebTestApp } from "./app/webTestApp"; import { environment } from "./environments/environment"; import { CssBaseline, ThemeProvider } from "@mui/material"; +import { createRewindTheater } from "@rewind/web-player/rewind"; -// TODO: process.env.URL -const url = environment.url; +// This project assumes that the backend is already initialized + +const apiUrl = environment.url; +export const theater = createRewindTheater({ apiUrl }); +theater.common.initialize(); +theater.analyzer.initialize(); ReactDOM.render( - + diff --git a/libs/feature-replay-viewer/src/components/PlayBar.tsx b/libs/feature-replay-viewer/src/components/PlayBar.tsx index 41d24dfd..fd5158cd 100644 --- a/libs/feature-replay-viewer/src/components/PlayBar.tsx +++ b/libs/feature-replay-viewer/src/components/PlayBar.tsx @@ -256,7 +256,7 @@ function GameTimeSlider() { return ( = { }; function BeatmapBackgroundSettings() { - const theater = useTheater(); + const theater = useCommonManagers(); const { beatmapBackgroundSettingsStore } = theater; const settings = useObservable(() => beatmapBackgroundSettingsStore.settings$, { blur: 0, enabled: false, dim: 0 }); return ( @@ -55,7 +55,7 @@ function BeatmapBackgroundSettings() { } function BeatmapRenderSettings() { - const { beatmapRenderSettingsStore } = useTheater(); + const { beatmapRenderSettingsStore } = useCommonManagers(); const settings = useObservable(() => beatmapRenderSettingsStore.settings$, DEFAULT_BEATMAP_RENDER_SETTINGS); return ( @@ -77,7 +77,7 @@ function BeatmapRenderSettings() { } function AnalysisCursorSettingsPanel() { - const { analysisCursorSettingsStore } = useTheater(); + const { analysisCursorSettingsStore } = useCommonManagers(); const settings = useObservable(() => analysisCursorSettingsStore.settings$, DEFAULT_ANALYSIS_CURSOR_SETTINGS); return ( @@ -98,7 +98,7 @@ function AnalysisCursorSettingsPanel() { } function ReplayCursorSettingsPanel() { - const { replayCursorSettingsStore } = useTheater(); + const { replayCursorSettingsStore } = useCommonManagers(); const settings = useObservable(() => replayCursorSettingsStore.settings$, DEFAULT_REPLAY_CURSOR_SETTINGS); return ( @@ -140,7 +140,7 @@ function GeneralSettings() { function SkinsSettings() { // TODO: Button for synchronizing skin list again - const theater = useTheater(); + const theater = useCommonManagers(); const { preferredSkinId } = useObservable(() => theater.skinSettingsStore.settings$, DEFAULT_SKIN_SETTINGS); const chosenSkinId = stringToSkinId(preferredSkinId); diff --git a/libs/feature-replay-viewer/src/hooks/audio.ts b/libs/feature-replay-viewer/src/hooks/audio.ts index edf2c9e0..c84e05e2 100644 --- a/libs/feature-replay-viewer/src/hooks/audio.ts +++ b/libs/feature-replay-viewer/src/hooks/audio.ts @@ -1,9 +1,9 @@ -import { useTheater } from "../providers/TheaterProvider"; +import { useCommonManagers } from "../providers/TheaterProvider"; import { useObservable } from "rxjs-hooks"; import { AudioSettings } from "@rewind/web-player/rewind"; export function useAudioSettingsService() { - const theater = useTheater(); + const theater = useCommonManagers(); return theater.audioSettingsService; } diff --git a/libs/feature-replay-viewer/src/providers/TheaterProvider.tsx b/libs/feature-replay-viewer/src/providers/TheaterProvider.tsx index df6e384f..a22705b1 100644 --- a/libs/feature-replay-viewer/src/providers/TheaterProvider.tsx +++ b/libs/feature-replay-viewer/src/providers/TheaterProvider.tsx @@ -1,23 +1,16 @@ -import React, { createContext, useContext, useEffect, useState } from "react"; -import { createRewindTheater } from "@rewind/web-player/rewind"; - -type ITheaterContext = ReturnType; +import React, { createContext, useContext } from "react"; +import { RewindTheater } from "@rewind/web-player/rewind"; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -export const TheaterContext = createContext(null!); +export const TheaterContext = createContext(null!); interface TheaterProviderProps { - apiUrl: string; + theater: RewindTheater; children: React.ReactNode; } -export function TheaterProvider({ apiUrl, children }: TheaterProviderProps) { - const [rewind] = useState(() => createRewindTheater({ apiUrl })); - useEffect(() => { - rewind.theater.initialize().then(() => console.log("Theater initialized")); - rewind.analyzer.initialize(); - }, [rewind]); - return {children}; +export function TheaterProvider({ theater, children }: TheaterProviderProps) { + return {children}; } export function useTheaterContext() { @@ -28,12 +21,12 @@ export function useTheaterContext() { return context; } -export function useTheater() { - const { theater } = useTheaterContext(); - return theater; +export function useCommonManagers() { + const theater = useTheaterContext(); + return theater.common; } export function useAnalysisApp() { - const { analyzer } = useTheaterContext(); - return analyzer; + const theater = useTheaterContext(); + return theater.analyzer; } diff --git a/libs/web-player/rewind/src/RewindTheater.ts b/libs/web-player/rewind/src/CommonManagers.ts similarity index 92% rename from libs/web-player/rewind/src/RewindTheater.ts rename to libs/web-player/rewind/src/CommonManagers.ts index 47aa7f98..2af6864f 100644 --- a/libs/web-player/rewind/src/RewindTheater.ts +++ b/libs/web-player/rewind/src/CommonManagers.ts @@ -25,7 +25,7 @@ import { SkinSettingsStore } from "./services/SkinSettingsStore"; * Example: Preferred skin can be set at only one place and is shared among all tools. */ @injectable() -export class RewindTheater { +export class CommonManagers { constructor( public readonly skinManager: SkinManager, public readonly skinSettingsStore: SkinSettingsStore, @@ -37,6 +37,7 @@ export class RewindTheater { private readonly rewindLocalStorage: RewindLocalStorage, ) {} + // This should only be called after there is a connection to the backend. async initialize() { this.rewindLocalStorage.initialize(); await this.skinManager.loadPreferredSkin(); @@ -70,10 +71,12 @@ export function createRewindTheater({ apiUrl }: Settings) { container.bind(RewindLocalStorage).toSelf(); // Theater facade - container.bind(RewindTheater).toSelf(); + container.bind(CommonManagers).toSelf(); return { - theater: container.get(RewindTheater), + common: container.get(CommonManagers), analyzer: createRewindAnalysisApp(container), }; } + +export type RewindTheater = ReturnType; diff --git a/libs/web-player/rewind/src/core/api/BackendStatusService.ts b/libs/web-player/rewind/src/core/api/BackendStatusService.ts new file mode 100644 index 00000000..99003b7b --- /dev/null +++ b/libs/web-player/rewind/src/core/api/BackendStatusService.ts @@ -0,0 +1,18 @@ +import { inject, injectable } from "inversify"; +import { TYPES } from "../../types/types"; +import { BehaviorSubject } from "rxjs"; + +export type BackendState = "NOT_STARTED" | "SETUP_MISSING" | "LOADING" | "READY"; + +@injectable() +export class BackendStatusService { + status$: BehaviorSubject; + + constructor(@inject(TYPES.API_URL) private readonly apiUrl: string) { + this.status$ = new BehaviorSubject("NOT_STARTED"); + } + + async wait() { + await fetch(`${this.apiUrl}/status`); + } +} diff --git a/libs/web-player/rewind/src/creators/createRewindAnalysisApp.ts b/libs/web-player/rewind/src/creators/createRewindAnalysisApp.ts index fc725a83..e27f7d23 100644 --- a/libs/web-player/rewind/src/creators/createRewindAnalysisApp.ts +++ b/libs/web-player/rewind/src/creators/createRewindAnalysisApp.ts @@ -41,9 +41,9 @@ import { ClipRecorder } from "../apps/analysis/manager/ClipRecorder"; * * The analysis tool can be used as a standalone app though. */ -export function createRewindAnalysisApp(rewindTheaterContainer: Container) { +export function createRewindAnalysisApp(commonContainer: Container) { const container = new Container({ defaultScope: "Singleton" }); - container.parent = rewindTheaterContainer; + container.parent = commonContainer; container.bind(STAGE_TYPES.EVENT_EMITTER).toConstantValue(new EventEmitter2()); // TODO container.bind(TYPES.WS_URL).toConstantValue("http://localhost:7271"); diff --git a/libs/web-player/rewind/src/index.ts b/libs/web-player/rewind/src/index.ts index 13262145..138b04d0 100644 --- a/libs/web-player/rewind/src/index.ts +++ b/libs/web-player/rewind/src/index.ts @@ -1,5 +1,5 @@ -export { createRewindTheater } from "./RewindTheater"; -export { AnalysisApp } from "./apps/analysis/AnalysisApp"; +export * from "./CommonManagers"; +export * from "./apps/analysis/AnalysisApp"; export * from "./core/game/GameplayClock"; export * from "./model/SkinId";