Refactor views to support dynamic updates.

This commit is contained in:
Dermot Duffy
2024-07-26 19:15:38 -07:00
parent 33dee47205
commit d5c4e56c45
80 changed files with 3714 additions and 4021 deletions
+164
View File
@@ -0,0 +1,164 @@
import { ViewContext } from 'view';
import { FrigateCardView } from '../../config/types';
import { log } from '../../utils/debug';
import { getStreamCameraID } from '../../utils/substream';
import { View } from '../../view/view';
import { getCameraIDsForViewName } from '../../view/view-to-cameras';
import { CardViewAPI } from '../types';
import { ViewFactory } from './factory';
import { ViewFactoryOptions, ViewManagerEpoch, ViewManagerInterface } from './types';
export class ViewManager implements ViewManagerInterface {
protected _view: View | null = null;
protected _factory: ViewFactory;
protected _api: CardViewAPI;
protected _epoch: ViewManagerEpoch = this._createEpoch();
constructor(api: CardViewAPI, factory?: ViewFactory) {
this._api = api;
this._factory = factory ?? new ViewFactory(api);
}
public getEpoch(): ViewManagerEpoch {
return this._epoch;
}
protected _createEpoch(oldView?: View | null): ViewManagerEpoch {
return {
manager: this,
...(oldView && { oldView }),
};
}
public getView(): View | null {
return this._view;
}
public hasView(): boolean {
return !!this.getView();
}
public reset(): void {
if (this._view) {
this._setView(null);
}
}
setViewDefault = (options?: ViewFactoryOptions): void =>
this._setViewGeneric(this._factory.getViewDefault, options);
setViewByParameters = (options?: ViewFactoryOptions): void =>
this._setViewGeneric(this._factory.getViewByParameters, options);
setViewDefaultWithNewQuery = async (options?: ViewFactoryOptions): Promise<void> =>
await this._setViewGenericAsync(this._factory.getViewDefaultWithNewQuery, options);
setViewByParametersWithNewQuery = async (
options?: ViewFactoryOptions,
): Promise<void> =>
await this._setViewGenericAsync(
this._factory.getViewByParametersWithNewQuery,
options,
);
setViewByParametersWithExistingQuery = async (
options?: ViewFactoryOptions,
): Promise<void> =>
await this._setViewGenericAsync(
this._factory.getViewByParametersWithExistingQuery,
options,
);
protected _setViewGeneric(
factoryFunc: (options?: ViewFactoryOptions) => View | null,
options?: ViewFactoryOptions,
): void {
let view: View | null = null;
try {
view = factoryFunc({
baseView: this._view,
...options,
});
} catch (e) {
return this._api.getMessageManager().setErrorIfHigherPriority(e);
}
view && this._setView(view);
}
protected async _setViewGenericAsync(
factoryFunc: (options?: ViewFactoryOptions) => Promise<View | null>,
options?: ViewFactoryOptions,
): Promise<void> {
let view: View | null = null;
try {
view = await factoryFunc({
baseView: this._view,
...options,
});
} catch (e) {
return this._api.getMessageManager().setErrorIfHigherPriority(e);
}
view && this._setView(view);
}
public setViewWithMergedContext(context: ViewContext | null): void {
if (this._view) {
return this._setView(this._view?.clone().mergeInContext(context));
}
}
public isViewSupportedByCamera(cameraID: string, view: FrigateCardView): boolean {
return !!getCameraIDsForViewName(this._api.getCameraManager(), view, cameraID).size;
}
/**
* Detect if the current view has a major "media change" for the given previous view.
* @param oldView The previous view.
* @returns True if the view change is a real media change.
*/
public hasMajorMediaChange(oldView?: View | null): boolean {
return (
!!oldView !== !!this._view ||
oldView?.view !== this._view?.view ||
oldView?.camera !== this._view?.camera ||
// When in live mode, take overrides (substreams) into account in deciding
// if this is a major media change.
(this._view?.view === 'live' &&
oldView &&
getStreamCameraID(oldView) !== getStreamCameraID(this._view)) ||
// When in the live view, the queryResults contain the events that
// happened in the past -- not reflective of the actual live media viewer
// the user is seeing.
(this._view?.view !== 'live' && oldView?.queryResults !== this._view?.queryResults)
);
}
protected _setView(view: View | null): void {
const oldView = this._view;
log(
this._api.getConfigManager().getCardWideConfig(),
`Frigate Card view change: `,
view,
);
this._view = view;
this._epoch = this._createEpoch(oldView);
if (this.hasMajorMediaChange(oldView)) {
this._api.getMediaLoadedInfoManager().clear();
}
if (oldView?.view !== view?.view) {
this._api.getCardElementManager().scrollReset();
}
this._api.getMessageManager().reset();
this._api.getStyleManager().setExpandedMode();
this._api.getConditionsManager()?.setState({
view: view?.view,
camera: view?.camera,
displayMode: view?.displayMode ?? undefined,
});
this._api.getCardElementManager().update();
}
}