• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2022 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
16/**
17 * View (for full update)
18 *
19 * all definitions in this file are framework internal
20 */
21
22/// <reference path="./fu_view_native.d.ts" />
23type ProvidedVarsMap = Map<string, ObservedPropertyAbstract<any>>;
24
25// Nativeview
26// implemented in C++  for release
27// and in utest/view_native_mock.ts for testing
28abstract class View extends NativeViewFullUpdate implements
29  IMultiPropertiesChangeSubscriber, IMultiPropertiesReadSubscriber {
30
31  private id_: number;
32  private propsUsedForRender: Set<string> = new Set<string>();
33  private isRenderingInProgress: boolean = false;
34
35  private watchedProps: Map<string, (propName: string) => void> = new Map<string, (propName: string) => void>();
36
37  // @Provide'd variables by this class and its ancestors
38  protected providedVars_: ProvidedVarsMap;
39
40  // my LocalStorge instance, shared with ancestor Views.
41  // create a default instance on demand if none is initialized
42  protected localStoragebackStore_: LocalStorage = undefined;
43  protected get localStorage_() {
44    if (!this.localStoragebackStore_) {
45      stateMgmtConsole.info(`${this.constructor.name} is accessing LocalStorage without being provided an instance. Creating a default instance.`);
46      this.localStoragebackStore_ = new LocalStorage({ /* emty */ });
47    }
48    return this.localStoragebackStore_;
49  }
50  protected set localStorage_(instance: LocalStorage) {
51    if (!instance) {
52      // setting to undefined not allowed
53      return;
54    }
55    if (this.localStoragebackStore_) {
56      stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`);
57    }
58    this.localStoragebackStore_ = instance;
59  }
60
61  /**
62   * Create a View
63   *
64   * 1. option: top level View, specify
65   *    - compilerAssignedUniqueChildId must specify
66   *    - parent=undefined
67   *    - localStorage  must provide if @LocalSTorageLink/Prop variables are used
68   *      in this View or descendant Views.
69   *
70   * 2. option: not a top level View
71   *    - compilerAssignedUniqueChildId must specify
72   *    - parent must specify
73   *    - localStorage do not specify, will inherit from parent View.
74   *
75   * @param compilerAssignedUniqueChildId Tw
76   * @param parent
77   * @param localStorage
78   */
79
80  constructor(compilerAssignedUniqueChildId: string, parent: View, localStorage?: LocalStorage) {
81    super(compilerAssignedUniqueChildId, parent);
82    this.id_ = SubscriberManager.MakeId();
83    this.providedVars_ = parent ? new Map(parent.providedVars_)
84      : new Map<string, ObservedPropertyAbstract<any>>();
85
86    this.localStoragebackStore_ = undefined;
87    if (parent) {
88      // this View is not a top-level View
89      stateMgmtConsole.debug(`${this.constructor.name} constructor: Using LocalStorage instance of the parent View.`);
90      this.setCardId(parent.getCardId());
91      this.localStorage_ = parent.localStorage_;
92    } else if (localStorage) {
93      this.localStorage_ = localStorage;
94      stateMgmtConsole.debug(`${this.constructor.name} constructor: Using LocalStorage instance provided via @Entry.`);
95    }
96
97    SubscriberManager.Add(this);
98    stateMgmtConsole.debug(`${this.constructor.name}: constructor done`);
99  }
100
101  // globally unique id, this is different from compilerAssignedUniqueChildId!
102  id__(): number {
103    return this.id_;
104  }
105
106  // temporary function, do not use, it will be removed soon!
107  // prupsoe is to allow eDSL transpiler to fix a bug that
108  // relies on this method
109  id() {
110    return this.id__();
111  }
112
113  // inform the subscribed property
114  // that the View and thereby all properties
115  // are about to be deleted
116  abstract aboutToBeDeleted(): void;
117
118  abstract updateWithValueParams(params: Object): void;
119
120  propertyHasChanged(info?: PropertyInfo): void {
121    if (info) {
122      // need to sync container instanceId to switch instanceId in C++ side.
123      this.syncInstanceId();
124      if (this.propsUsedForRender.has(info)) {
125        stateMgmtConsole.debug(`${this.constructor.name}: propertyHasChanged ['${info || 'unknowm'}']. View needs update`);
126        this.markNeedUpdate();
127      } else {
128        stateMgmtConsole.debug(`${this.constructor.name}: propertyHasChanged ['${info || 'unknowm'}']. View does NOT need update`);
129      }
130      let cb = this.watchedProps.get(info);
131      if (cb) {
132        stateMgmtConsole.debug(`${this.constructor.name}: propertyHasChanged ['${info || 'unknowm'}']. calling @Watch function`);
133        cb.call(this, info);
134      }
135      this.restoreInstanceId();
136    } // if info avail.
137  }
138
139  propertyRead(info?: PropertyInfo): void {
140    stateMgmtConsole.debug(`${this.constructor.name}: propertyRead ['${info || 'unknowm'}'].`);
141    if (info && (info !== 'unknown') && this.isRenderingInProgress) {
142      this.propsUsedForRender.add(info);
143    }
144  }
145
146
147  // for test purposes
148  public propertiesNeededToRender(): Set<string> {
149    return this.propsUsedForRender;
150  }
151
152  public aboutToRender(): void {
153    stateMgmtConsole.debug(`${this.constructor.name}: aboutToRender`);
154    // reset
155    this.propsUsedForRender = new Set<string>();
156    this.isRenderingInProgress = true;
157  }
158
159  public aboutToContinueRender(): void {
160    // do not reset
161    this.isRenderingInProgress = true;
162  }
163
164  public onRenderDone(): void {
165    this.isRenderingInProgress = false;
166    stateMgmtConsole.debug(`${this.constructor.name}: onRenderDone: render performed get access to these properties: ${JSON.stringify(Array.from(this.propsUsedForRender))}.`);
167  }
168
169
170  /**
171   * Function to be called from the constructor of the sub component
172   * to register a @Watch varibale
173   * @param propStr name of the variable. Note from @Provide and @Consume this is
174   *      the variable name and not the alias!
175   * @param callback application defined member function of sub-class
176   */
177  protected declareWatch(propStr: string, callback: (propName: string) => void): void {
178    this.watchedProps.set(propStr, callback);
179  }
180
181  /**
182   * This View @Provide's a variable under given name
183   * Call this function from the constructor of the sub class
184   * @param providedPropName either the variable name or the alias defined as
185   *        decorator param
186   * @param store the backing store object for this variable (not the get/set variable!)
187   */
188  protected addProvidedVar<T>(providedPropName: string, store: ObservedPropertyAbstract<T>) {
189    if (this.providedVars_.has(providedPropName)) {
190      throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}.
191      Property with this name is provided by one of the ancestor Views already.`);
192    }
193    this.providedVars_.set(providedPropName, store);
194  }
195
196  /**
197   * Method for the sub-class to call from its constructor for resolving
198   *       a @Consume variable and initializing its backing store
199   *       with the yncedPropertyTwoWay<T> object created from the
200   *       @Provide variable's backing store.
201   * @param providedPropName the name of the @Provide'd variable.
202   *     This is either the @Consume decortor parameter, or variable name.
203   * @param consumeVarName the @Consume variable name (not the
204   *            @Consume decortor parameter)
205   * @returns initiaizing value of the @Consume backing store
206   */
207  protected initializeConsume<T>(providedPropName: string,
208    consumeVarName: string): ObservedPropertyAbstract<T> {
209    let providedVarStore = this.providedVars_.get(providedPropName);
210    if (providedVarStore === undefined) {
211      throw new ReferenceError(`${this.constructor.name}: missing @Provide property with name ${providedPropName}.
212     Fail to resolve @Consume(${providedPropName}).`);
213    }
214
215    return providedVarStore.createLink(this, consumeVarName);
216  }
217}
218