1/* 2 * Copyright (c) 2021 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 16type ProvidedVarsMap = Map<string, ObservedPropertyAbstract<any>>; 17type Context = any; 18 19// Nativeview 20// implemented in C++ for release 21// and in utest/view_native_mock.ts for testing 22abstract class View extends NativeView implements 23 IMultiPropertiesChangeSubscriber, IMultiPropertiesReadSubscriber { 24 25 private id_: number; 26 private propsUsedForRender: Set<string> = new Set<string>(); 27 private isRenderingInProgress: boolean = false; 28 29 private watchedProps: Map<string, (propName: string) => void> 30 = new Map<string, (propName: string) => void>(); 31 32 // @Provide'd variables by this class and its ancestors 33 protected providedVars_: ProvidedVarsMap; 34 35 36 constructor(compilerAssignedUniqueChildId: string, parent: View) { 37 super(compilerAssignedUniqueChildId, parent); 38 this.id_ = SubscriberManager.Get().MakeId(); 39 this.providedVars_ = parent ? new Map(parent.providedVars_) 40 : new Map<string, ObservedPropertyAbstract<any>>(); 41 SubscriberManager.Get().add(this); 42 console.debug(`${this.constructor.name}: constructor done`); 43 } 44 45 // globally unique id, this is different from compilerAssignedUniqueChildId! 46 id__(): number { 47 return this.id_; 48 } 49 50 // inform the subscribed property 51 // that the View and thereby all properties 52 // are about to be deleted 53 abstract aboutToBeDeleted(): void; 54 55 abstract updateWithValueParams(params: Object): void; 56 57 propertyHasChanged(info?: PropertyInfo): void { 58 if (info) { 59 // need to sync container instanceId to switch instanceId in C++ side. 60 this.syncInstanceId(); 61 if (this.propsUsedForRender.has(info)) { 62 console.debug(`${this.constructor.name}: propertyHasChanged ['${info || "unknowm"}']. View needs update`); 63 this.markNeedUpdate(); 64 } else { 65 console.debug(`${this.constructor.name}: propertyHasChanged ['${info || "unknowm"}']. View does NOT need update`); 66 } 67 let cb = this.watchedProps.get(info) 68 if (cb) { 69 console.debug(`${this.constructor.name}: propertyHasChanged ['${info || "unknowm"}']. calling @Watch function`); 70 cb.call(this, info); 71 } 72 this.restoreInstanceId(); 73 } // if info avail. 74 } 75 76 propertyRead(info?: PropertyInfo): void { 77 console.debug(`${this.constructor.name}: propertyRead ['${info || "unknowm"}'].`); 78 if (info && (info != "unknown") && this.isRenderingInProgress) { 79 this.propsUsedForRender.add(info); 80 } 81 } 82 83 84 // for test purposes 85 public propertiesNeededToRender(): Set<string> { 86 return this.propsUsedForRender; 87 } 88 89 public aboutToRender(): void { 90 console.log(`${this.constructor.name}: aboutToRender`); 91 // reset 92 this.propsUsedForRender = new Set<string>(); 93 this.isRenderingInProgress = true; 94 } 95 96 public aboutToContinueRender(): void { 97 // do not reset 98 //this.propsUsedForRender = new Set<string>(); 99 this.isRenderingInProgress = true; 100 } 101 102 public onRenderDone(): void { 103 this.isRenderingInProgress = false; 104 console.log(`${this.constructor.name}: onRenderDone: render performed get access to these properties: ${JSON.stringify(Array.from(this.propsUsedForRender))}.`); 105 } 106 107 108 /** 109 * Function to be called from the constructor of the sub component 110 * to register a @Watch varibale 111 * @param propStr name of the variable. Note from @Provide and @Consume this is 112 * the variable name and not the alias! 113 * @param callback application defined member function of sub-class 114 */ 115 protected declareWatch(propStr: string, callback: (propName: string) => void): void { 116 this.watchedProps.set(propStr, callback); 117 } 118 119 /** 120 * This View @Provide's a variable under given name 121 * Call this function from the constructor of the sub class 122 * @param providedPropName either the variable name or the alias defined as 123 * decorator param 124 * @param store the backing store object for this variable (not the get/set variable!) 125 */ 126 protected addProvidedVar<T>(providedPropName: string, store: ObservedPropertyAbstract<T>) { 127 if (this.providedVars_.has(providedPropName)) { 128 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. 129 Property with this name is provided by one of the ancestor Views already.`); 130 } 131 this.providedVars_.set(providedPropName, store); 132 } 133 134 /** 135 * Method for the sub-class to call from its constructor for resolving 136 * a @Consume variable and initializing its backing store 137 * with the yncedPropertyTwoWay<T> object created from the 138 * @Provide variable's backing store. 139 * @param providedPropName the name of the @Provide'd variable. 140 * This is either the @Consume decortor parameter, or variable name. 141 * @param consumeVarName the @Consume variable name (not the 142 * @Consume decortor parameter) 143 * @returns initiaizing value of the @Consume backing store 144 */ 145 protected initializeConsume<T>(providedPropName: string, 146 consumeVarName: string): ObservedPropertyAbstract<T> { 147 let providedVarStore = this.providedVars_.get(providedPropName); 148 if (providedVarStore === undefined) { 149 throw new ReferenceError(`${this.constructor.name}: missing @Provide property with name ${providedPropName}. 150 Fail to resolve @Consume(${providedPropName}).`); 151 } 152 153 return providedVarStore.createLink(this, consumeVarName); 154 } 155} 156 157function getContentStorage(view: View) : ContentStorage { 158 return view.getContentStorage(); 159} 160 161function getContext(view: View) : Context { 162 return view.getContext(); 163} 164