• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { Log } from '../utils/Log';
17import { loggerMiddle } from './middlewares/loggerMiddle';
18import type { CameraInitState } from './reducers/CameraInitReducer';
19import { cameraInitReducer } from './reducers/CameraInitReducer';
20import type { ContextState } from './reducers/ContextReducer';
21import { contextReducer } from './reducers/ContextReducer';
22import type { CameraState } from './reducers/CameraReducer';
23import { cameraReducer } from './reducers/CameraReducer';
24import type { PreviewState } from './reducers/PreviewReducer';
25import { previewReducer } from './reducers/PreviewReducer';
26import type { CaptureState } from './reducers/CaptureReducer';
27import { captureReducer } from './reducers/CaptureReducer';
28import type { ModeChangeState } from './reducers/ModeChangeReducer';
29import { modeChangeReducer } from './reducers/ModeChangeReducer';
30import type { ModeState } from './reducers/ModeReducer';
31import { modeReducer } from './reducers/ModeReducer';
32import type { SettingState } from './reducers/SettingReducer';
33import { settingReducer } from './reducers/SettingReducer';
34import type { RecordState } from './reducers/RecordReducer';
35import { recordReducer } from './reducers/RecordReducer';
36import type { ZoomState } from './reducers/ZoomReducer';
37import { zoomReducer } from './reducers/ZoomReducer';
38import type { ActionData } from './actions/Action';
39import type { Enhancer, Reducer } from './core';
40import { applyMiddleware, combineReducers } from './core';
41import { eventBusMiddle } from './middlewares/EventBusMiddle';
42
43const TAG = '[store]:';
44const INIT_TAG = 'StoreInit';
45
46export type OhCombinedState = {
47  cameraInitReducer: CameraInitState;
48  contextReducer: ContextState;
49  cameraReducer: CameraState;
50  previewReducer: PreviewState;
51  captureReducer: CaptureState;
52  recordReducer: RecordState;
53  modeChangeReducer: ModeChangeState;
54  modeReducer: ModeState
55  settingReducer: SettingState
56  zoomReducer: ZoomState
57};
58
59function getReducers(): Reducer {
60  return combineReducers([
61    cameraInitReducer,
62    contextReducer,
63    cameraReducer,
64    previewReducer,
65    captureReducer,
66    recordReducer,
67    modeChangeReducer,
68    modeReducer,
69    settingReducer,
70    zoomReducer,
71  ]);
72}
73
74export interface Unsubscribe {
75  destroy(): void
76}
77
78export interface Dispatch<A = ActionData> {
79  <T extends A>(action: T): T;
80}
81
82function getEnhancer(): Enhancer {
83  return applyMiddleware(loggerMiddle, eventBusMiddle);
84}
85
86export function getStore(): Store {
87  Log.info('getStore');
88  return Store.getInstance();
89}
90
91export class Store {
92  private static mInstance: Store | undefined = undefined;
93  private currentReducer: Reducer;
94  private currentState = undefined;
95  private currentListeners: (() => void)[] | null = [];
96  private nextListeners = this.currentListeners;
97  private isDispatching = false;
98
99  private constructor(reducer: Reducer, enhancer: Enhancer) {
100    this.currentReducer = reducer;
101    this.dispatch({ type: INIT_TAG, data: null });
102    this.dispatch = enhancer(this.dispatch.bind(this));
103  }
104
105  public static hasStore(): boolean {
106    return Store.mInstance !== undefined;
107  }
108
109  public static getInstance(): Store {
110    if (!Store.mInstance) {
111      Store.mInstance = new Store(getReducers(), getEnhancer());
112    }
113    return Store.mInstance;
114  }
115
116  public static createStore(reducer: Reducer, enhancer: Enhancer): Store {
117    Store.mInstance = new Store(reducer, enhancer);
118    return Store.mInstance;
119  }
120
121  public getState(): OhCombinedState {
122    if (this.isDispatching) {
123      Log.error('isDispatching get error');
124      return null;
125    }
126    return this.currentState;
127  }
128
129  public dispatch(action: ActionData): ActionData {
130    if (this.isDispatching) {
131      Log.error('isDispatching get error');
132      return null;
133    }
134    try {
135      this.isDispatching = true;
136      this.currentState = this.currentReducer(this.currentState, action);
137    } finally {
138      this.isDispatching = false;
139    }
140    this.currentListeners = this.nextListeners;
141    const listeners = this.nextListeners;
142    for (let i = 0; i < listeners.length; i++) {
143      const listener = listeners[i];
144      listener();
145    }
146    return action;
147  }
148
149  public subscribe(mapToProps: MapStateProp | null, mapToDispatch: MapDispatchProp | null): Unsubscribe {
150    Log.info(`${TAG} getStore subscribe invoke.`);
151    if (mapToDispatch) {
152      mapToDispatch(this.dispatch as Dispatch);
153    }
154    let unsubscribe: () => void = () => {
155    };
156    if (mapToProps) {
157      mapToProps(this.currentState);
158      unsubscribe = this.stateSubscribe(() => mapToProps(this.currentState));
159      return {
160        destroy(): void {
161          unsubscribe();
162        }
163      };
164    }
165    return null;
166  }
167
168  private stateSubscribe(listener: () => void): () => void {
169    if (typeof listener !== 'function') {
170      Log.error('listener is not function');
171      return () => {
172      };
173    }
174    if (this.isDispatching) {
175      Log.error('isDispatching stateSubscribe error');
176      return () => {
177      };
178    }
179    let isSubScribed = true;
180
181    this.ensureCanMutateNextListeners();
182    this.nextListeners.push(listener);
183    return (): void => {
184      if (!isSubScribed) {
185        return;
186      }
187      this.currentListeners = null;
188      if (this.isDispatching) {
189        Log.error('isDispatching unstateSubscribe error');
190        return;
191      }
192      isSubScribed = false;
193      this.ensureCanMutateNextListeners();
194      const index = this.nextListeners.indexOf(listener);
195      this.nextListeners.slice(index, 1);
196      this.currentListeners = null;
197    };
198  }
199
200  private ensureCanMutateNextListeners(): void {
201    if (this.nextListeners === this.currentListeners) {
202      this.nextListeners = this.currentListeners.slice();
203    }
204  }
205}
206
207interface MapStateProp {
208  (state: OhCombinedState): void;
209}
210
211interface MapDispatchProp {
212  (dispatch: Dispatch): void;
213}