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/* 16 * Copyright (c) 2021 Huawei Device Co., Ltd. 17 * Licensed under the Apache License, Version 2.0 (the "License"); 18 * you may not use this file except in compliance with the License. 19 * You may obtain a copy of the License at 20 * 21 * http://www.apache.org/licenses/LICENSE-2.0 22 * 23 * Unless required by applicable law or agreed to in writing, software 24 * distributed under the License is distributed on an "AS IS" BASIS, 25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 * See the License for the specific language governing permissions and 27 * limitations under the License. 28 */ 29/* 30 * Copyright (c) 2023 Huawei Device Co., Ltd. 31 * Licensed under the Apache License, Version 2.0 (the "License"); 32 * you may not use this file except in compliance with the License. 33 * You may obtain a copy of the License at 34 * 35 * http://www.apache.org/licenses/LICENSE-2.0 36 * 37 * Unless required by applicable law or agreed to in writing, software 38 * distributed under the License is distributed on an "AS IS" BASIS, 39 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 40 * See the License for the specific language governing permissions and 41 * limitations under the License. 42 */ 43/* 44 * Copyright (c) 2021 Huawei Device Co., Ltd. 45 * Licensed under the Apache License, Version 2.0 (the "License"); 46 * you may not use this file except in compliance with the License. 47 * You may obtain a copy of the License at 48 * 49 * http://www.apache.org/licenses/LICENSE-2.0 50 * 51 * Unless required by applicable law or agreed to in writing, software 52 * distributed under the License is distributed on an "AS IS" BASIS, 53 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 54 * See the License for the specific language governing permissions and 55 * limitations under the License. 56 */ 57/* 58 * Copyright (c) 2021 Huawei Device Co., Ltd. 59 * Licensed under the Apache License, Version 2.0 (the "License"); 60 * you may not use this file except in compliance with the License. 61 * You may obtain a copy of the License at 62 * 63 * http://www.apache.org/licenses/LICENSE-2.0 64 * 65 * Unless required by applicable law or agreed to in writing, software 66 * distributed under the License is distributed on an "AS IS" BASIS, 67 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 68 * See the License for the specific language governing permissions and 69 * limitations under the License. 70 */ 71/** 72 * 73 * LocalStorage 74 * 75 * Class implements a Map of ObservableObjectBase UI state variables. 76 * Instances can be created to manage UI state within a limited "local" 77 * access, and life cycle as defined by the app. 78 * AppStorage singleton is sub-class of LocalStorage for 79 * UI state of app-wide access and same life cycle as the app. 80 * 81 * @since 9 82 */ 83class LocalStorage extends NativeLocalStorage { 84 /* 85 get access to provded LocalStorage instance thru Stake model 86 @StageModelOnly 87 @form 88 @since 10 89 */ 90 static getShared() { 91 return LocalStorage.GetShared(); 92 } 93 /** 94 * Construct new instance of LocalStorage 95 * initialzie with all properties and their values that Object.keys(params) returns 96 * Property values must not be undefined. 97 * @param initializingProperties Object containing keys and values. @see set() for valid values 98 * 99 * @since 9 100 */ 101 constructor(initializingProperties = {}) { 102 super(); 103 104 this.storage_ = new Map(); 105 if (Object.keys(initializingProperties).length) { 106 this.initializeProps(initializingProperties); 107 } 108 } 109 /** 110 * clear storage and init with given properties 111 * @param initializingProperties 112 * 113 * not a public / sdk function 114 */ 115 initializeProps(initializingProperties = {}) { 116 117 this.storage_.clear(); 118 Object.keys(initializingProperties).filter((propName) => initializingProperties[propName] != undefined).forEach((propName) => this.addNewPropertyInternal(propName, initializingProperties[propName])); 119 } 120 /** 121 * Use before deleting owning Ability, window, or service UI 122 * (letting it go out of scope). 123 * 124 * This method orderly closes down a LocalStorage instance by calling @see clear(). 125 * This requires that no property is left with one or more subscribers. 126 * @see clear() and @see delete() 127 * @returns true if all properties could be removed from storage 128 */ 129 aboutToBeDeleted() { 130 return this.clear(); 131 } 132 /** 133 * Check if LocalStorage has a property with given name 134 * return true if prooperty with given name exists 135 * same as ES6 Map.prototype.has() 136 * @param propName searched property 137 * @returns true if property with such name exists in LocalStorage 138 * 139 * @since 9 140 */ 141 has(propName) { 142 return this.storage_.has(propName); 143 } 144 /** 145 * Provide names of all properties in LocalStorage 146 * same as ES6 Map.prototype.keys() 147 * @returns return a Map Iterator 148 * 149 * @since 9 150 */ 151 keys() { 152 return this.storage_.keys(); 153 } 154 /** 155 * Returns number of properties in LocalStorage 156 * same as Map.prototype.size() 157 * @param propName 158 * @returns return number of properties 159 * 160 * @since 9 161 */ 162 size() { 163 return this.storage_.size; 164 } 165 /** 166 * Returns value of given property 167 * return undefined if no property with this name 168 * @param propName 169 * @returns property value if found or undefined 170 * 171 * @since 9 172 */ 173 get(propName) { 174 var p = this.storage_.get(propName); 175 return (p) ? p.get() : undefined; 176 } 177 /** 178 * Set value of given property in LocalStorage 179 * Methosd sets nothing and returns false if property with this name does not exist 180 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 181 * @param propName 182 * @param newValue must be of type T and must not be undefined or null 183 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 184 * 185 * @since 9 186 */ 187 set(propName, newValue) { 188 if (newValue == undefined) { 189 stateMgmtConsole.warn(`${this.constructor.name}: set('${propName}') with newValue == undefined not allowed.`); 190 return false; 191 } 192 var p = this.storage_.get(propName); 193 if (p == undefined) { 194 stateMgmtConsole.warn(`${this.constructor.name}: set: no property ${propName} error.`); 195 return false; 196 } 197 p.set(newValue); 198 return true; 199 } 200 /** 201 * Set value of given property, if it exists, @see set() . 202 * Add property if no property with given name and initialize with given value. 203 * Do nothing and return false if newValuue is undefined or null 204 * (undefined, null value is not allowed for state variables) 205 * @param propName 206 * @param newValue must be of type T and must not be undefined or null 207 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 208 * 209 * @since 9 210 */ 211 setOrCreate(propName, newValue) { 212 if (newValue == undefined) { 213 stateMgmtConsole.warn(`${this.constructor.name}: setOrCreate('${propName}') with newValue == undefined not allowed.`); 214 return false; 215 } 216 var p = this.storage_.get(propName); 217 if (p) { 218 219 p.set(newValue); 220 } 221 else { 222 223 this.addNewPropertyInternal(propName, newValue); 224 } 225 return true; 226 } 227 /** 228 * Internal use helper function to create and initialize a new property. 229 * caller needs to be all the checking beforehand 230 * @param propName 231 * @param value 232 * 233 * Not a public / sdk method. 234 */ 235 addNewPropertyInternal(propName, value) { 236 const newProp = (typeof value === "object") ? 237 new ObservedPropertyObject(value, undefined, propName) 238 : new ObservedPropertySimple(value, undefined, propName); 239 this.storage_.set(propName, newProp); 240 return newProp; 241 } 242 /** 243 * create and return a two-way sync "(link") to named property 244 * @param propName name of source property in LocalStorage 245 * @param linkUser IPropertySubscriber to be notified when source changes, 246 * @param subscribersName optional, the linkUser (subscriber) uses this name for the property 247 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 248 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 249 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 250 * return undefiend if named property does not already exist in LocalStorage 251 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 252 * return undefiend if named property does not already exist in LocalStorage 253 * 254 * @since 9 255 */ 256 link(propName, linkUser, subscribersName) { 257 var p = this.storage_.get(propName); 258 if (p == undefined) { 259 stateMgmtConsole.warn(`${this.constructor.name}: link: no property ${propName} error.`); 260 return undefined; 261 } 262 let linkResult; 263 if (ViewStackProcessor.UsesNewPipeline()) { 264 linkResult = (p instanceof ObservedPropertySimple) 265 ? new SynchedPropertySimpleTwoWayPU(p, linkUser, propName) 266 : new SynchedPropertyObjectTwoWayPU(p, linkUser, propName); 267 } 268 else { 269 linkResult = p.createLink(linkUser, propName); 270 } 271 linkResult.setInfo(subscribersName); 272 return linkResult; 273 } 274 /** 275 * Like @see link(), but will create and initialize a new source property in LocalStorge if missing 276 * @param propName name of source property in LocalStorage 277 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage 278 * default value must be of type S, must not be undefined or null. 279 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 280 * @param subscribersName the linkUser (subscriber) uses this name for the property 281 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 282 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 283 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 284 * 285 * @since 9 286 */ 287 setAndLink(propName, defaultValue, linkUser, subscribersName) { 288 var p = this.storage_.get(propName); 289 if (!p) { 290 this.setOrCreate(propName, defaultValue); 291 } 292 return this.link(propName, linkUser, subscribersName); 293 } 294 /** 295 * create and return a one-way sync ('prop') to named property 296 * @param propName name of source property in LocalStorage 297 * @param propUser IPropertySubscriber to be notified when source changes, 298 * @param subscribersName the linkUser (subscriber) uses this name for the property 299 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 300 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 301 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 302 * return undefiend if named property does not already exist in LocalStorage. 303 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 304 * return undefiend if named property does not already exist in LocalStorage. 305 * @since 9 306 */ 307 prop(propName, propUser, subscribersName) { 308 var p = this.storage_.get(propName); 309 if (p == undefined) { 310 stateMgmtConsole.warn(`${this.constructor.name}: prop: no property ${propName} error.`); 311 return undefined; 312 } 313 let propResult; 314 if (ViewStackProcessor.UsesNewPipeline()) { 315 propResult = (p instanceof ObservedPropertySimple) 316 ? new SynchedPropertySimpleOneWayPU(p, propUser, propName) 317 : new SynchedPropertyObjectOneWayPU(p, propUser, propName); 318 } 319 else { 320 propResult = p.createProp(propUser, propName); 321 } 322 propResult.setInfo(subscribersName); 323 return propResult; 324 } 325 /** 326 * Like @see prop(), will create and initialize a new source property in LocalStorage if missing 327 * @param propName name of source property in LocalStorage 328 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage. 329 * default value must be of type S, must not be undefined or null. 330 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 331 * @param subscribersName the propUser (subscriber) uses this name for the property 332 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 333 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 334 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 335 * @since 9 336 */ 337 setAndProp(propName, defaultValue, propUser, subscribersName) { 338 var p = this.storage_.get(propName); 339 if (!p) { 340 this.setOrCreate(propName, defaultValue); 341 } 342 return this.prop(propName, propUser, subscribersName); 343 } 344 /** 345 * Delete property from StorageBase 346 * Use with caution: 347 * Before deleting a prop from LocalStorage all its subscribers need to 348 * unsubscribe from the property. 349 * This method fails and returns false if given property still has subscribers 350 * Another reason for failing is unkmown property. 351 * 352 * Developer advise: 353 * Subscribers are created with @see link(), @see prop() 354 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 355 * That means as long as their is a @Component instance that uses such decorated variable 356 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 357 * (and also should not!) be deleted from LocalStorage. 358 * 359 * @param propName 360 * @returns false if method failed 361 * 362 * @since 9 363 */ 364 delete(propName) { 365 var p = this.storage_.get(propName); 366 if (p) { 367 if (p.numberOfSubscrbers()) { 368 stateMgmtConsole.error(`${this.constructor.name}: Attempt to delete property ${propName} that has \ 369 ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion.`); 370 return false; 371 } 372 p.aboutToBeDeleted(); 373 this.storage_.delete(propName); 374 return true; 375 } 376 else { 377 stateMgmtConsole.warn(`${this.constructor.name}: Attempt to delete unknown property ${propName}.`); 378 return false; 379 } 380 } 381 /** 382 * delete all properties from the LocalStorage instance 383 * @see delete(). 384 * precondition is that there are no subscribers. 385 * method returns false and deletes no poperties if there is any property 386 * that still has subscribers 387 * 388 * @since 9 389 */ 390 clear() { 391 for (let propName of this.keys()) { 392 var p = this.storage_.get(propName); 393 if (p.numberOfSubscrbers()) { 394 stateMgmtConsole.error(`${this.constructor.name}.deleteAll: Attempt to delete property ${propName} that \ 395 has ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion. 396 Any @Component instance with a @StorageLink/Prop or @LocalStorageLink/Prop is a subscriber.`); 397 return false; 398 } 399 } 400 for (let propName of this.keys()) { 401 var p = this.storage_.get(propName); 402 p.aboutToBeDeleted(); 403 } 404 this.storage_.clear(); 405 406 return true; 407 } 408 /** 409 * Subscribe to value change notifications of named property 410 * Any object implementing ISinglePropertyChangeSubscriber interface 411 * and registerign itself to SubscriberManager can register 412 * Caution: do remember to unregister, otherwise the property will block 413 * cleanup, @see delete() and @see clear() 414 * 415 * @param propName property in LocalStorage to subscribe to 416 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 417 * @returns false if named property does not exist 418 * 419 * @since 9 420 */ 421 subscribeToChangesOf(propName, subscriber) { 422 var p = this.storage_.get(propName); 423 if (p) { 424 p.addSubscriber(subscriber); 425 return true; 426 } 427 return false; 428 } 429 /** 430 * inverse of @see subscribeToChangesOf 431 * @param propName property in LocalStorage to subscribe to 432 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 433 * @returns false if named property does not exist 434 * 435 * @since 9 436 */ 437 unsubscribeFromChangesOf(propName, subscriberId) { 438 var p = this.storage_.get(propName); 439 if (p) { 440 p.removeSubscriber(null, subscriberId); 441 return true; 442 } 443 return false; 444 } 445 /** 446 * return number of subscribers to named property 447 * useful for debug purposes 448 * 449 * Not a public / sdk function 450 */ 451 numberOfSubscrbersTo(propName) { 452 var p = this.storage_.get(propName); 453 if (p) { 454 return p.numberOfSubscrbers(); 455 } 456 return undefined; 457 } 458 __createSync(storagePropName, defaultValue, factoryFunc) { 459 let p = this.storage_.get(storagePropName); 460 if (p == undefined) { 461 // property named 'storagePropName' not yet in storage 462 // add new property to storage 463 if (defaultValue === undefined) { 464 stateMgmtConsole.error(`${this.constructor.name}.__createSync(${storagePropName}, non-existing property and undefined default value. ERROR.`); 465 return undefined; 466 } 467 p = this.addNewPropertyInternal(storagePropName, defaultValue); 468 } 469 return factoryFunc(p); 470 } 471} 472/* 473 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 474 * Licensed under the Apache License, Version 2.0 (the "License"); 475 * you may not use this file except in compliance with the License. 476 * You may obtain a copy of the License at 477 * 478 * http://www.apache.org/licenses/LICENSE-2.0 479 * 480 * Unless required by applicable law or agreed to in writing, software 481 * distributed under the License is distributed on an "AS IS" BASIS, 482 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 483 * See the License for the specific language governing permissions and 484 * limitations under the License. 485 */ 486/** 487 * 488 * AppStorage 489 * 490 * Class implements a Map of ObservableObjectBase UI state variables. 491 * AppStorage singleton is sub-class of @see LocalStorage for 492 * UI state of app-wide access and same life cycle as the app. 493 * 494 * @since 7 495 */ 496class AppStorage extends LocalStorage { 497 /** 498 * create and initialize singleton 499 * initialzie with all properties and their values that Object.keys(params) returns 500 * Property values must not be undefined. 501 * 502 * not a public / sdk function 503 */ 504 static createSingleton(initializingPropersties) { 505 if (!AppStorage.instance_) { 506 507 AppStorage.instance_ = new AppStorage(initializingPropersties); 508 } 509 else { 510 stateMgmtConsole.error("AppStorage.createNewInstance(..): instance exists already, internal error!"); 511 } 512 } 513 /** 514 * create and return a two-way sync "(link") to named property 515 * 516 * Same as @see LocalStorage.link() 517 * 518 * @param propName name of source property in AppStorage 519 * @param linkUser IPropertySubscriber to be notified when source changes, 520 * @param subscribersName the linkUser (subscriber) uses this name for the property 521 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 522 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 523 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 524 * return undefiend if named property does not already exist in AppStorage 525 * 526 * @since 10 527 */ 528 static link(key, linkUser, subscribersName) { 529 return AppStorage.getOrCreate().link(key, linkUser, subscribersName); 530 } 531 /** 532 * @see link 533 * @since 7 534 * @deprecated 535 */ 536 static Link(key, linkUser, subscribersName) { 537 return AppStorage.getOrCreate().link(key, linkUser, subscribersName); 538 } 539 /** 540 * Like @see link(), but will create and initialize a new source property in LocalStorage if missing 541 * 542 * Same as @see LocalStorage.setAndLink() 543 * 544 * @param propName name of source property in AppStorage 545 * @param defaultValue value to be used for initializing if new creating new property in AppStorage 546 * default value must be of type S, must not be undefined or null. 547 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 548 * @param subscribersName the linkUser (subscriber) uses this name for the property 549 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 550 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 551 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 552 * 553 * @since 10 554 */ 555 static setAndLink(key, defaultValue, linkUser, subscribersName) { 556 return AppStorage.getOrCreate().setAndLink(key, defaultValue, linkUser, subscribersName); 557 } 558 /** 559 * @see setAndLink 560 * @since 7 561 * @deprecated 562 */ 563 static SetAndLink(key, defaultValue, linkUser, subscribersName) { 564 return AppStorage.getOrCreate().setAndLink(key, defaultValue, linkUser, subscribersName); 565 } 566 /** 567 * create and return a one-way sync ('prop') to named property 568 * 569 * Same as @see LocalStorage.prop() 570 * 571 * @param propName name of source property in AppStorage 572 * @param propUser IPropertySubscriber to be notified when source changes, 573 * @param subscribersName the linkUser (subscriber) uses this name for the property 574 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 575 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 576 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 577 * return undefiend if named property does not already exist in AppStorage. 578 * @since 10 579 */ 580 static prop(propName, propUser, subscribersName) { 581 return AppStorage.getOrCreate().prop(propName, propUser, subscribersName); 582 } 583 /** 584 * @see prop 585 * @since 7 586 * @deprecated 587 */ 588 static Prop(propName, propUser, subscribersName) { 589 return AppStorage.getOrCreate().prop(propName, propUser, subscribersName); 590 } 591 /** 592 * Like @see prop(), will create and initialize a new source property in AppStorage if missing 593 * 594 * Same as @see LocalStorage.setAndProp() 595 * 596 * @param propName name of source property in AppStorage 597 * @param defaultValue value to be used for initializing if new creating new property in AppStorage. 598 * default value must be of type S, must not be undefined or null. 599 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 600 * @param subscribersName the propUser (subscriber) uses this name for the property 601 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 602 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 603 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 604 * 605 * @since 10 606 */ 607 static setAndProp(key, defaultValue, propUser, subscribersName) { 608 return AppStorage.getOrCreate().setAndProp(key, defaultValue, propUser, subscribersName); 609 } 610 /** 611 * @see setAndProp 612 * @since 7 613 * @deprecated 614 */ 615 static SetAndProp(key, defaultValue, propUser, subscribersName) { 616 return AppStorage.getOrCreate().setAndProp(key, defaultValue, propUser, subscribersName); 617 } 618 /** 619 * Check if AppStorage has a property with given name 620 * return true if property with given name exists 621 * same as ES6 Map.prototype.has() 622 * 623 * Same as @see LocalStorage.has() 624 * 625 * @param propName searched property 626 * @returns true if property with such name exists in AppStorage 627 * 628 * @since 10 629 */ 630 static has(key) { 631 return AppStorage.getOrCreate().has(key); 632 } 633 /** 634 * @see has() 635 * @since 7 636 * @deprecated 637 */ 638 static Has(key) { 639 return AppStorage.getOrCreate().has(key); 640 } 641 /** 642 * Returns value of given property 643 * return undefined if no property with this name 644 * 645 * @Same as see LocalStorage.get() 646 * 647 * @param propName 648 * @returns property value if found or undefined 649 * 650 * @since 10 651 * 652 */ 653 static get(key) { 654 return AppStorage.getOrCreate().get(key); 655 } 656 /** 657 * @see get 658 * @since 7 659 * @deprecated 660 * 661 */ 662 static Get(key) { 663 return AppStorage.getOrCreate().get(key); 664 } 665 /** 666 * Set value of given property in AppStorage 667 * Method sets nothing and returns false if property with this name does not exist 668 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 669 * 670 * Same as @see LocalStorage.set 671 * 672 * @param propName 673 * @param newValue must be of type T and must not be undefined or null 674 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 675 * 676 * @since 10 677 */ 678 static set(key, newValue) { 679 return AppStorage.getOrCreate().set(key, newValue); 680 } 681 /** 682 * @see set 683 * @since 7 684 * @deprecated 685 */ 686 static Set(key, newValue) { 687 return AppStorage.getOrCreate().set(key, newValue); 688 } 689 /** 690 * Set value of given property, if it exists, @see set() . 691 * Add property if no property with given name and initialize with given value. 692 * Do nothing and return false if newValuue is undefined or null 693 * (undefined, null value is not allowed for state variables) 694 * 695 * @see LocalStorage.setOrCreate() 696 * 697 * @param propName 698 * @param newValue must be of type T and must not be undefined or null 699 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 700 * 701 * @since 10 702 */ 703 static setOrCreate(key, newValue) { 704 AppStorage.getOrCreate().setOrCreate(key, newValue); 705 } 706 /** 707 * @see setOrCreate 708 * @since 7 709 * @deprecated 710 */ 711 static SetOrCreate(key, newValue) { 712 AppStorage.getOrCreate().setOrCreate(key, newValue); 713 } 714 /** 715 * Delete property from StorageBase 716 * Use with caution: 717 * Before deleting a prop from AppStorage all its subscribers need to 718 * unsubscribe from the property. 719 * This method fails and returns false if given property still has subscribers 720 * Another reason for failing is unkmown property. 721 * 722 * Developer advise: 723 * Subscribers are created with @see link(), @see prop() 724 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 725 * That means as long as their is a @Component instance that uses such decorated variable 726 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 727 * (and also should not!) be deleted from AppStorage. 728 * 729 * Same as @see LocalStorage.delete() 730 * 731 * @param propName 732 * @returns false if method failed 733 * 734 * @since 10 735 */ 736 static delete(key) { 737 return AppStorage.getOrCreate().delete(key); 738 } 739 /** 740 * @see delete 741 * @since 7 742 * @deprecated 743 */ 744 static Delete(key) { 745 return AppStorage.getOrCreate().delete(key); 746 } 747 /** 748 * Provide names of all properties in AppStorage 749 * same as ES6 Map.prototype.keys() 750 * 751 * Same as @see LocalStorage.keys() 752 * 753 * @returns return a Map Iterator 754 * 755 * @since 10 756 */ 757 static keys() { 758 return AppStorage.getOrCreate().keys(); 759 } 760 /** 761 * @see keys 762 * @since 7 763 * @deprecated 764 */ 765 static Keys() { 766 return AppStorage.getOrCreate().keys(); 767 } 768 /** 769 * Returns number of properties in AppStorage 770 * same as Map.prototype.size() 771 * 772 * Same as @see LocalStorage.size() 773 * 774 * @param propName 775 * @returns return number of properties 776 * 777 * @since 10 778 */ 779 static size() { 780 return AppStorage.getOrCreate().size(); 781 } 782 /** 783 * @see size 784 * @since 7 785 * @deprecated 786 */ 787 static Size() { 788 return AppStorage.getOrCreate().size(); 789 } 790 /** 791 * delete all properties from the AppStorage 792 * 793 * @see delete(), same as @see LocalStorage.clear() 794 * 795 * precondition is that there are no subscribers. 796 * method returns false and deletes no poperties if there is any property 797 * that still has subscribers 798 * 799 * @since 10 800 */ 801 static clear() { 802 return AppStorage.getOrCreate().clear(); 803 } 804 /** 805 * @see clear 806 * @since 7 807 * @deprecated 808 */ 809 static Clear() { 810 return AppStorage.getOrCreate().clear(); 811 } 812 /** 813 * Same as @see clear(). 814 * 815 * @since 7, deprecated, used clear() instead! 816 * 817 */ 818 static StaticClear() { 819 return AppStorage.clear(); 820 } 821 /** 822 * not a public / sdk function 823 */ 824 static aboutToBeDeleted() { 825 AppStorage.getOrCreate().aboutToBeDeleted(); 826 } 827 /** 828 * return number of subscribers to named property 829 * useful for debug purposes 830 * 831 * not a public / sdk function 832 */ 833 static numberOfSubscribersTo(propName) { 834 return AppStorage.getOrCreate().numberOfSubscrbersTo(propName); 835 } 836 /** 837 * Subscribe to value change notifications of named property 838 * Any object implementing ISinglePropertyChangeSubscriber interface 839 * and registerign itself to SubscriberManager can register 840 * Caution: do remember to unregister, otherwise the property will block 841 * cleanup, @see delete() and @see clear() 842 * 843 * Same as @see LocalStorage.subscribeToChangesOf() 844 * 845 * @param propName property in AppStorage to subscribe to 846 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 847 * @returns false if named property does not exist 848 * 849 * @since 10 850 */ 851 static subscribeToChangesOf(propName, subscriber) { 852 return AppStorage.getOrCreate().subscribeToChangesOf(propName, subscriber); 853 } 854 /** 855 * @see subscribeToChangesOf 856 * @since 7 857 * @deprecated 858 */ 859 static SubscribeToChangesOf(propName, subscriber) { 860 return AppStorage.getOrCreate().subscribeToChangesOf(propName, subscriber); 861 } 862 /** 863 * inverse of @see SubscribeToChangesOf, 864 * same as @see LocalStorage.subscribeToChangesOf() 865 * 866 * @param propName property in AppStorage to subscribe to 867 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 868 * @returns false if named property does not exist 869 * 870 * @since 10 871 */ 872 static unsubscribeFromChangesOf(propName, subscriberId) { 873 return AppStorage.getOrCreate().unsubscribeFromChangesOf(propName, subscriberId); 874 } 875 /** 876 * @see unsubscribeFromChangesOf 877 * @since 7 878 * @deprecated 879 */ 880 static UnsubscribeFromChangesOf(propName, subscriberId) { 881 return AppStorage.getOrCreate().unsubscribeFromChangesOf(propName, subscriberId); 882 } 883 /** 884 * Unimplemented, currently all properties of AppStorage are mutable. 885 * 886 * @since 7, deprecated 887 */ 888 static IsMutable(key) { 889 return true; 890 } 891 /** 892 * not a public / sdk function 893 */ 894 static __createSync(storagePropName, defaultValue, factoryFunc) { 895 return AppStorage.getOrCreate().__createSync(storagePropName, defaultValue, factoryFunc); 896 } 897 /** 898 * not a public / sdk function 899 */ 900 static getOrCreate() { 901 if (!AppStorage.instance_) { 902 stateMgmtConsole.warn("AppStorage instance missing. Use AppStorage.createInstance(initObj). Creating instance without any initialization."); 903 AppStorage.instance_ = new AppStorage({}); 904 } 905 return AppStorage.instance_; 906 } 907 /** singleton class, app can not create instances 908 * 909 * not a public / sdk function 910 */ 911 constructor(initializingProperties) { 912 super(initializingProperties); 913 } 914} 915// instance functions below: 916// Should all be protected, but TS lang does not allow access from static member to protected member 917AppStorage.instance_ = undefined; 918/* 919 * Copyright (c) 2022 Huawei Device Co., Ltd. 920 * Licensed under the Apache License, Version 2.0 (the "License"); 921 * you may not use this file except in compliance with the License. 922 * You may obtain a copy of the License at 923 * 924 * http://www.apache.org/licenses/LICENSE-2.0 925 * 926 * Unless required by applicable law or agreed to in writing, software 927 * distributed under the License is distributed on an "AS IS" BASIS, 928 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 929 * See the License for the specific language governing permissions and 930 * limitations under the License. 931 */ 932/** 933 * Singleton class SubscriberManager implements IPropertySubscriberLookup 934 * public API to manage IPropertySubscriber 935 */ 936class SubscriberManager { 937 /** 938 * check subscriber is known 939 * same as ES6 Map.prototype.has() 940 * 941 * @since 9 942 */ 943 static Has(id) { 944 return SubscriberManager.GetInstance().has(id); 945 } 946 /** 947 * 948 * retrieve subscriber by id 949 * same as ES6 Map.prototype.get() 950 * 951 * @since 9 952 */ 953 static Find(id) { 954 return SubscriberManager.GetInstance().get(id); 955 } 956 /** 957 * unregister a subscriber 958 * same as ES6 Map.prototype.delete() 959 * @return boolean success or failure to delete 960 * 961 * @since 9 962 */ 963 static Delete(id) { 964 return SubscriberManager.GetInstance().delete(id); 965 } 966 /** 967 * add a new subscriber. 968 * The subscriber must have a new (unused) id (@see MakeId() ) 969 * for add() to succeed. 970 * same as Map.prototype.set() 971 * 972 * @since 9 973 */ 974 static Add(newSubsriber) { 975 return SubscriberManager.GetInstance().add(newSubsriber); 976 } 977 /** 978 * Update recycle custom node element id. 979 */ 980 static UpdateRecycleElmtId(oldId, newId) { 981 return SubscriberManager.GetInstance().updateRecycleElmtId(oldId, newId); 982 } 983 /** 984 * 985 * @returns a globally unique id to be assigned to a IPropertySubscriber objet 986 * Use MakeId() to assign a IPropertySubscriber object an id before calling @see add() . 987 * 988 * @since 9 989 */ 990 static MakeId() { 991 return SubscriberManager.GetInstance().makeId(); 992 } 993 /** 994 * Check number of registered Subscriber / registered IDs. 995 * @returns number of registered unique ids. 996 * 997 * @since 9 998 */ 999 static NumberOfSubscribers() { 1000 return SubscriberManager.GetInstance().numberOfSubscribers(); 1001 } 1002 /** 1003 * 1004 * internal (non-SDK) methods below 1005 * 1006 */ 1007 /** 1008 * Get singleton, create it on first call 1009 * @returns SubscriberManager singleton 1010 * 1011 * internal function 1012 * This function will be removed soon, use static functions instead! 1013 * Note: Fnction gets used by transpiler output for both full update and partial update 1014 */ 1015 static Get() { 1016 if (!SubscriberManager.instance_) { 1017 SubscriberManager.instance_ = new SubscriberManager(); 1018 } 1019 return SubscriberManager.instance_; 1020 } 1021 /** 1022 * Get singleton, create it on first call 1023 * @returns SubscriberManager singleton 1024 * 1025 * internal function 1026 */ 1027 static GetInstance() { 1028 if (!SubscriberManager.instance_) { 1029 SubscriberManager.instance_ = new SubscriberManager(); 1030 } 1031 return SubscriberManager.instance_; 1032 } 1033 /** 1034 * for debug purposes dump all known subscriber's info to comsole 1035 * 1036 * not a public / sdk function 1037 */ 1038 static DumpSubscriberInfo() { 1039 SubscriberManager.GetInstance().dumpSubscriberInfo(); 1040 } 1041 /** 1042 * not a public / sdk function 1043 * @see Has 1044 */ 1045 has(id) { 1046 return this.subscriberById_.has(id); 1047 } 1048 /** 1049 * not a public / sdk function 1050 * @see Get 1051 */ 1052 get(id) { 1053 return this.subscriberById_.get(id); 1054 } 1055 /** 1056 * not a public / sdk function 1057 * @see Delete 1058 */ 1059 delete(id) { 1060 if (!this.has(id)) { 1061 stateMgmtConsole.warn(`SubscriberManager.delete unknown id ${id} `); 1062 return false; 1063 } 1064 return this.subscriberById_.delete(id); 1065 } 1066 /** 1067 * not a public / sdk function 1068 * @see Add 1069 */ 1070 add(newSubsriber) { 1071 if (this.has(newSubsriber.id__())) { 1072 return false; 1073 } 1074 this.subscriberById_.set(newSubsriber.id__(), newSubsriber); 1075 return true; 1076 } 1077 updateRecycleElmtId(oldId, newId) { 1078 if (!this.has(oldId)) { 1079 return false; 1080 } 1081 const subscriber = this.get(oldId); 1082 this.subscriberById_.delete(oldId); 1083 this.subscriberById_.set(newId, subscriber); 1084 return true; 1085 } 1086 /** 1087 * Method for testing purposes 1088 * @returns number of subscribers 1089 * 1090 * not a public / sdk function 1091 */ 1092 numberOfSubscribers() { 1093 return this.subscriberById_.size; 1094 } 1095 /** 1096 * for debug purposes dump all known subscriber's info to comsole 1097 * 1098 * not a public / sdk function 1099 */ 1100 dumpSubscriberInfo() { 1101 1102 for (let [id, subscriber] of this.subscriberById_) { 1103 1104 } 1105 1106 } 1107 /** 1108 * 1109 * @returns a globally unique id to be assigned to a Subscriber 1110 */ 1111 makeId() { 1112 return ViewStackProcessor.MakeUniqueId(); 1113 } 1114 /** 1115 * SubscriberManager is a singleton created by the framework 1116 * do not use 1117 * 1118 * internal method 1119 */ 1120 constructor() { 1121 this.subscriberById_ = new Map(); 1122 1123 } 1124} 1125/* 1126 * Copyright (c) 2022 Huawei Device Co., Ltd. 1127 * Licensed under the Apache License, Version 2.0 (the "License"); 1128 * you may not use this file except in compliance with the License. 1129 * You may obtain a copy of the License at 1130 * 1131 * http://www.apache.org/licenses/LICENSE-2.0 1132 * 1133 * Unless required by applicable law or agreed to in writing, software 1134 * distributed under the License is distributed on an "AS IS" BASIS, 1135 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1136 * See the License for the specific language governing permissions and 1137 * limitations under the License. 1138 */ 1139/** 1140 * 1141 * SubscribedAbstractProperty is base class of ObservedPropertyAbstract 1142 * and includes these 3 functions that are part of the SDK. 1143 * 1144 * SubscribedAbstractProperty<T> is the return type of 1145 * - AppStorage static functions Link(), Prop(), SetAndLink(), and SetAndProp() 1146 * - LocalStorage methods link(), prop(), setAndLink(), and setAndProp() 1147 * 1148 * 'T' can be boolean, string, number or custom class. 1149 * 1150 * Main functions 1151 * @see get() reads the linked AppStorage/LocalStorage property value, 1152 * @see set(newValue) write a new value to the synched AppStorage/LocalStorage property value 1153 * @see aboutToBeDeleted() ends the sync relationship with the AppStorage/LocalStorage property 1154 * The app must call this function before the SubscribedAbstractProperty<T> object 1155 * goes out of scope. 1156 * 1157 * @since 7 1158*/ 1159class SubscribedAbstractProperty { 1160} 1161/* 1162 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 1163 * Licensed under the Apache License, Version 2.0 (the "License"); 1164 * you may not use this file except in compliance with the License. 1165 * You may obtain a copy of the License at 1166 * 1167 * http://www.apache.org/licenses/LICENSE-2.0 1168 * 1169 * Unless required by applicable law or agreed to in writing, software 1170 * distributed under the License is distributed on an "AS IS" BASIS, 1171 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1172 * See the License for the specific language governing permissions and 1173 * limitations under the License. 1174 */ 1175/** 1176 * 1177 * SubscribableAbstract 1178 * 1179 * This class is part of the SDK. 1180 * @since 10 1181 * 1182 * SubscribableAbstract is an abstract class that manages subscribers 1183 * to value changes. These subscribers are the implementation of 1184 * @State, @Link, @Provide, @Consume decorated variables inside the 1185 * framework. Each using @State, @Link, etc., decorated variable in 1186 * a @Component will make its own subscription. When the component 1187 * is created the subscription is added, and before the component 1188 * is deleted it unsubscribes 1189 * 1190 * An application may extend SubscribableAbstract for a custom class 1191 * that manages state data. @State, @Link, @Provide, @Consume 1192 * decorated variables can hold an Object that is instance of 1193 * SubscribableAbstract. 1194 * 1195 * About lifecycle: It is legal use for two @Components with two @State 1196 * decorated variables to share the same SubscribableAbstract object. 1197 * Each such decorated variable implementation makes its own 1198 * subscription to the SubscribableAbstract object. Hence, when both variables 1199 * have unsubscribed the SubscribableAbstract custom class may do its own 1200 * de-initialization, e.g. release held external resources. 1201 * 1202 * How to extend: 1203 * A subclass manages the get and set to one or several properties on its own. 1204 * The subclass needs to notify all relevant value changes to the framework for the 1205 * UI to be updated. Notification should only be given for class properties that 1206 * are used to generate the UI. 1207 * 1208 * A subclass must call super() in its constructor to let this base class 1209 * initialize itself. 1210 * 1211 * A subclass must call 'notifyPropertyHasChanged*(' after the relevant property 1212 * has changes. The framework will notify all dependent components to re-render. 1213 * 1214 * A sub-class may overwrite the 'addOwningProperty' function to add own 1215 * functionality, but it must call super.addowningOwningProperty(..). E.g. 1216 * the sub-class could connect to external resources upon the first subscriber. 1217 * 1218 * A sub-class may also overwrite the 'removeOwningProperty' function or 1219 * 'removeOwningPropertyById' function to add own functionality, 1220 * but it must call super.removeOwningProperty(..). 1221 * E.g. the sub-class could release held external resources upon loosing the 1222 * last subscriber. 1223 * 1224 */ 1225class SubscribableAbstract { 1226 /** 1227 * make sure to call super() from subclass constructor! 1228 * 1229 * @since 10 1230 */ 1231 constructor() { 1232 this.owningProperties_ = new Set(); 1233 1234 } 1235 /** 1236 * A subsclass must call this function whenever one of its properties has 1237 * changed that is used to construct the UI. 1238 * @param propName name of the change property 1239 * @param newValue the property value after the change 1240 * 1241 * @since 10 1242 */ 1243 notifyPropertyHasChanged(propName, newValue) { 1244 1245 this.owningProperties_.forEach((subscribedId) => { 1246 var owningProperty = SubscriberManager.Find(subscribedId); 1247 if (owningProperty) { 1248 if ('objectPropertyHasChangedPU' in owningProperty) { 1249 // PU code path 1250 owningProperty.objectPropertyHasChangedPU(this, propName); 1251 } 1252 // FU code path 1253 if ('hasChanged' in owningProperty) { 1254 owningProperty.hasChanged(newValue); 1255 } 1256 if ('propertyHasChanged' in owningProperty) { 1257 owningProperty.propertyHasChanged(propName); 1258 } 1259 } 1260 else { 1261 stateMgmtConsole.error(`SubscribableAbstract: notifyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 1262 } 1263 }); 1264 } 1265 /** 1266 * Provides the current number of subscribers. 1267 * Application may use this function to determine a shared object has no more remaining subscribers and can be deleted. 1268 * @returns number of current subscribers 1269 * 1270 * @since 10 1271 */ 1272 numberOfSubscribers() { 1273 return this.owningProperties_.size; 1274 } 1275 /** 1276 * Method used by the framework to add subscribing decorated variables 1277 * Subclass may overwrite this function but must call the function of the base 1278 * class from its own implementation. 1279 * @param subscriber new subscriber that implements ISinglePropertyChangeSubscriber 1280 * and/or IMultiPropertiesChangeSubscriber interfaces 1281 * 1282 * @since 10 1283 */ 1284 addOwningProperty(subscriber) { 1285 1286 this.owningProperties_.add(subscriber.id__()); 1287 } 1288 /** 1289 * Method used by the framework to unsubscribing decorated variables 1290 * Subclass may overwrite this function but must call the function of the base 1291 * class from its own implementation. 1292 * @param subscriber subscriber that implements ISinglePropertyChangeSubscriber 1293 * and/or IMultiPropertiesChangeSubscriber interfaces 1294 * 1295 * @since 10 1296 */ 1297 removeOwningProperty(property) { 1298 return this.removeOwningPropertyById(property.id__()); 1299 } 1300 /** 1301 * Same as @see removeOwningProperty() but by Subscriber id. 1302 * @param subscriberId 1303 * 1304 * framework internal function, not to be used by applications. 1305 */ 1306 removeOwningPropertyById(subscriberId) { 1307 1308 this.owningProperties_.delete(subscriberId); 1309 } 1310 /** 1311 * flush all subscribers / owning properties 1312 * This is needed when copying a SubscribableAbstract object to the localObject or @prop / SynchedPropertyObjectOneWay 1313 * - shallowCopy: copies the _reference to original_ Set. Hence, we must not modify this Set but assign a new Set 1314 * - deepCopy also (deep-) copies this class' owningProperties_ Set, incl. the numbers it includes. Assigning a new Set fixes. 1315 * 1316 */ 1317 clearOwningProperties() { 1318 this.owningProperties_ = new Set(); 1319 } 1320} 1321/** 1322 * SubscribaleAbstract class with typo in its nam,e 1323 * 1324 * @depreciated, use SubscribableAbstract 1325 */ 1326class SubscribaleAbstract extends SubscribableAbstract { 1327} 1328/* 1329 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1330 * Licensed under the Apache License, Version 2.0 (the "License"); 1331 * you may not use this file except in compliance with the License. 1332 * You may obtain a copy of the License at 1333 * 1334 * http://www.apache.org/licenses/LICENSE-2.0 1335 * 1336 * Unless required by applicable law or agreed to in writing, software 1337 * distributed under the License is distributed on an "AS IS" BASIS, 1338 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1339 * See the License for the specific language governing permissions and 1340 * limitations under the License. 1341 */ 1342/** 1343 * PersistentStorage 1344 * 1345 * Keeps current values of select AppStorage property properties persisted to file. 1346 * 1347 * since 9 1348 */ 1349class PersistentStorage { 1350 /** 1351 * 1352 * @param storage method to be used by the framework to set the backend 1353 * this is to be done during startup 1354 * 1355 * internal function, not part of the SDK 1356 * 1357 */ 1358 static configureBackend(storage) { 1359 PersistentStorage.storage_ = storage; 1360 } 1361 /** 1362 * private, use static functions! 1363 */ 1364 static getOrCreate() { 1365 if (PersistentStorage.instance_) { 1366 // already initialized 1367 return PersistentStorage.instance_; 1368 } 1369 PersistentStorage.instance_ = new PersistentStorage(); 1370 return PersistentStorage.instance_; 1371 } 1372 /** 1373 * 1374 * internal function, not part of the SDK 1375 */ 1376 static aboutToBeDeleted() { 1377 if (!PersistentStorage.instance_) { 1378 return; 1379 } 1380 PersistentStorage.getOrCreate().aboutToBeDeleted(); 1381 PersistentStorage.instance_ = undefined; 1382 } 1383 /** 1384 * Add property 'key' to AppStorage properties whose current value will be 1385 * persistent. 1386 * If AppStorage does not include this property it will be added and initializes 1387 * with given value 1388 * 1389 * @since 10 1390 * 1391 * @param key property name 1392 * @param defaultValue If AppStorage does not include this property it will be initialized with this value 1393 * 1394 */ 1395 static persistProp(key, defaultValue) { 1396 PersistentStorage.getOrCreate().persistProp(key, defaultValue); 1397 } 1398 /** 1399 * @see persistProp 1400 * @deprecated 1401 */ 1402 static PersistProp(key, defaultValue) { 1403 PersistentStorage.getOrCreate().persistProp(key, defaultValue); 1404 } 1405 /** 1406 * Reverse of @see persistProp 1407 * @param key no longer persist the property named key 1408 * 1409 * @since 10 1410 */ 1411 static deleteProp(key) { 1412 PersistentStorage.getOrCreate().deleteProp(key); 1413 } 1414 /** 1415 * @see deleteProp 1416 * @deprecated 1417 */ 1418 static DeleteProp(key) { 1419 PersistentStorage.getOrCreate().deleteProp(key); 1420 } 1421 /** 1422 * Persist given AppStorage properties with given names. 1423 * If a property does not exist in AppStorage, add it and initialize it with given value 1424 * works as @see persistProp for multiple properties. 1425 * 1426 * @param properties 1427 * 1428 * @since 10 1429 * 1430 */ 1431 static persistProps(properties) { 1432 PersistentStorage.getOrCreate().persistProps(properties); 1433 } 1434 /** 1435 * @see persistProps 1436 * @deprecated 1437 */ 1438 static PersistProps(properties) { 1439 PersistentStorage.getOrCreate().persistProps(properties); 1440 } 1441 /** 1442 * Inform persisted AppStorage property names 1443 * @returns array of AppStorage keys 1444 * 1445 * @since 10 1446 */ 1447 static keys() { 1448 let result = []; 1449 const it = PersistentStorage.getOrCreate().keys(); 1450 let val = it.next(); 1451 while (!val.done) { 1452 result.push(val.value); 1453 val = it.next(); 1454 } 1455 return result; 1456 } 1457 /** 1458 * @see keys 1459 * @deprecated 1460 */ 1461 static Keys() { 1462 return PersistentStorage.keys(); 1463 } 1464 /** 1465 * This methid offers a way to force writing the property value with given 1466 * key to persistent storage. 1467 * In the general case this is unnecessary as the framework observed changes 1468 * and triggers writing to disk by itself. For nested objects (e.g. array of 1469 * objects) however changes of a property of a property as not observed. This 1470 * is the case where the application needs to signal to the framework. 1471 * 1472 * @param key property that has changed 1473 * 1474 * @since 10 1475 * 1476 */ 1477 static notifyHasChanged(propName) { 1478 1479 PersistentStorage.storage_.set(propName, PersistentStorage.getOrCreate().links_.get(propName).get()); 1480 } 1481 /** 1482 * @see notifyHasChanged 1483 * @deprecated 1484 */ 1485 static NotifyHasChanged(propName) { 1486 1487 PersistentStorage.storage_.set(propName, PersistentStorage.getOrCreate().links_.get(propName).get()); 1488 } 1489 /** 1490 * all following methods are framework internal 1491 */ 1492 constructor() { 1493 this.links_ = new Map(); 1494 this.id_ = SubscriberManager.MakeId(); 1495 SubscriberManager.Add(this); 1496 } 1497 keys() { 1498 return this.links_.keys(); 1499 } 1500 persistProp(propName, defaultValue) { 1501 if (this.persistProp1(propName, defaultValue)) { 1502 // persist new prop 1503 1504 PersistentStorage.storage_.set(propName, this.links_.get(propName).get()); 1505 } 1506 } 1507 // helper function to persist a property 1508 // does everything except writing prop to disk 1509 persistProp1(propName, defaultValue) { 1510 1511 if (defaultValue == null || defaultValue == undefined) { 1512 stateMgmtConsole.error(`PersistentStorage: persistProp for ${propName} called with 'null' or 'undefined' default value!`); 1513 return false; 1514 } 1515 if (this.links_.get(propName)) { 1516 stateMgmtConsole.warn(`PersistentStorage: persistProp: ${propName} is already persisted`); 1517 return false; 1518 } 1519 let link = AppStorage.link(propName, this); 1520 if (link) { 1521 1522 this.links_.set(propName, link); 1523 } 1524 else { 1525 let newValue = PersistentStorage.storage_.get(propName); 1526 let returnValue; 1527 if (newValue == undefined || newValue == null) { 1528 1529 returnValue = defaultValue; 1530 } 1531 else { 1532 returnValue = newValue; 1533 } 1534 link = AppStorage.setAndLink(propName, returnValue, this); 1535 this.links_.set(propName, link); 1536 1537 } 1538 return true; 1539 } 1540 persistProps(properties) { 1541 properties.forEach(property => this.persistProp1(property.key, property.defaultValue)); 1542 this.write(); 1543 } 1544 deleteProp(propName) { 1545 let link = this.links_.get(propName); 1546 if (link) { 1547 link.aboutToBeDeleted(); 1548 this.links_.delete(propName); 1549 PersistentStorage.storage_.delete(propName); 1550 1551 } 1552 else { 1553 stateMgmtConsole.warn(`PersistentStorage: '${propName}' is not a persisted property warning.`); 1554 } 1555 } 1556 write() { 1557 this.links_.forEach((link, propName, map) => { 1558 1559 PersistentStorage.storage_.set(propName, link.get()); 1560 }); 1561 } 1562 propertyHasChanged(info) { 1563 1564 this.write(); 1565 } 1566 syncPeerHasChanged(eventSource) { 1567 1568 this.write(); 1569 } 1570 // public required by the interface, use the static method instead! 1571 aboutToBeDeleted() { 1572 1573 this.links_.forEach((val, key, map) => { 1574 1575 val.aboutToBeDeleted(); 1576 }); 1577 this.links_.clear(); 1578 SubscriberManager.Delete(this.id__()); 1579 PersistentStorage.storage_.clear(); 1580 } 1581 id__() { 1582 return this.id_; 1583 } 1584} 1585PersistentStorage.instance_ = undefined; 1586; 1587/* 1588 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1589 * Licensed under the Apache License, Version 2.0 (the "License"); 1590 * you may not use this file except in compliance with the License. 1591 * You may obtain a copy of the License at 1592 * 1593 * http://www.apache.org/licenses/LICENSE-2.0 1594 * 1595 * Unless required by applicable law or agreed to in writing, software 1596 * distributed under the License is distributed on an "AS IS" BASIS, 1597 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1598 * See the License for the specific language governing permissions and 1599 * limitations under the License. 1600 */ 1601/** 1602 * Environment 1603 * 1604 * Injects device properties ("environment") into AppStorage 1605 * 1606 */ 1607class Environment { 1608 static getOrCreate() { 1609 if (Environment.instance_) { 1610 // already initialized 1611 return Environment.instance_; 1612 } 1613 Environment.instance_ = new Environment(); 1614 return Environment.instance_; 1615 } 1616 static configureBackend(envBackend) { 1617 Environment.envBackend_ = envBackend; 1618 } 1619 /** 1620 * @see configureBackend 1621 * @deprecated 1622 */ 1623 static ConfigureBackend(envBackend) { 1624 Environment.envBackend_ = envBackend; 1625 } 1626 static aboutToBeDeleted() { 1627 if (!Environment.instance_) { 1628 return; 1629 } 1630 Environment.getOrCreate().aboutToBeDeleted(); 1631 Environment.instance_ = undefined; 1632 } 1633 /** 1634 * @see aboutToBeDeleted 1635 * @deprecated 1636 */ 1637 static AboutToBeDeleted() { 1638 Environment.aboutToBeDeleted(); 1639 } 1640 static envProp(key, value) { 1641 return Environment.getOrCreate().envProp(key, value); 1642 } 1643 /** 1644 * @see envProp 1645 * @deprecated 1646 */ 1647 static EnvProp(key, value) { 1648 return Environment.getOrCreate().envProp(key, value); 1649 } 1650 static envProps(props) { 1651 Environment.getOrCreate().envProps(props); 1652 } 1653 /** 1654 * @see envProps 1655 * @deprecated 1656 */ 1657 static EnvProps(props) { 1658 Environment.getOrCreate().envProps(props); 1659 } 1660 static keys() { 1661 return Environment.getOrCreate().keys(); 1662 } 1663 /** 1664 * @see keys 1665 * @deprecated 1666 */ 1667 static Keys() { 1668 return Environment.getOrCreate().keys(); 1669 } 1670 constructor() { 1671 this.props_ = new Map(); 1672 Environment.envBackend_.onValueChanged(this.onValueChanged.bind(this)); 1673 } 1674 envProp(key, value) { 1675 let prop = AppStorage.prop(key); 1676 if (prop) { 1677 stateMgmtConsole.warn(`Environment: envProp '${key}': Property already exists in AppStorage. Not using environment property.`); 1678 return false; 1679 } 1680 let tmp; 1681 switch (key) { 1682 case "accessibilityEnabled": 1683 tmp = Environment.envBackend_.getAccessibilityEnabled(); 1684 break; 1685 case "colorMode": 1686 tmp = Environment.envBackend_.getColorMode(); 1687 break; 1688 case "fontScale": 1689 tmp = Environment.envBackend_.getFontScale(); 1690 break; 1691 case "fontWeightScale": 1692 tmp = Environment.envBackend_.getFontWeightScale().toFixed(2); 1693 break; 1694 case "layoutDirection": 1695 tmp = Environment.envBackend_.getLayoutDirection(); 1696 break; 1697 case "languageCode": 1698 tmp = Environment.envBackend_.getLanguageCode(); 1699 break; 1700 default: 1701 tmp = value; 1702 } 1703 if (!tmp && tmp !== 0) { 1704 tmp = value; 1705 } 1706 prop = AppStorage.setAndProp(key, tmp); 1707 if (!prop) { 1708 stateMgmtConsole.warn(`Environment: envProp '${key}': AppStorage setAndProp failed.`); 1709 return false; 1710 } 1711 this.props_.set(key, prop); 1712 1713 return true; 1714 } 1715 envProps(properties) { 1716 properties.forEach(property => { 1717 this.envProp(property.key, property.defaultValue); 1718 1719 }); 1720 } 1721 keys() { 1722 let result = []; 1723 const it = this.props_.keys(); 1724 let val = it.next(); 1725 while (!val.done) { 1726 result.push(val.value); 1727 val = it.next(); 1728 } 1729 return result; 1730 } 1731 onValueChanged(key, value) { 1732 let ok = AppStorage.set(key, value); 1733 if (ok) { 1734 1735 } 1736 else { 1737 stateMgmtConsole.warn(`Environment: onValueChanged: error changing ${key}! See results above.`); 1738 } 1739 } 1740 aboutToBeDeleted() { 1741 this.props_.forEach((val, key, map) => { 1742 val.aboutToBeDeleted(); 1743 AppStorage.delete(key); 1744 }); 1745 } 1746} 1747Environment.instance_ = undefined; 1748/* 1749 * Copyright (c) 2022 Huawei Device Co., Ltd. 1750 * Licensed under the Apache License, Version 2.0 (the "License"); 1751 * you may not use this file except in compliance with the License. 1752 * You may obtain a copy of the License at 1753 * 1754 * http://www.apache.org/licenses/LICENSE-2.0 1755 * 1756 * Unless required by applicable law or agreed to in writing, software 1757 * distributed under the License is distributed on an "AS IS" BASIS, 1758 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1759 * See the License for the specific language governing permissions and 1760 * limitations under the License. 1761 */ 1762/** 1763 * state mgmt library uses its own class for logging 1764* allows to remap separately from other use of aceConsole 1765* 1766* everything in this file is framework internal 1767*/ 1768class stateMgmtConsole { 1769 static log(...args) { 1770 aceConsole.log(...args); 1771 } 1772 static debug(...args) { 1773 aceConsole.debug(...args); 1774 } 1775 static info(...args) { 1776 aceConsole.info(...args); 1777 } 1778 static warn(...args) { 1779 aceConsole.warn(...args); 1780 } 1781 static error(...args) { 1782 aceConsole.error(...args); 1783 } 1784} 1785class stateMgmtTrace { 1786 static scopedTrace(codeBlock, arg1, ...args) { 1787 aceTrace.begin(arg1, ...args); 1788 let result = codeBlock(); 1789 aceTrace.end(); 1790 return result; 1791 } 1792} 1793class errorReport { 1794 static varValueCheckFailed(params) { 1795 let msg = `@Component '${params.customComponent}': Illegal variable value error with decorated variable ${params.variableDeco} '${params.variableName}': `; 1796 msg += `failed validation: '${params.expectedType}`; 1797 try { 1798 msg += `, attempt to assign value type: '${typeof params.value}'`; 1799 msg += `, value: '${JSON.stringify(params.value, null, 4)}'`; 1800 } 1801 catch (e) { } 1802 msg += "!"; 1803 throw new TypeError(msg); 1804 } 1805 static varObservationFailed(params) { 1806 let msg = `@Component '${params.customComponent}': decorated variable ${params.variableDeco} '${params.variableName}': `; 1807 msg += `its class is neither decorated with '@Observed' nor it is an instance of 'SubscribableAbstract'`; 1808 try { 1809 msg += `, attempt to assign value type: '${typeof params.value}'`; 1810 msg += `, value: '${JSON.stringify(params.value, null, 4)}'`; 1811 } 1812 catch (e) { } 1813 msg += "!"; 1814 throw new TypeError(msg); 1815 } 1816} 1817/* 1818 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1819 * Licensed under the Apache License, Version 2.0 (the "License"); 1820 * you may not use this file except in compliance with the License. 1821 * You may obtain a copy of the License at 1822 * 1823 * http://www.apache.org/licenses/LICENSE-2.0 1824 * 1825 * Unless required by applicable law or agreed to in writing, software 1826 * distributed under the License is distributed on an "AS IS" BASIS, 1827 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1828 * See the License for the specific language governing permissions and 1829 * limitations under the License. 1830 */ 1831var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 1832 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 1833 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 1834 else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 1835 return c > 3 && r && Object.defineProperty(target, key, r), r; 1836}; 1837/** 1838* @Observed class decorator 1839* 1840* usage: 1841* @Observed class ClassA { ... } 1842* 1843* Causes every instance of decorated clss to be automatically wrapped inside an ObservedObject. 1844* 1845* Implemented by extending the decroaetd class by class named 'ObservableObjectClass'. 1846* 1847* It is permisstable to decorate the base and the extended class like thisNote: I 1848* @Observed class ClassA { ...} 1849* @Observed class ClassB extends ClassA { ... } 1850* and use 1851* a = new ClassA(); 1852* b = new ClassB(); 1853* Only one ES6 Proxy is added. 1854* 1855* 1856* Take note the decorator implementation extends the prototype chain. 1857* 1858* The prototype chain of a in above example is 1859* - ObservableObjectClass prototype 1860* - ClassA prototype 1861* - Object prototype 1862* 1863* Snd the prototype chain of b is 1864* - ObservableObjectClass prototype 1865* - ClassB prototype 1866* - ObservableObjectClass prototype 1867* - ClassA prototype 1868* - Object prototype 1869* 1870* The @Observed decorator is public, part of the SDK, starting from API 9. 1871* 1872*/ 1873// define just once to get just one Symbol 1874const __IS_OBSERVED_PROXIED = Symbol("_____is_observed_proxied__"); 1875function Observed(constructor_, _) { 1876 1877 let ObservedClass = class extends constructor_ { 1878 constructor(...args) { 1879 super(...args); 1880 1881 let isProxied = Reflect.has(this, __IS_OBSERVED_PROXIED); 1882 Object.defineProperty(this, __IS_OBSERVED_PROXIED, { 1883 value: true, 1884 enumerable: false, 1885 configurable: false, 1886 writable: false 1887 }); 1888 if (isProxied) { 1889 1890 return this; 1891 } 1892 else { 1893 1894 return ObservedObject.createNewInternal(this, undefined); 1895 } 1896 } 1897 }; 1898 return ObservedClass; 1899} 1900// force tsc to generate the __decorate data structure needed for @Observed 1901// tsc will not generate unless the @Observed class decorator is used at least once 1902let __IGNORE_FORCE_decode_GENERATION__ = class __IGNORE_FORCE_decode_GENERATION__ { 1903}; 1904__IGNORE_FORCE_decode_GENERATION__ = __decorate([ 1905 Observed 1906], __IGNORE_FORCE_decode_GENERATION__); 1907/** 1908 * class ObservedObject and supporting Handler classes, 1909 * Extends from ES6 Proxy. In adding to 'get' and 'set' 1910 * the clasess manage subscribers that receive notification 1911 * about proxies object being 'read' or 'changed'. 1912 * 1913 * These classes are framework internal / non-SDK 1914 * 1915 */ 1916class SubscribableHandler { 1917 constructor(owningProperty) { 1918 this.owningProperties_ = new Set(); 1919 if (owningProperty) { 1920 this.addOwningProperty(owningProperty); 1921 } 1922 1923 } 1924 addOwningProperty(subscriber) { 1925 if (subscriber) { 1926 1927 this.owningProperties_.add(subscriber.id__()); 1928 } 1929 else { 1930 stateMgmtConsole.warn(`SubscribableHandler: addOwningProperty: undefined subscriber. - Internal error?`); 1931 } 1932 } 1933 /* 1934 the inverse function of createOneWaySync or createTwoWaySync 1935 */ 1936 removeOwningProperty(property) { 1937 return this.removeOwningPropertyById(property.id__()); 1938 } 1939 removeOwningPropertyById(subscriberId) { 1940 1941 this.owningProperties_.delete(subscriberId); 1942 } 1943 notifyObjectPropertyHasChanged(propName, newValue) { 1944 1945 this.owningProperties_.forEach((subscribedId) => { 1946 var owningProperty = SubscriberManager.Find(subscribedId); 1947 if (owningProperty) { 1948 if ('objectPropertyHasChangedPU' in owningProperty) { 1949 // PU code path 1950 owningProperty.objectPropertyHasChangedPU(this, propName); 1951 } 1952 // FU code path 1953 if ('hasChanged' in owningProperty) { 1954 owningProperty.hasChanged(newValue); 1955 } 1956 if ('propertyHasChanged' in owningProperty) { 1957 owningProperty.propertyHasChanged(propName); 1958 } 1959 } 1960 else { 1961 stateMgmtConsole.warn(`SubscribableHandler: notifyObjectPropertyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 1962 } 1963 }); 1964 } 1965 // notify a property has been 'read' 1966 // this functionality is in preparation for observed computed variables 1967 // enable calling from 'get' trap handler functions to this function once 1968 // adding support for observed computed variables 1969 notifyObjectPropertyHasBeenRead(propName) { 1970 1971 this.owningProperties_.forEach((subscribedId) => { 1972 var owningProperty = SubscriberManager.Find(subscribedId); 1973 if (owningProperty) { 1974 // PU code path 1975 if ('objectPropertyHasBeenReadPU' in owningProperty) { 1976 owningProperty.objectPropertyHasBeenReadPU(this, propName); 1977 } 1978 } 1979 }); 1980 } 1981 has(target, property) { 1982 1983 return (property === ObservedObject.__IS_OBSERVED_OBJECT) ? true : Reflect.has(target, property); 1984 } 1985 get(target, property, receiver) { 1986 1987 return (property === ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT) ? target : Reflect.get(target, property, receiver); 1988 } 1989 set(target, property, newValue) { 1990 switch (property) { 1991 case SubscribableHandler.SUBSCRIBE: 1992 // assignment obsObj[SubscribableHandler.SUBSCRCRIBE] = subscriber 1993 this.addOwningProperty(newValue); 1994 return true; 1995 break; 1996 case SubscribableHandler.UNSUBSCRIBE: 1997 // assignment obsObj[SubscribableHandler.UNSUBSCRCRIBE] = subscriber 1998 this.removeOwningProperty(newValue); 1999 return true; 2000 break; 2001 default: 2002 if (Reflect.get(target, property) == newValue) { 2003 return true; 2004 } 2005 2006 Reflect.set(target, property, newValue); 2007 this.notifyObjectPropertyHasChanged(property.toString(), newValue); 2008 return true; 2009 break; 2010 } 2011 // unreachable 2012 return false; 2013 } 2014} 2015SubscribableHandler.SUBSCRIBE = Symbol("_____subscribe__"); 2016SubscribableHandler.UNSUBSCRIBE = Symbol("_____unsubscribe__"); 2017class SubscribableDateHandler extends SubscribableHandler { 2018 constructor(owningProperty) { 2019 super(owningProperty); 2020 } 2021 /** 2022 * Get trap for Date type proxy 2023 * Functions that modify Date in-place are intercepted and replaced with a function 2024 * that executes the original function and notifies the handler of a change. 2025 * @param target Original Date object 2026 * @param property 2027 * @returns 2028 */ 2029 get(target, property) { 2030 const dateSetFunctions = new Set(["setFullYear", "setMonth", "setDate", "setHours", "setMinutes", "setSeconds", 2031 "setMilliseconds", "setTime", "setUTCFullYear", "setUTCMonth", "setUTCDate", "setUTCHours", "setUTCMinutes", 2032 "setUTCSeconds", "setUTCMilliseconds"]); 2033 let ret = super.get(target, property); 2034 if (typeof ret === "function" && property.toString() && dateSetFunctions.has(property.toString())) { 2035 const self = this; 2036 return function () { 2037 // execute original function with given arguments 2038 let result = ret.apply(this, arguments); 2039 self.notifyObjectPropertyHasChanged(property.toString(), this); 2040 return result; 2041 }.bind(target); // bind "this" to target inside the function 2042 } 2043 else if (typeof ret === "function") { 2044 ret = ret.bind(target); 2045 } 2046 return ret; 2047 } 2048} 2049class ExtendableProxy { 2050 constructor(obj, handler) { 2051 return new Proxy(obj, handler); 2052 } 2053} 2054class ObservedObject extends ExtendableProxy { 2055 /** 2056 * Factory function for ObservedObjects / 2057 * wrapping of objects for proxying 2058 * 2059 * @param rawObject unproxied Object or ObservedObject 2060 * @param objOwner owner of this Object to sign uop for propertyChange 2061 * notifications 2062 * @returns the rawObject if object is already an ObservedObject, 2063 * otherwise the newly created ObservedObject 2064 */ 2065 static createNew(rawObject, owningProperty) { 2066 if (rawObject === null || rawObject === undefined) { 2067 stateMgmtConsole.error(`ObservedObject.CreateNew, input object must not be null or undefined.`); 2068 return rawObject; 2069 } 2070 if (ObservedObject.IsObservedObject(rawObject)) { 2071 ObservedObject.addOwningProperty(rawObject, owningProperty); 2072 return rawObject; 2073 } 2074 return ObservedObject.createNewInternal(rawObject, owningProperty); 2075 } 2076 static createNewInternal(rawObject, owningProperty) { 2077 let proxiedObject = new ObservedObject(rawObject, Array.isArray(rawObject) ? new class extends SubscribableHandler { 2078 constructor(owningProperty) { 2079 super(owningProperty); 2080 // In-place array modification functions 2081 // splice is also in-place modifying function, but we need to handle separately 2082 this.inPlaceModifications = new Set(["copyWithin", "fill", "reverse", "sort"]); 2083 } 2084 get(target, property, receiver) { 2085 let ret = super.get(target, property, receiver); 2086 if (ret && typeof ret === "function") { 2087 const self = this; 2088 const prop = property.toString(); 2089 // prop is the function name here 2090 if (prop == "splice") { 2091 // 'splice' self modifies the array, returns deleted array items 2092 // means, alike other self-modifying functions, splice does not return the array itself. 2093 return function () { 2094 const result = ret.apply(target, arguments); 2095 // prop is the function name here 2096 // and result is the function return value 2097 // functinon modifies none or more properties 2098 self.notifyObjectPropertyHasChanged(prop, target); 2099 return result; 2100 }.bind(proxiedObject); 2101 } 2102 if (self.inPlaceModifications.has(prop)) { 2103 // in place modfication function result == target, the raw array modified 2104 2105 return function () { 2106 const result = ret.apply(target, arguments); 2107 // 'result' is the unproxied object 2108 // functinon modifies none or more properties 2109 self.notifyObjectPropertyHasChanged(prop, result); 2110 // returning the 'proxiedObject' ensures that when chain calls also 2nd function call 2111 // operates on the proxied object. 2112 return proxiedObject; 2113 }.bind(proxiedObject); 2114 } 2115 // binding the proxiedObject ensures that modifying functions like push() operate on the 2116 // proxied array and each array change is notified. 2117 return ret.bind(proxiedObject); 2118 } 2119 return ret; 2120 } 2121 }(owningProperty) // SubscribableArrayHandlerAnonymous 2122 : (rawObject instanceof Date) 2123 ? new SubscribableDateHandler(owningProperty) 2124 : new SubscribableHandler(owningProperty), owningProperty); 2125 return proxiedObject; 2126 } 2127 /* 2128 Return the unproxied object 'inside' the ObservedObject / the ES6 Proxy 2129 no set observation, no notification of changes! 2130 Use with caution, do not store any references 2131 */ 2132 static GetRawObject(obj) { 2133 return !ObservedObject.IsObservedObject(obj) ? obj : obj[ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT]; 2134 } 2135 /** 2136 * 2137 * @param obj anything 2138 * @returns true if the parameter is an Object wrpped with a ObservedObject 2139 * Note: Since ES6 Proying is transparent, 'instance of' will not work. Use 2140 * this static function instead. 2141 */ 2142 static IsObservedObject(obj) { 2143 return (obj && (typeof obj === "object") && Reflect.has(obj, ObservedObject.__IS_OBSERVED_OBJECT)); 2144 } 2145 /** 2146 * add a subscriber to given ObservedObject 2147 * due to the proxy nature this static method approach needs to be used instead of a member 2148 * function 2149 * @param obj 2150 * @param subscriber 2151 * @returns false if given object is not an ObservedObject 2152 */ 2153 static addOwningProperty(obj, subscriber) { 2154 if (!ObservedObject.IsObservedObject(obj) || subscriber == undefined) { 2155 return false; 2156 } 2157 obj[SubscribableHandler.SUBSCRIBE] = subscriber; 2158 return true; 2159 } 2160 /** 2161 * remove a subscriber to given ObservedObject 2162 * due to the proxy nature this static method approach needs to be used instead of a member 2163 * function 2164 * @param obj 2165 * @param subscriber 2166 * @returns false if given object is not an ObservedObject 2167 */ 2168 static removeOwningProperty(obj, subscriber) { 2169 if (!ObservedObject.IsObservedObject(obj)) { 2170 return false; 2171 } 2172 obj[SubscribableHandler.UNSUBSCRIBE] = subscriber; 2173 return true; 2174 } 2175 /** 2176 * Utility function for debugging the prototype chain of given Object 2177 * The given object can be any Object, it is not required to be an ObservedObject 2178 * @param object 2179 * @returns multi-line string containing info about the prototype chain 2180 * on class in class hiararchy per line 2181 */ 2182 static tracePrototypeChainOfObject(object) { 2183 let proto = Object.getPrototypeOf(object); 2184 let result = ""; 2185 let sepa = ""; 2186 while (proto) { 2187 result += `${sepa}${ObservedObject.tracePrototype(proto)}`; 2188 proto = Object.getPrototypeOf(proto); 2189 sepa = ",\n"; 2190 } 2191 return result; 2192 } 2193 /** 2194 * Utility function for debugging all functions of given Prototype. 2195 * @returns string containing containing names of all functions and members of given Prototype 2196 */ 2197 static tracePrototype(proto) { 2198 if (!proto) { 2199 return ""; 2200 } 2201 let result = `${proto.constructor && proto.constructor.name ? proto.constructor.name : '<no class>'}: `; 2202 let sepa = ""; 2203 for (let name of Object.getOwnPropertyNames(proto)) { 2204 result += `${sepa}${name}`; 2205 sepa = ", "; 2206 } 2207 ; 2208 return result; 2209 } 2210 /** 2211 * @Observed decorator extends the decorated class. This function returns the prototype of the decorated class 2212 * @param proto 2213 * @returns prototype of the @Observed decorated class or 'proto' parameter if not @Observed decorated 2214 */ 2215 static getPrototypeOfObservedClass(proto) { 2216 return (proto.constructor && proto.constructor.name == "ObservedClass") 2217 ? Object.getPrototypeOf(proto.constructor.prototype) 2218 : proto; 2219 } 2220 /** 2221 * To create a new ObservableObject use CreateNew function 2222 * 2223 * constructor create a new ObservableObject and subscribe its owner to propertyHasChanged 2224 * notifications 2225 * @param obj raw Object, if obj is a ObservableOject throws an error 2226 * @param objectOwner 2227 */ 2228 constructor(obj, handler, objectOwningProperty) { 2229 super(obj, handler); 2230 if (ObservedObject.IsObservedObject(obj)) { 2231 stateMgmtConsole.error("ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already"); 2232 } 2233 if (objectOwningProperty != undefined) { 2234 this[SubscribableHandler.SUBSCRIBE] = objectOwningProperty; 2235 } 2236 } // end of constructor 2237} 2238ObservedObject.__IS_OBSERVED_OBJECT = Symbol("_____is_observed_object__"); 2239ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT = Symbol("_____raw_object__"); 2240/* 2241 * Copyright (c) 2021 Huawei Device Co., Ltd. 2242 * Licensed under the Apache License, Version 2.0 (the "License"); 2243 * you may not use this file except in compliance with the License. 2244 * You may obtain a copy of the License at 2245 * 2246 * http://www.apache.org/licenses/LICENSE-2.0 2247 * 2248 * Unless required by applicable law or agreed to in writing, software 2249 * distributed under the License is distributed on an "AS IS" BASIS, 2250 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2251 * See the License for the specific language governing permissions and 2252 * limitations under the License. 2253 */ 2254/* 2255 manage subscriptions to a property 2256 managing the property is left to sub 2257 classes 2258 Extended by ObservedProperty, SyncedPropertyOneWay 2259 and SyncedPropertyTwoWay 2260*/ 2261class ObservedPropertyAbstract extends SubscribedAbstractProperty { 2262 constructor(subscribeMe, info) { 2263 super(); 2264 this.subscribers_ = new Set(); 2265 this.id_ = SubscriberManager.MakeId(); 2266 SubscriberManager.Add(this); 2267 if (subscribeMe) { 2268 this.subscribers_.add(subscribeMe.id__()); 2269 } 2270 if (info) { 2271 this.info_ = info; 2272 } 2273 } 2274 aboutToBeDeleted() { 2275 SubscriberManager.Delete(this.id__()); 2276 } 2277 id__() { 2278 return this.id_; 2279 } 2280 info() { 2281 return this.info_; 2282 } 2283 setInfo(propName) { 2284 if (propName && propName != "") { 2285 this.info_ = propName; 2286 } 2287 } 2288 // Partial Update "*PU" classes will overwrite 2289 getUnmonitored() { 2290 return this.get(); 2291 } 2292 // update the element id for recycle custom component 2293 updateElmtId(oldElmtId, newElmtId) { 2294 if (this.subscribers_.has(oldElmtId)) { 2295 this.subscribers_.delete(oldElmtId); 2296 this.subscribers_.add(newElmtId); 2297 } 2298 } 2299 // Method name is used to check object is of type ObservedPropertyAbstract 2300 // Do NOT override in derived classed, use addSubscriber 2301 subscribeMe(subscriber) { 2302 2303 this.subscribers_.add(subscriber.id__()); 2304 } 2305 /* 2306 the inverse function of createOneWaySync or createTwoWaySync 2307 Do NOT override in derived classed, use removeSubscriber 2308 */ 2309 unlinkSuscriber(subscriberId) { 2310 this.subscribers_.delete(subscriberId); 2311 } 2312 /* 2313 Virtualized version of the subscription mechanism - add subscriber 2314 */ 2315 addSubscriber(subscriber) { 2316 if (subscriber) { 2317 this.subscribeMe(subscriber); 2318 } 2319 } 2320 /* 2321 Virtualized version of the subscription mechanism - remove subscriber 2322 */ 2323 removeSubscriber(subscriber, id) { 2324 if (id) { 2325 this.unlinkSuscriber(id); 2326 } 2327 else if (subscriber) { 2328 this.unlinkSuscriber(subscriber.id__()); 2329 } 2330 } 2331 notifyHasChanged(newValue) { 2332 2333 this.subscribers_.forEach((subscribedId) => { 2334 var subscriber = SubscriberManager.Find(subscribedId); 2335 if (subscriber) { 2336 // FU code path 2337 if ('hasChanged' in subscriber) { 2338 subscriber.hasChanged(newValue); 2339 } 2340 if ('propertyHasChanged' in subscriber) { 2341 subscriber.propertyHasChanged(this.info_); 2342 } 2343 // PU code path, only used for ObservedPropertySimple/Object stored inside App/LocalStorage 2344 // ObservedPropertySimplePU/ObjectPU used in all other PU cases, has its own notifyPropertyHasChangedPU() 2345 if ('syncPeerHasChanged' in subscriber) { 2346 subscriber.syncPeerHasChanged(this); 2347 } 2348 } 2349 else { 2350 stateMgmtConsole.warn(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: notifyHasChanged: unknown subscriber ID '${subscribedId}' error!`); 2351 } 2352 }); 2353 } 2354 notifyPropertyRead() { 2355 2356 this.subscribers_.forEach((subscribedId) => { 2357 var subscriber = SubscriberManager.Find(subscribedId); 2358 if (subscriber) { 2359 if ('propertyRead' in subscriber) { 2360 subscriber.propertyRead(this.info_); 2361 } 2362 } 2363 }); 2364 } 2365 /* 2366 return numebr of subscribers to this property 2367 mostly useful for unit testin 2368 */ 2369 numberOfSubscrbers() { 2370 return this.subscribers_.size; 2371 } 2372 /** 2373 * provide a factory function that creates a SynchedPropertyXXXX of choice 2374 * that uses 'this' as source 2375 * @param factoryFunc 2376 * @returns 2377 */ 2378 createSync(factoryFunc) { 2379 return factoryFunc(this); 2380 } 2381 /** 2382 * depreciated SDK function, not used anywhere by the framework 2383 */ 2384 createTwoWaySync(subscribeMe, info) { 2385 stateMgmtConsole.warn("Using depreciated method 'createTwoWaySync'!"); 2386 return this.createLink(subscribeMe, info); 2387 } 2388 /** 2389 * depreciated SDK function, not used anywhere by the framework 2390 */ 2391 createOneWaySync(subscribeMe, info) { 2392 stateMgmtConsole.warn("Using depreciated method 'createOneWaySync' !"); 2393 return this.createProp(subscribeMe, info); 2394 } 2395 /** 2396 * factory function for concrete 'object' or 'simple' ObservedProperty object 2397 * depending if value is Class object 2398 * or simple type (boolean | number | string) 2399 * @param value 2400 * @param owningView 2401 * @param thisPropertyName 2402 * @returns either 2403 */ 2404 static CreateObservedObject(value, owningView, thisPropertyName) { 2405 return (typeof value === "object") ? 2406 new ObservedPropertyObject(value, owningView, thisPropertyName) 2407 : new ObservedPropertySimple(value, owningView, thisPropertyName); 2408 } 2409} 2410/* 2411 * Copyright (c) 2021 Huawei Device Co., Ltd. 2412 * Licensed under the Apache License, Version 2.0 (the "License"); 2413 * you may not use this file except in compliance with the License. 2414 * You may obtain a copy of the License at 2415 * 2416 * http://www.apache.org/licenses/LICENSE-2.0 2417 * 2418 * Unless required by applicable law or agreed to in writing, software 2419 * distributed under the License is distributed on an "AS IS" BASIS, 2420 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2421 * See the License for the specific language governing permissions and 2422 * limitations under the License. 2423 */ 2424/** 2425 * ObservedPropertyObjectAbstract 2426 * 2427 * all definitions in this file are framework internal 2428 * 2429 * common base class of ObservedPropertyObject and 2430 * SyncedObjectPropertyTwoWay 2431 * adds the createObjectLink to the ObservedPropertyAbstract base 2432 */ 2433class ObservedPropertyObjectAbstract extends ObservedPropertyAbstract { 2434 constructor(owningView, thisPropertyName) { 2435 super(owningView, thisPropertyName); 2436 } 2437} 2438/* 2439 * Copyright (c) 2021 Huawei Device Co., Ltd. 2440 * Licensed under the Apache License, Version 2.0 (the "License"); 2441 * you may not use this file except in compliance with the License. 2442 * You may obtain a copy of the License at 2443 * 2444 * http://www.apache.org/licenses/LICENSE-2.0 2445 * 2446 * Unless required by applicable law or agreed to in writing, software 2447 * distributed under the License is distributed on an "AS IS" BASIS, 2448 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2449 * See the License for the specific language governing permissions and 2450 * limitations under the License. 2451 */ 2452/** 2453 * 2454 * ObservedPropertySimpleAbstract 2455 * 2456 * all definitions in this file are framework internal 2457 */ 2458class ObservedPropertySimpleAbstract extends ObservedPropertyAbstract { 2459 constructor(owningView, propertyName) { 2460 super(owningView, propertyName); 2461 } 2462} 2463/* 2464 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2465 * Licensed under the Apache License, Version 2.0 (the "License"); 2466 * you may not use this file except in compliance with the License. 2467 * You may obtain a copy of the License at 2468 * 2469 * http://www.apache.org/licenses/LICENSE-2.0 2470 * 2471 * Unless required by applicable law or agreed to in writing, software 2472 * distributed under the License is distributed on an "AS IS" BASIS, 2473 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2474 * See the License for the specific language governing permissions and 2475 * limitations under the License. 2476 */ 2477/** 2478 * ObservedPropertyObject 2479 * 2480 * all definitions in this file are framework internal 2481 * 2482 * class that holds an actual property value of type T 2483 * uses its base class to manage subscribers to this 2484 * property. 2485*/ 2486class ObservedPropertyObject extends ObservedPropertyObjectAbstract { 2487 constructor(value, owningView, propertyName) { 2488 super(owningView, propertyName); 2489 this.setValueInternal(value); 2490 } 2491 aboutToBeDeleted(unsubscribeMe) { 2492 this.unsubscribeFromOwningProperty(); 2493 if (unsubscribeMe) { 2494 this.unlinkSuscriber(unsubscribeMe.id__()); 2495 } 2496 super.aboutToBeDeleted(); 2497 } 2498 // notification from ObservedObject value one of its 2499 // props has chnaged. Implies the ObservedProperty has changed 2500 // Note: this function gets called when in this case: 2501 // thisProp.aObsObj.aProp = 47 a object prop gets changed 2502 // It is NOT called when 2503 // thisProp.aObsObj = new ClassA 2504 hasChanged(newValue) { 2505 2506 this.notifyHasChanged(this.wrappedValue_); 2507 } 2508 unsubscribeFromOwningProperty() { 2509 if (this.wrappedValue_) { 2510 if (this.wrappedValue_ instanceof SubscribaleAbstract) { 2511 this.wrappedValue_.removeOwningProperty(this); 2512 } 2513 else { 2514 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 2515 } 2516 } 2517 } 2518 /* 2519 actually update this.wrappedValue_ 2520 called needs to do value change check 2521 and also notify with this.aboutToChange(); 2522 */ 2523 setValueInternal(newValue) { 2524 if (typeof newValue !== 'object') { 2525 2526 return false; 2527 } 2528 this.unsubscribeFromOwningProperty(); 2529 if (ObservedObject.IsObservedObject(newValue)) { 2530 2531 ObservedObject.addOwningProperty(newValue, this); 2532 this.wrappedValue_ = newValue; 2533 } 2534 else if (newValue instanceof SubscribaleAbstract) { 2535 2536 this.wrappedValue_ = newValue; 2537 this.wrappedValue_.addOwningProperty(this); 2538 } 2539 else { 2540 2541 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 2542 } 2543 return true; 2544 } 2545 get() { 2546 2547 this.notifyPropertyRead(); 2548 return this.wrappedValue_; 2549 } 2550 set(newValue) { 2551 if (this.wrappedValue_ == newValue) { 2552 2553 return; 2554 } 2555 2556 this.setValueInternal(newValue); 2557 this.notifyHasChanged(newValue); 2558 } 2559 /** 2560 * These functions are used 2561 * LocalStorage.link (also in partial update config) 2562 * (FU)View.initializeConsumeinitializeConsume 2563 */ 2564 createLink(subscribeOwner, linkPropName) { 2565 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 2566 } 2567 createProp(subscribeOwner, linkPropName) { 2568 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 2569 } 2570} 2571/* 2572 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2573 * Licensed under the Apache License, Version 2.0 (the "License"); 2574 * you may not use this file except in compliance with the License. 2575 * You may obtain a copy of the License at 2576 * 2577 * http://www.apache.org/licenses/LICENSE-2.0 2578 * 2579 * Unless required by applicable law or agreed to in writing, software 2580 * distributed under the License is distributed on an "AS IS" BASIS, 2581 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2582 * See the License for the specific language governing permissions and 2583 * limitations under the License. 2584 */ 2585/** 2586 * ObservedPropertySimple 2587 * 2588 * all definitions in this file are framework internal 2589 */ 2590class ObservedPropertySimple extends ObservedPropertySimpleAbstract { 2591 constructor(value, owningView, propertyName) { 2592 super(owningView, propertyName); 2593 if (typeof value === "object") { 2594 throw new SyntaxError("ObservedPropertySimple value must not be an object"); 2595 } 2596 this.setValueInternal(value); 2597 } 2598 aboutToBeDeleted(unsubscribeMe) { 2599 if (unsubscribeMe) { 2600 this.unlinkSuscriber(unsubscribeMe.id__()); 2601 } 2602 super.aboutToBeDeleted(); 2603 } 2604 hasChanged(newValue) { 2605 2606 this.notifyHasChanged(this.wrappedValue_); 2607 } 2608 /* 2609 actually update this.wrappedValue_ 2610 called needs to do value change check 2611 and also notify with this.aboutToChange(); 2612 */ 2613 setValueInternal(newValue) { 2614 2615 this.wrappedValue_ = newValue; 2616 } 2617 get() { 2618 2619 this.notifyPropertyRead(); 2620 return this.wrappedValue_; 2621 } 2622 set(newValue) { 2623 if (this.wrappedValue_ == newValue) { 2624 2625 return; 2626 } 2627 2628 this.setValueInternal(newValue); 2629 this.notifyHasChanged(newValue); 2630 } 2631 /** 2632 * These functions are meant for use in connection with the App Stoage and 2633 * business logic implementation. 2634 * the created Link and Prop will update when 'this' property value 2635 * changes. 2636 */ 2637 createLink(subscribeOwner, linkPropName) { 2638 return ((subscribeOwner !== undefined) && ("rerender" in subscribeOwner)) ? 2639 new SynchedPropertySimpleTwoWayPU(this, subscribeOwner, linkPropName) : 2640 new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 2641 } 2642 createProp(subscribeOwner, linkPropName) { 2643 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, linkPropName); 2644 } 2645} 2646/* 2647 * Copyright (c) 2021 Huawei Device Co., Ltd. 2648 * Licensed under the Apache License, Version 2.0 (the "License"); 2649 * you may not use this file except in compliance with the License. 2650 * You may obtain a copy of the License at 2651 * 2652 * http://www.apache.org/licenses/LICENSE-2.0 2653 * 2654 * Unless required by applicable law or agreed to in writing, software 2655 * distributed under the License is distributed on an "AS IS" BASIS, 2656 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2657 * See the License for the specific language governing permissions and 2658 * limitations under the License. 2659 */ 2660/** 2661 * SynchedPropertyObjectTwoWay 2662 * 2663 * all definitions in this file are framework internal 2664 */ 2665class SynchedPropertyObjectTwoWay extends ObservedPropertyObjectAbstract { 2666 constructor(linkSource, owningChildView, thisPropertyName) { 2667 super(owningChildView, thisPropertyName); 2668 this.changeNotificationIsOngoing_ = false; 2669 this.linkedParentProperty_ = linkSource; 2670 if (this.linkedParentProperty_) { 2671 // register to the parent property 2672 this.linkedParentProperty_.subscribeMe(this); 2673 } 2674 // register to the ObservedObject 2675 ObservedObject.addOwningProperty(this.getObject(), this); 2676 } 2677 /* 2678 like a destructor, need to call this before deleting 2679 the property. 2680 */ 2681 aboutToBeDeleted() { 2682 if (this.linkedParentProperty_) { 2683 // unregister from parent of this link 2684 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 2685 // unregister from the ObservedObject 2686 ObservedObject.removeOwningProperty(this.getObject(), this); 2687 } 2688 super.aboutToBeDeleted(); 2689 } 2690 getObject() { 2691 this.notifyPropertyRead(); 2692 return (this.linkedParentProperty_ ? this.linkedParentProperty_.get() : undefined); 2693 } 2694 setObject(newValue) { 2695 if (this.linkedParentProperty_) { 2696 this.linkedParentProperty_.set(newValue); 2697 } 2698 } 2699 // this object is subscriber to ObservedObject 2700 // will call this cb function when property has changed 2701 hasChanged(newValue) { 2702 if (!this.changeNotificationIsOngoing_) { 2703 2704 this.notifyHasChanged(this.getObject()); 2705 } 2706 } 2707 // get 'read through` from the ObservedProperty 2708 get() { 2709 2710 return this.getObject(); 2711 } 2712 // set 'writes through` to the ObservedProperty 2713 set(newValue) { 2714 if (this.getObject() == newValue) { 2715 2716 return; 2717 } 2718 2719 ObservedObject.removeOwningProperty(this.getObject(), this); 2720 // the purpose of the changeNotificationIsOngoing_ is to avoid 2721 // circular notifications @Link -> source @State -> other but alos same @Link 2722 this.changeNotificationIsOngoing_ = true; 2723 this.setObject(newValue); 2724 ObservedObject.addOwningProperty(this.getObject(), this); 2725 this.notifyHasChanged(newValue); 2726 this.changeNotificationIsOngoing_ = false; 2727 } 2728 /** 2729 * These functions are meant for use in connection with the App Stoage and 2730 * business logic implementation. 2731 * the created Link and Prop will update when 'this' property value 2732 * changes. 2733 */ 2734 createLink(subscribeOwner, linkPropName) { 2735 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 2736 } 2737 createProp(subscribeOwner, linkPropName) { 2738 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 2739 } 2740} 2741/* 2742 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2743 * Licensed under the Apache License, Version 2.0 (the "License"); 2744 * you may not use this file except in compliance with the License. 2745 * You may obtain a copy of the License at 2746 * 2747 * http://www.apache.org/licenses/LICENSE-2.0 2748 * 2749 * Unless required by applicable law or agreed to in writing, software 2750 * distributed under the License is distributed on an "AS IS" BASIS, 2751 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2752 * See the License for the specific language governing permissions and 2753 * limitations under the License. 2754 */ 2755/** 2756 * SynchedPropertySimpleOneWay 2757 * 2758 * all definitions in this file are framework internal 2759 */ 2760class SynchedPropertySimpleOneWay extends ObservedPropertySimpleAbstract { 2761 constructor(value, subscribeMe, info) { 2762 super(subscribeMe, info); 2763 // add a test here that T is a simple type 2764 this.wrappedValue_ = value; 2765 } 2766 /* 2767 like a destructor, need to call this before deleting 2768 the property. 2769 */ 2770 aboutToBeDeleted() { 2771 super.aboutToBeDeleted(); 2772 } 2773 // get 'read through` from the ObservedProperty 2774 get() { 2775 2776 this.notifyPropertyRead(); 2777 return this.wrappedValue_; 2778 } 2779 set(newValue) { 2780 if (this.wrappedValue_ == newValue) { 2781 2782 return; 2783 } 2784 2785 this.wrappedValue_ = newValue; 2786 this.notifyHasChanged(newValue); 2787 } 2788 /** 2789 * These functions are meant for use in connection with the App Stoage and 2790 * business logic implementation. 2791 * the created Link and Prop will update when 'this' property value 2792 * changes. 2793 */ 2794 createLink(subscribeOwner, linkPropName) { 2795 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 2796 } 2797 createProp(subscribeOwner, linkPropName) { 2798 throw new Error("Method not supported, create a SynchedPropertySimpleOneWaySubscribing from, where to create a Prop."); 2799 } 2800} 2801/* 2802 This exrension of SynchedPropertySimpleOneWay needs to be used for AppStorage 2803 because it needs to be notified about the source property changing 2804 ( there is no re-render process as in Views to update the wrappedValue ) 2805*/ 2806class SynchedPropertySimpleOneWaySubscribing extends SynchedPropertySimpleOneWay { 2807 constructor(linkedProperty, subscribeMe, info) { 2808 super(linkedProperty.get(), subscribeMe, info); 2809 this.linkedParentProperty_ = linkedProperty; 2810 this.linkedParentProperty_.subscribeMe(this); 2811 } 2812 aboutToBeDeleted() { 2813 // unregister from parent of this prop 2814 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 2815 super.aboutToBeDeleted(); 2816 } 2817 hasChanged(newValue) { 2818 2819 this.set(newValue); 2820 } 2821 /** 2822 * These functions are meant for use in connection with the App Stoage and 2823 * business logic implementation. 2824 * the created Link and Prop will update when 'this' property value 2825 * changes. 2826 */ 2827 createLink(subscribeOwner, linkPropName) { 2828 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 2829 } 2830 createProp(subscribeOwner, propPropName) { 2831 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 2832 } 2833} 2834/* 2835 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2836 * Licensed under the Apache License, Version 2.0 (the "License"); 2837 * you may not use this file except in compliance with the License. 2838 * You may obtain a copy of the License at 2839 * 2840 * http://www.apache.org/licenses/LICENSE-2.0 2841 * 2842 * Unless required by applicable law or agreed to in writing, software 2843 * distributed under the License is distributed on an "AS IS" BASIS, 2844 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2845 * See the License for the specific language governing permissions and 2846 * limitations under the License. 2847 */ 2848/** 2849 * SynchedPropertySimpleTwoWay 2850 * 2851 * all definitions in this file are framework internal 2852 */ 2853class SynchedPropertySimpleTwoWay extends ObservedPropertySimpleAbstract { 2854 constructor(source, owningView, owningViewPropNme) { 2855 super(owningView, owningViewPropNme); 2856 this.changeNotificationIsOngoing_ = false; 2857 this.source_ = source; 2858 this.source_.subscribeMe(this); 2859 } 2860 /* 2861 like a destructor, need to call this before deleting 2862 the property. 2863 */ 2864 aboutToBeDeleted() { 2865 if (this.source_) { 2866 this.source_.unlinkSuscriber(this.id__()); 2867 this.source_ = undefined; 2868 } 2869 super.aboutToBeDeleted(); 2870 } 2871 // this object is subscriber to SynchedPropertySimpleTwoWay 2872 // will call this cb function when property has changed 2873 // a set (newValue) is not done because get reads through for the source_ 2874 hasChanged(newValue) { 2875 if (!this.changeNotificationIsOngoing_) { 2876 2877 this.notifyHasChanged(newValue); 2878 } 2879 } 2880 // get 'read through` from the ObservedProperty 2881 get() { 2882 2883 if (!this.source_) { 2884 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || "unknown"}'] source_ is undefined: get value is undefined.`); 2885 return undefined; 2886 } 2887 this.notifyPropertyRead(); 2888 return this.source_.get(); 2889 } 2890 // set 'writes through` to the ObservedProperty 2891 set(newValue) { 2892 if (!this.source_) { 2893 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || "unknown"}'] source_ is undefined: set '${newValue}' ignoring.`); 2894 return; 2895 } 2896 if (this.source_.get() == newValue) { 2897 2898 return; 2899 } 2900 2901 // the source_ ObservedProeprty will call: this.hasChanged(newValue); 2902 // the purpose of the changeNotificationIsOngoing_ is to avoid 2903 // circular notifications @Link -> source @State -> other but alos same @Link 2904 this.changeNotificationIsOngoing_ = true; 2905 this.source_.set(newValue); 2906 this.notifyHasChanged(newValue); 2907 this.changeNotificationIsOngoing_ = false; 2908 } 2909 /** 2910 * These functions are meant for use in connection with the App Stoage and 2911 * business logic implementation. 2912 * the created Link and Prop will update when 'this' property value 2913 * changes. 2914 */ 2915 createLink(subscribeOwner, linkPropName) { 2916 return new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 2917 } 2918 createProp(subscribeOwner, propPropName) { 2919 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 2920 } 2921} 2922/* 2923 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2924 * Licensed under the Apache License, Version 2.0 (the "License"); 2925 * you may not use this file except in compliance with the License. 2926 * You may obtain a copy of the License at 2927 * 2928 * http://www.apache.org/licenses/LICENSE-2.0 2929 * 2930 * Unless required by applicable law or agreed to in writing, software 2931 * distributed under the License is distributed on an "AS IS" BASIS, 2932 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2933 * See the License for the specific language governing permissions and 2934 * limitations under the License. 2935 */ 2936/** 2937 * SynchedPropertyNesedObject 2938 * 2939 * all definitions in this file are framework internal 2940 */ 2941class SynchedPropertyNesedObject extends ObservedPropertyObjectAbstract { 2942 /** 2943 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 2944 * example 2945 * this.b.$a with b of type PC and a of type C, or 2946 * this.$b[5] with this.b of type PC and array item b[5] of type C; 2947 * 2948 * @param subscribeMe 2949 * @param propName 2950 */ 2951 constructor(obsObject, owningChildView, propertyName) { 2952 super(owningChildView, propertyName); 2953 this.obsObject_ = obsObject; 2954 // register to the ObservedObject 2955 ObservedObject.addOwningProperty(this.obsObject_, this); 2956 } 2957 /* 2958 like a destructor, need to call this before deleting 2959 the property. 2960 */ 2961 aboutToBeDeleted() { 2962 // unregister from the ObservedObject 2963 ObservedObject.removeOwningProperty(this.obsObject_, this); 2964 super.aboutToBeDeleted(); 2965 } 2966 // this object is subscriber to ObservedObject 2967 // will call this cb function when property has changed 2968 hasChanged(newValue) { 2969 2970 this.notifyHasChanged(this.obsObject_); 2971 } 2972 // get 'read through` from the ObservedProperty 2973 get() { 2974 2975 this.notifyPropertyRead(); 2976 return this.obsObject_; 2977 } 2978 // set 'writes through` to the ObservedProperty 2979 set(newValue) { 2980 if (this.obsObject_ == newValue) { 2981 2982 return; 2983 } 2984 2985 // unsubscribe from the old value ObservedObject 2986 ObservedObject.removeOwningProperty(this.obsObject_, this); 2987 this.obsObject_ = newValue; 2988 // subscribe to the new value ObservedObject 2989 ObservedObject.addOwningProperty(this.obsObject_, this); 2990 // notify value change to subscribing View 2991 this.notifyHasChanged(this.obsObject_); 2992 } 2993 /** 2994 * These functions are meant for use in connection with the App Stoage and 2995 * business logic implementation. 2996 * the created Link and Prop will update when 'this' property value 2997 * changes. 2998 */ 2999 createLink(subscribeOwner, linkPropName) { 3000 throw new Error("Method not supported for property linking to a nested objects."); 3001 } 3002 createProp(subscribeOwner, linkPropName) { 3003 throw new Error("Creating a 'Prop' proerty is unsuppoeted for Object type prperty value."); 3004 } 3005} 3006/* 3007 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3008 * Licensed under the Apache License, Version 2.0 (the "License"); 3009 * you may not use this file except in compliance with the License. 3010 * You may obtain a copy of the License at 3011 * 3012 * http://www.apache.org/licenses/LICENSE-2.0 3013 * 3014 * Unless required by applicable law or agreed to in writing, software 3015 * distributed under the License is distributed on an "AS IS" BASIS, 3016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3017 * See the License for the specific language governing permissions and 3018 * limitations under the License. 3019 */ 3020// Nativeview 3021// implemented in C++ for release 3022// and in utest/view_native_mock.ts for testing 3023class View extends NativeViewFullUpdate { 3024 get localStorage_() { 3025 if (!this.localStoragebackStore_) { 3026 3027 this.localStoragebackStore_ = new LocalStorage({ /* emty */}); 3028 } 3029 return this.localStoragebackStore_; 3030 } 3031 set localStorage_(instance) { 3032 if (!instance) { 3033 // setting to undefined not allowed 3034 return; 3035 } 3036 if (this.localStoragebackStore_) { 3037 stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); 3038 } 3039 this.localStoragebackStore_ = instance; 3040 } 3041 /** 3042 * Create a View 3043 * 3044 * 1. option: top level View, specify 3045 * - compilerAssignedUniqueChildId must specify 3046 * - parent=undefined 3047 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 3048 * in this View or descendant Views. 3049 * 3050 * 2. option: not a top level View 3051 * - compilerAssignedUniqueChildId must specify 3052 * - parent must specify 3053 * - localStorage do not specify, will inherit from parent View. 3054 * 3055 * @param compilerAssignedUniqueChildId Tw 3056 * @param parent 3057 * @param localStorage 3058 */ 3059 constructor(compilerAssignedUniqueChildId, parent, localStorage) { 3060 super(compilerAssignedUniqueChildId, parent); 3061 this.propsUsedForRender = new Set(); 3062 this.isRenderingInProgress = false; 3063 this.watchedProps = new Map(); 3064 // my LocalStorge instance, shared with ancestor Views. 3065 // create a default instance on demand if none is initialized 3066 this.localStoragebackStore_ = undefined; 3067 this.id_ = SubscriberManager.MakeId(); 3068 this.providedVars_ = parent ? new Map(parent.providedVars_) 3069 : new Map(); 3070 this.localStoragebackStore_ = undefined; 3071 if (parent) { 3072 // this View is not a top-level View 3073 3074 this.setCardId(parent.getCardId()); 3075 this.localStorage_ = parent.localStorage_; 3076 } 3077 else if (localStorage) { 3078 this.localStorage_ = localStorage; 3079 3080 } 3081 SubscriberManager.Add(this); 3082 3083 } 3084 // globally unique id, this is different from compilerAssignedUniqueChildId! 3085 id__() { 3086 return this.id_; 3087 } 3088 // temporary function, do not use, it will be removed soon! 3089 // prupsoe is to allow eDSL transpiler to fix a bug that 3090 // relies on this method 3091 id() { 3092 return this.id__(); 3093 } 3094 propertyHasChanged(info) { 3095 if (info) { 3096 // need to sync container instanceId to switch instanceId in C++ side. 3097 this.syncInstanceId(); 3098 if (this.propsUsedForRender.has(info)) { 3099 3100 this.markNeedUpdate(); 3101 } 3102 else { 3103 3104 } 3105 let cb = this.watchedProps.get(info); 3106 if (cb) { 3107 3108 cb.call(this, info); 3109 } 3110 this.restoreInstanceId(); 3111 } // if info avail. 3112 } 3113 propertyRead(info) { 3114 3115 if (info && (info != "unknown") && this.isRenderingInProgress) { 3116 this.propsUsedForRender.add(info); 3117 } 3118 } 3119 // for test purposes 3120 propertiesNeededToRender() { 3121 return this.propsUsedForRender; 3122 } 3123 aboutToRender() { 3124 3125 // reset 3126 this.propsUsedForRender = new Set(); 3127 this.isRenderingInProgress = true; 3128 } 3129 aboutToContinueRender() { 3130 // do not reset 3131 this.isRenderingInProgress = true; 3132 } 3133 onRenderDone() { 3134 this.isRenderingInProgress = false; 3135 3136 } 3137 /** 3138 * Function to be called from the constructor of the sub component 3139 * to register a @Watch varibale 3140 * @param propStr name of the variable. Note from @Provide and @Consume this is 3141 * the variable name and not the alias! 3142 * @param callback application defined member function of sub-class 3143 */ 3144 declareWatch(propStr, callback) { 3145 this.watchedProps.set(propStr, callback); 3146 } 3147 /** 3148 * This View @Provide's a variable under given name 3149 * Call this function from the constructor of the sub class 3150 * @param providedPropName either the variable name or the alias defined as 3151 * decorator param 3152 * @param store the backing store object for this variable (not the get/set variable!) 3153 */ 3154 addProvidedVar(providedPropName, store) { 3155 if (this.providedVars_.has(providedPropName)) { 3156 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. 3157 Property with this name is provided by one of the ancestor Views already.`); 3158 } 3159 this.providedVars_.set(providedPropName, store); 3160 } 3161 /** 3162 * Method for the sub-class to call from its constructor for resolving 3163 * a @Consume variable and initializing its backing store 3164 * with the yncedPropertyTwoWay<T> object created from the 3165 * @Provide variable's backing store. 3166 * @param providedPropName the name of the @Provide'd variable. 3167 * This is either the @Consume decortor parameter, or variable name. 3168 * @param consumeVarName the @Consume variable name (not the 3169 * @Consume decortor parameter) 3170 * @returns initiaizing value of the @Consume backing store 3171 */ 3172 initializeConsume(providedPropName, consumeVarName) { 3173 let providedVarStore = this.providedVars_.get(providedPropName); 3174 if (providedVarStore === undefined) { 3175 throw new ReferenceError(`${this.constructor.name}: missing @Provide property with name ${providedPropName}. 3176 Fail to resolve @Consume(${providedPropName}).`); 3177 } 3178 return providedVarStore.createLink(this, consumeVarName); 3179 } 3180} 3181/* 3182 * Copyright (c) 2022 Huawei Device Co., Ltd. 3183 * Licensed under the Apache License, Version 2.0 (the "License"); 3184 * you may not use this file except in compliance with the License. 3185 * You may obtain a copy of the License at 3186 * 3187 * http://www.apache.org/licenses/LICENSE-2.0 3188 * 3189 * Unless required by applicable law or agreed to in writing, software 3190 * distributed under the License is distributed on an "AS IS" BASIS, 3191 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3192 * See the License for the specific language governing permissions and 3193 * limitations under the License. 3194 */ 3195/* 3196 * Copyright (c) 2022 Huawei Device Co., Ltd. 3197 * Licensed under the Apache License, Version 2.0 (the "License"); 3198 * you may not use this file except in compliance with the License. 3199 * You may obtain a copy of the License at 3200 * 3201 * http://www.apache.org/licenses/LICENSE-2.0 3202 * 3203 * Unless required by applicable law or agreed to in writing, software 3204 * distributed under the License is distributed on an "AS IS" BASIS, 3205 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3206 * See the License for the specific language governing permissions and 3207 * limitations under the License. 3208 */ 3209var _a; 3210/** 3211 * ObservedPropertyAbstractPU aka ObservedPropertyAbstract for partial update 3212 * 3213 * all definitions in this file are framework internal 3214 */ 3215class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { 3216 constructor(subscriber, viewName) { 3217 super(subscriber, viewName); 3218 this.owningView_ = undefined; 3219 this.dependentElementIds_ = new Set(); 3220 // when owning ViewPU is inActive, delay notifying changes 3221 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 3222 Object.defineProperty(this, 'owningView_', { writable: true, enumerable: false }); 3223 Object.defineProperty(this, 'subscriberRefs_', { writable: true, enumerable: false, value: new Set() }); 3224 if (subscriber) { 3225 if (subscriber instanceof ViewPU) { 3226 this.owningView_ = subscriber; 3227 } 3228 else { 3229 this.subscriberRefs_.add(subscriber); 3230 } 3231 } 3232 } 3233 aboutToBeDeleted() { 3234 super.aboutToBeDeleted(); 3235 this.subscriberRefs_.clear(); 3236 this.owningView_ = undefined; 3237 } 3238 /* 3239 Virtualized version of the subscription mechanism - add subscriber 3240 Overrides implementation in ObservedPropertyAbstract<T> 3241 */ 3242 addSubscriber(subscriber) { 3243 if (subscriber) { 3244 // ObservedPropertyAbstract will also add subscriber to 3245 // SubscriberManager map and to its own Set of subscribers as well 3246 // Something to improve in the future for PU path. 3247 // subscribeMe should accept IPropertySubscriber interface 3248 super.subscribeMe(subscriber); 3249 this.subscriberRefs_.add(subscriber); 3250 } 3251 } 3252 /* 3253 Virtualized version of the subscription mechanism - remove subscriber 3254 Overrides implementation in ObservedPropertyAbstract<T> 3255 */ 3256 removeSubscriber(subscriber, id) { 3257 if (subscriber) { 3258 this.subscriberRefs_.delete(subscriber); 3259 if (!id) { 3260 id = subscriber.id__(); 3261 } 3262 } 3263 super.unlinkSuscriber(id); 3264 } 3265 /** 3266 * put the property to delayed notification mode 3267 * feature is only used for @StorageLink/Prop, @LocalStorageLink/Prop 3268 */ 3269 enableDelayedNotification() { 3270 if (this.delayedNotification_ != ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) { 3271 3272 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_none_pending; 3273 } 3274 } 3275 /* 3276 when moving from inActive to active state the owning ViewPU calls this function 3277 This solution is faster than ViewPU polling each variable to send back a viewPropertyHasChanged event 3278 with the elmtIds 3279 3280 returns undefined if variable has _not_ changed 3281 returns dependentElementIds_ Set if changed. This Set is empty if variable is not used to construct the UI 3282 */ 3283 moveElmtIdsForDelayedUpdate() { 3284 const result = (this.delayedNotification_ == ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) 3285 ? this.dependentElementIds_ 3286 : undefined; 3287 3288 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 3289 return result; 3290 } 3291 notifyPropertyRead() { 3292 stateMgmtConsole.error(`ObservedPropertyAbstractPU[${this.id__()}, '${this.info() || "unknown"}']: \ 3293 notifyPropertyRead, DO NOT USE with PU. Use notifyPropertyHasBeenReadPU`); 3294 } 3295 notifyPropertyHasBeenReadPU() { 3296 3297 this.subscriberRefs_.forEach((subscriber) => { 3298 if (subscriber) { 3299 // TODO 3300 // propertyHasBeenReadPU is not use in the code 3301 // defined by interface that is not used either: PropertyReadEventListener 3302 // Maybe compiler generated code has it? 3303 if ('propertyHasBeenReadPU' in subscriber) { 3304 subscriber.propertyHasBeenReadPU(this); 3305 } 3306 } 3307 }); 3308 this.recordDependentUpdate(); 3309 } 3310 notifyPropertyHasChangedPU() { 3311 3312 if (this.owningView_) { 3313 if (this.delayedNotification_ == ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) { 3314 // send viewPropertyHasChanged right away 3315 this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElementIds_); 3316 } 3317 else { 3318 // mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending 3319 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending; 3320 } 3321 } 3322 this.subscriberRefs_.forEach((subscriber) => { 3323 if (subscriber) { 3324 if ('syncPeerHasChanged' in subscriber) { 3325 subscriber.syncPeerHasChanged(this); 3326 } 3327 else { 3328 stateMgmtConsole.warn(`ObservedPropertyAbstractPU[${this.id__()}, '${this.info() || "unknown"}']: notifyPropertryHasChangedPU: unknown subscriber ID 'subscribedId' error!`); 3329 } 3330 } 3331 }); 3332 } 3333 markDependentElementsDirty(view) { 3334 // TODO ace-ets2bundle, framework, complicated apps need to update together 3335 // this function will be removed after a short transition period. 3336 stateMgmtConsole.warn(`ObservedPropertyAbstractPU[${this.id__()}, '${this.info() || "unknown"}']: markDependentElementsDirty no longer supported. App will work ok, but 3337 please update your ace-ets2bundle and recompile your application!`); 3338 } 3339 numberOfSubscrbers() { 3340 return this.subscriberRefs_.size + (this.owningView_ ? 1 : 0); 3341 } 3342 /* 3343 type checking for any supported type, as required for union type support 3344 see 1st parameter for explanation what is allowed 3345 3346 FIXME this expects the Map, Set patch to go in 3347 */ 3348 checkIsSupportedValue(value) { 3349 return this.checkNewValue(`undefined, null, number, boolean, string, or Object but not function`, value, () => ((typeof value == "object" && typeof value != "function") 3350 || typeof value == "number" || typeof value == "string" || typeof value == "boolean") 3351 || (value == undefined || value == null)); 3352 } 3353 /* 3354 type checking for allowed Object type value 3355 see 1st parameter for explanation what is allowed 3356 3357 FIXME this expects the Map, Set patch to go in 3358 */ 3359 checkIsObject(value) { 3360 return this.checkNewValue(`undefined, null, Object including Array and instance of SubscribableAbstract and excluding function, Set, and Map`, value, () => (value == undefined || value == null || (typeof value == "object"))); 3361 } 3362 /* 3363 type checking for allowed simple types value 3364 see 1st parameter for explanation what is allowed 3365 */ 3366 checkIsSimple(value) { 3367 return this.checkNewValue(`undefined, number, boolean, string`, value, () => (value == undefined || typeof value == "number" || typeof value == "string" || typeof value == "boolean")); 3368 } 3369 checkNewValue(isAllowedComment, newValue, validator) { 3370 if (validator(newValue)) { 3371 return true; 3372 } 3373 // report error 3374 // current implementation throws an Exception 3375 errorReport.varValueCheckFailed({ 3376 customComponent: this.owningView_ ? this.owningView_.constructor.name : "unknown owningView / internal error", 3377 variableDeco: ObservedPropertyAbstractPU.mapDeco.get(this.constructor.name), 3378 variableName: this.info(), 3379 expectedType: isAllowedComment, 3380 value: newValue 3381 }); 3382 // never gets here if errorReport.varValueCheckFailed throws an exception 3383 // but should nto depend on its implementation 3384 return false; 3385 } 3386 /** 3387 * factory function for concrete 'object' or 'simple' ObservedProperty object 3388 * depending if value is Class object 3389 * or simple type (boolean | number | string) 3390 * @param value 3391 * @param owningView 3392 * @param thisPropertyName 3393 * @returns either 3394 */ 3395 static CreateObservedObject(value, owningView, thisPropertyName) { 3396 return (typeof value === "object") ? 3397 new ObservedPropertyObject(value, owningView, thisPropertyName) 3398 : new ObservedPropertySimple(value, owningView, thisPropertyName); 3399 } 3400 /** 3401 * during 'get' access recording take note of the created component and its elmtId 3402 * and add this component to the list of components who are dependent on this property 3403 */ 3404 recordDependentUpdate() { 3405 const elmtId = ViewStackProcessor.GetElmtIdToAccountFor(); 3406 if (elmtId < 0) { 3407 // not access recording 3408 return; 3409 } 3410 3411 this.dependentElementIds_.add(elmtId); 3412 } 3413 purgeDependencyOnElmtId(rmElmtId) { 3414 3415 this.dependentElementIds_.delete(rmElmtId); 3416 } 3417 SetPropertyUnchanged() { 3418 // function to be removed 3419 // keep it here until transpiler is updated. 3420 } 3421 // FIXME check, is this used from AppStorage. 3422 // unified Appstorage, what classes to use, and the API 3423 createLink(subscribeOwner, linkPropName) { 3424 throw new Error("Can not create a AppStorage 'Link' from a @State property. "); 3425 } 3426 createProp(subscribeOwner, linkPropName) { 3427 throw new Error("Can not create a AppStorage 'Prop' from a @State property. "); 3428 } 3429 /* 3430 Below empty functions required to keep as long as this class derives from FU version 3431 ObservedPropertyAbstract. Need to overwrite these functions to do nothing for PU 3432 */ 3433 notifyHasChanged(_) { 3434 stateMgmtConsole.error(`ObservedPropertyAbstractPU[${this.id__()}, '${this.info() || "unknown"}']: \ 3435 notifyHasChanged, DO NOT USE with PU. Use syncPeerHasChanged() or objectPropertyHasChangedPU()`); 3436 } 3437 hasChanged(_) { 3438 // unused for PU 3439 // need to overwrite impl of base class with empty function. 3440 } 3441 propertyHasChanged(_) { 3442 // unused for PU 3443 // need to overwrite impl of base class with empty function. 3444 } 3445 propertyRead(_) { 3446 // unused for PU 3447 // need to overwrite impl of base class with empty function. 3448 } 3449} 3450ObservedPropertyAbstractPU.DelayedNotifyChangesEnum = (_a = class { 3451 }, 3452 _a.do_not_delay = 0, 3453 _a.delay_none_pending = 1, 3454 _a.delay_notification_pending = 2, 3455 _a); 3456ObservedPropertyAbstractPU.mapDeco = new Map([ 3457 ["ObservedPropertyObjectPU", "@State/@Provide"], 3458 ["ObservedPropertySimplePU", "@State/@Provide (error, should not be used)"], 3459 ["SynchedPropertyObjectOneWayPU", "@Prop"], 3460 ["SynchedPropertySimpleOneWayPU", "@Prop (error, should not be used)"], 3461 ["SynchedPropertyObjectTwoWayPU", "@Link/@Consume"], 3462 ["SynchedPropertySimpleTwoWayPU", "@Link/@Consume (error, should not be used)"], 3463 ["SynchedPropertyNestedObjectPU", "@ObjectLink (only class-objects supported"], 3464 ["SynchedPropertyNesedObjectPU", "@ObjectLink (only class-objects supported"] 3465]); 3466/* 3467 * Copyright (c) 2022 Huawei Device Co., Ltd. 3468 * Licensed under the Apache License, Version 2.0 (the "License"); 3469 * you may not use this file except in compliance with the License. 3470 * You may obtain a copy of the License at 3471 * 3472 * http://www.apache.org/licenses/LICENSE-2.0 3473 * 3474 * Unless required by applicable law or agreed to in writing, software 3475 * distributed under the License is distributed on an "AS IS" BASIS, 3476 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3477 * See the License for the specific language governing permissions and 3478 * limitations under the License. 3479 */ 3480/** 3481 * ObservedPropertyObjectPU 3482 * implementation of @State and @Provide decorated variables of type class object 3483 * 3484 * all definitions in this file are framework internal 3485 * 3486 * class that holds an actual property value of type T 3487 * uses its base class to manage subscribers to this 3488 * property. 3489*/ 3490class ObservedPropertyPU extends ObservedPropertyAbstractPU { 3491 constructor(localInitValue, owningView, propertyName) { 3492 super(owningView, propertyName); 3493 this.setValueInternal(localInitValue); 3494 } 3495 aboutToBeDeleted(unsubscribeMe) { 3496 this.unsubscribeWrappedObject(); 3497 this.removeSubscriber(unsubscribeMe); 3498 super.aboutToBeDeleted(); 3499 } 3500 /** 3501 * Called by a SynchedPropertyObjectTwoWayPU (@Link, @Consume) that uses this as sync peer when it has changed 3502 * @param eventSource 3503 */ 3504 syncPeerHasChanged(eventSource) { 3505 3506 this.notifyPropertyHasChangedPU(); 3507 } 3508 /** 3509 * Wraped ObservedObjectPU has changed 3510 * @param souceObject 3511 * @param changedPropertyName 3512 */ 3513 objectPropertyHasChangedPU(souceObject, changedPropertyName) { 3514 3515 this.notifyPropertyHasChangedPU(); 3516 } 3517 objectPropertyHasBeenReadPU(souceObject, changedPropertyName) { 3518 3519 this.notifyPropertyHasBeenReadPU(); 3520 } 3521 unsubscribeWrappedObject() { 3522 if (this.wrappedValue_) { 3523 if (this.wrappedValue_ instanceof SubscribableAbstract) { 3524 this.wrappedValue_.removeOwningProperty(this); 3525 } 3526 else { 3527 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 3528 } 3529 } 3530 } 3531 /* 3532 actually update this.wrappedValue_ 3533 called needs to do value change check 3534 and also notify with this.aboutToChange(); 3535 */ 3536 setValueInternal(newValue) { 3537 if (newValue === this.wrappedValue_) { 3538 3539 return false; 3540 } 3541 if (!this.checkIsSupportedValue(newValue)) { 3542 return false; 3543 } 3544 this.unsubscribeWrappedObject(); 3545 if (!newValue || typeof newValue !== 'object') { 3546 // undefined, null, simple type: 3547 // nothing to subscribe to in case of new value undefined || null || simple type 3548 this.wrappedValue_ = newValue; 3549 } 3550 else if (newValue instanceof SubscribableAbstract) { 3551 3552 this.wrappedValue_ = newValue; 3553 this.wrappedValue_.addOwningProperty(this); 3554 } 3555 else if (ObservedObject.IsObservedObject(newValue)) { 3556 3557 ObservedObject.addOwningProperty(newValue, this); 3558 this.wrappedValue_ = newValue; 3559 } 3560 else { 3561 3562 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 3563 } 3564 return true; 3565 } 3566 get() { 3567 3568 this.notifyPropertyHasBeenReadPU(); 3569 return this.wrappedValue_; 3570 } 3571 getUnmonitored() { 3572 3573 // unmonitored get access , no call to notifyPropertyRead ! 3574 return this.wrappedValue_; 3575 } 3576 set(newValue) { 3577 if (this.wrappedValue_ === newValue) { 3578 3579 return; 3580 } 3581 3582 if (this.setValueInternal(newValue)) { 3583 this.notifyPropertyHasChangedPU(); 3584 } 3585 } 3586} 3587// class definitions for backward compatibility 3588class ObservedPropertyObjectPU extends ObservedPropertyPU { 3589} 3590class ObservedPropertySimplePU extends ObservedPropertyPU { 3591} 3592/* 3593 * Copyright (c) 2022 Huawei Device Co., Ltd. 3594 * Licensed under the Apache License, Version 2.0 (the "License"); 3595 * you may not use this file except in compliance with the License. 3596 * You may obtain a copy of the License at 3597 * 3598 * http://www.apache.org/licenses/LICENSE-2.0 3599 * 3600 * Unless required by applicable law or agreed to in writing, software 3601 * distributed under the License is distributed on an "AS IS" BASIS, 3602 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3603 * See the License for the specific language governing permissions and 3604 * limitations under the License. 3605 */ 3606/** 3607 * SynchedPropertyObjectOneWayPU 3608 * implementation of @Prop decorated variables of type class object 3609 * 3610 * all definitions in this file are framework internal 3611 * 3612 */ 3613/** 3614 * Initialisation scenarios: 3615 * ------------------------- 3616 * 3617 * 1 - no local initialization, source provided (its ObservedObject value) 3618 * wrap the ObservedObject into an ObservedPropertyObjectPU 3619 * deep copy the ObservedObject into localCopyObservedObject_ 3620 * 3621 * 2 - local initialization, no source provided 3622 * app transpiled code calls set 3623 * leave source_ undefined 3624 * no deep copy needed, but provided local init might need wrapping inside an ObservedObject to set to 3625 * localCopyObservedObject_ 3626 * 3627 * 3 local initialization, source provided (its ObservedObject value) 3628 * current app transpiled code is not optional 3629 * sets source in constructor, as in case 1 3630 * calls set() to set the source value, but this will not deepcopy 3631 * 3632 * Update scenarios: 3633 * ----------------- 3634 * 3635 * 1- assignment of a new Object value: this.aProp = new ClassA() 3636 * rhs can be ObservedObject because of @Observed decoration or now 3637 * notifyPropertyHasChangedPU 3638 * 3639 * 2- local ObservedObject member property change 3640 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored in localCopyObservedObject_ 3641 * no need to copy, notifyPropertyHasChangedPU 3642 * 3643 * 3- Rerender of the custom component triggered from the parent 3644 * reset() is called (code generated by the transpiler), set the value of source_ , if that causes a change will call syncPeerHasChanged 3645 * syncPeerHasChanged need to deep copy the ObservedObject from source to localCopyObservedObject_ 3646 * notifyPropertyHasChangedPU 3647 * 3648 * 4- source_ ObservedObject member property change 3649 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored source_.getUnmonitored 3650 * notifyPropertyHasChangedPU 3651 */ 3652class SynchedPropertyOneWayPU extends ObservedPropertyAbstractPU { 3653 constructor(source, owningChildView, thisPropertyName) { 3654 super(owningChildView, thisPropertyName); 3655 if (source && (typeof (source) === "object") && ("subscribeMe" in source)) { 3656 // code path for @(Local)StorageProp, the source is a ObservedPropertyObject<C> in a LocalStorage) 3657 this.source_ = source; 3658 this.sourceIsOwnObject = false; 3659 // subscribe to receive value change updates from LocalStorage source property 3660 this.source_.addSubscriber(this); 3661 } 3662 else { 3663 const sourceValue = source; 3664 if (this.checkIsSupportedValue(sourceValue)) { 3665 // code path for 3666 // 1- source is of same type C in parent, source is its value, not the backing store ObservedPropertyObject 3667 // 2- nested Object/Array inside observed another object/array in parent, source is its value 3668 if (!((sourceValue instanceof SubscribableAbstract) || ObservedObject.IsObservedObject(sourceValue))) { 3669 stateMgmtConsole.warn(`@Prop ${this.info()} Provided source object's class is not instance of SubscribableAbstract, 3670 it also lacks @Observed class decorator. Object property changes will not be observed.`); 3671 } 3672 3673 this.source_ = new ObservedPropertyObjectPU(sourceValue, this, this.getSourceObservedPropertyFakeName()); 3674 this.sourceIsOwnObject = true; 3675 } 3676 } 3677 if (this.source_ != undefined) { 3678 this.resetLocalValue(this.source_.get(), /* needCopyObject */ true); 3679 } 3680 3681 } 3682 /* 3683 like a destructor, need to call this before deleting 3684 the property. 3685 */ 3686 aboutToBeDeleted() { 3687 if (this.source_) { 3688 this.source_.removeSubscriber(this); 3689 if (this.sourceIsOwnObject == true && this.source_.numberOfSubscrbers() == 0) { 3690 3691 this.source_.aboutToBeDeleted(); 3692 } 3693 this.source_ = undefined; 3694 } 3695 super.aboutToBeDeleted(); 3696 } 3697 getSourceObservedPropertyFakeName() { 3698 return `${this.info()}_source`; 3699 } 3700 syncPeerHasChanged(eventSource) { 3701 if (this.source_ == undefined) { 3702 stateMgmtConsole.error(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: \ 3703 @Prop syncPeerHasChanged peer '${eventSource ? eventSource.info() : "no eventSource info"}' but source_ undefned. Internal error.`); 3704 return; 3705 } 3706 if (eventSource && this.source_ == eventSource) { 3707 // defensive programming: should always be the case! 3708 3709 const newValue = this.source_.getUnmonitored(); 3710 if (this.checkIsSupportedValue(newValue)) { 3711 3712 if (this.resetLocalValue(newValue, /* needCopyObject */ true)) { 3713 this.notifyPropertyHasChangedPU(); 3714 } 3715 } 3716 } 3717 else { 3718 stateMgmtConsole.warn(`SynchedPropertyObjectOneWayPU[${this.id__()}]: syncPeerHasChanged Unexpected situation. syncPeerHasChanged from different sender than source_. Ignoring event.`); 3719 } 3720 } 3721 /** 3722 * event emited by wrapped ObservedObject, when one of its property values changes 3723 * @param souceObject 3724 * @param changedPropertyName 3725 */ 3726 objectPropertyHasChangedPU(sourceObject, changedPropertyName) { 3727 3728 this.notifyPropertyHasChangedPU(); 3729 } 3730 objectPropertyHasBeenReadPU(sourceObject, changedPropertyName) { 3731 3732 this.notifyPropertyHasBeenReadPU(); 3733 } 3734 getUnmonitored() { 3735 3736 // unmonitored get access , no call to notifyPropertyRead ! 3737 return this.localCopyObservedObject_; 3738 } 3739 get() { 3740 3741 this.notifyPropertyHasBeenReadPU(); 3742 return this.localCopyObservedObject_; 3743 } 3744 // assignment to local variable in the form of this.aProp = <object value> 3745 // set 'writes through` to the ObservedObject 3746 set(newValue) { 3747 if (this.localCopyObservedObject_ === newValue) { 3748 3749 return; 3750 } 3751 3752 if (this.resetLocalValue(newValue, /* needCopyObject */ false)) { 3753 this.notifyPropertyHasChangedPU(); 3754 } 3755 } 3756 // called when updated from parent 3757 reset(sourceChangedValue) { 3758 3759 if (this.source_ !== undefined && this.checkIsSupportedValue(sourceChangedValue)) { 3760 // if this.source_.set causes an actual change, then, ObservedPropertyObject source_ will call syncPeerHasChanged method 3761 this.source_.set(sourceChangedValue); 3762 } 3763 } 3764 /* 3765 unsubscribe from previous wrapped ObjectObject 3766 take a shallow or (TODO) deep copy 3767 copied Object might already be an ObservedObject (e.g. becurse of @Observed decorator) or might be raw 3768 Therefore, conditionally wrap the object, then subscribe 3769 return value true only if localCopyObservedObject_ has been changed 3770 */ 3771 resetLocalValue(newObservedObjectValue, needCopyObject) { 3772 // note: We can not test for newObservedObjectValue == this.localCopyObservedObject_ 3773 // here because the object might still be the same, but some property of it has changed 3774 if (!this.checkIsSupportedValue(newObservedObjectValue)) { 3775 return; 3776 } 3777 // unsubscribe from old local copy 3778 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 3779 this.localCopyObservedObject_.removeOwningProperty(this); 3780 } 3781 else { 3782 ObservedObject.removeOwningProperty(this.localCopyObservedObject_, this); 3783 } 3784 // shallow/deep copy value 3785 // needed whenever newObservedObjectValue comes from source 3786 // not needed on a local set (aka when called from set() method) 3787 this.localCopyObservedObject_ = needCopyObject ? this.copyObject(newObservedObjectValue, this.info_) : newObservedObjectValue; 3788 if (typeof this.localCopyObservedObject_ == "object") { 3789 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 3790 // deep copy will copy Set of subscribers as well. But local copy only has its own subscribers 3791 // not those of its parent value. 3792 this.localCopyObservedObject_.clearOwningProperties(); 3793 this.localCopyObservedObject_.addOwningProperty(this); 3794 } 3795 else if (ObservedObject.IsObservedObject(this.localCopyObservedObject_)) { 3796 // case: new ObservedObject 3797 ObservedObject.addOwningProperty(this.localCopyObservedObject_, this); 3798 } 3799 else { 3800 // wrap newObservedObjectValue raw object as ObservedObject and subscribe to it 3801 stateMgmtConsole.error(`@Prop ${this.info()} Provided source object's class \ 3802 lacks @Observed class decorator. Object property changes will not be observed.`); 3803 this.localCopyObservedObject_ = ObservedObject.createNew(this.localCopyObservedObject_, this); 3804 } 3805 } 3806 return true; 3807 } 3808 copyObject(value, propName) { 3809 // ViewStackProcessor.getApiVersion function is not present in API9 3810 // therefore shallowCopyObject will always be used in API version 9 and before 3811 // but the code in this file is the same regardless of API version 3812 3813 return ((typeof ViewStackProcessor["getApiVersion"] == "function") && 3814 (ViewStackProcessor["getApiVersion"]() >= 10)) 3815 ? this.deepCopyObject(value, propName) 3816 : this.shallowCopyObject(value, propName); 3817 } 3818 // API 9 code path 3819 shallowCopyObject(value, propName) { 3820 let rawValue = ObservedObject.GetRawObject(value); 3821 let copy; 3822 if (!rawValue || typeof rawValue !== 'object') { 3823 copy = rawValue; 3824 } 3825 else if (typeof rawValue != "object") { 3826 // FIXME would it be better to throw Exception here? 3827 stateMgmtConsole.error(`@Prop ${this.info()} shallowCopyObject: request to copy non-object but defined value of type '${typeof rawValue}'. Internal error! Setting copy=original value.`); 3828 copy = rawValue; 3829 } 3830 else if (rawValue instanceof Array) { 3831 // case Array inside ObservedObject 3832 copy = ObservedObject.createNew([...rawValue], this); 3833 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 3834 } 3835 else if (rawValue instanceof Date) { 3836 // case Date inside ObservedObject 3837 let d = new Date(); 3838 d.setTime(rawValue.getTime()); 3839 // subscribe, also Date gets wrapped / proxied by ObservedObject 3840 copy = ObservedObject.createNew(d, this); 3841 } 3842 else if (rawValue instanceof SubscribableAbstract) { 3843 // case SubscribableAbstract, no wrapping inside ObservedObject 3844 copy = Object.assign({}, rawValue); 3845 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 3846 if (copy instanceof SubscribableAbstract) { 3847 // subscribe 3848 copy.addOwningProperty(this); 3849 } 3850 } 3851 else if (typeof rawValue == "object") { 3852 // case Object that is not Array, not Date, not SubscribableAbstract 3853 copy = ObservedObject.createNew(Object.assign({}, rawValue), this); 3854 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 3855 } 3856 else { 3857 // TODO in PR "F": change to exception throwing: 3858 stateMgmtConsole.error(`@Prop ${this.info()} shallow failed. Attempt to copy unsupported value type '${typeof rawValue}' .`); 3859 copy = rawValue; 3860 } 3861 return copy; 3862 } 3863 // API 10 code path 3864 deepCopyObject(obj, variable) { 3865 let copy = SynchedPropertyObjectOneWayPU.deepCopyObjectInternal(obj, variable); 3866 // this subscribe to the top level object/array of the copy 3867 // same as shallowCopy does 3868 if ((obj instanceof SubscribableAbstract) && 3869 (copy instanceof SubscribableAbstract)) { 3870 copy.addOwningProperty(this); 3871 } 3872 else if (ObservedObject.IsObservedObject(obj) && ObservedObject.IsObservedObject(copy)) { 3873 ObservedObject.addOwningProperty(copy, this); 3874 } 3875 return copy; 3876 ; 3877 } 3878 // do not use this function from outside unless it is for testing purposes. 3879 static deepCopyObjectInternal(obj, variable) { 3880 if (!obj || typeof obj !== 'object') { 3881 return obj; 3882 } 3883 let stack = new Array(); 3884 let copiedObjects = new Map(); 3885 return getDeepCopyOfObjectRecursive(obj); 3886 function getDeepCopyOfObjectRecursive(obj) { 3887 if (!obj || typeof obj !== 'object') { 3888 return obj; 3889 } 3890 const alreadyCopiedObject = copiedObjects.get(obj); 3891 if (alreadyCopiedObject) { 3892 let msg = `@Prop deepCopyObject: Found reference to already copied object: Path ${variable ? variable : 'unknown variable'}`; 3893 stack.forEach(stackItem => msg += ` - ${stackItem.name}`); 3894 3895 return alreadyCopiedObject; 3896 } 3897 let copy; 3898 if (obj instanceof Set) { 3899 copy = new Set(); 3900 for (const setKey of obj.keys()) { 3901 stack.push({ name: setKey }); 3902 copiedObjects.set(obj, copy); 3903 copy.add(getDeepCopyOfObjectRecursive(setKey)); 3904 stack.pop(); 3905 } 3906 } 3907 else if (obj instanceof Map) { 3908 copy = new Map(); 3909 for (const mapKey of obj.keys()) { 3910 stack.push({ name: mapKey }); 3911 copiedObjects.set(obj, copy); 3912 copy.set(mapKey, getDeepCopyOfObjectRecursive(obj.get(mapKey))); 3913 stack.pop(); 3914 } 3915 } 3916 else if (obj instanceof Date) { 3917 copy = new Date(); 3918 copy.setTime(obj.getTime()); 3919 } 3920 else if (obj instanceof Object) { 3921 copy = Array.isArray(obj) ? [] : {}; 3922 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 3923 for (const objKey of Object.keys(obj)) { 3924 stack.push({ name: objKey }); 3925 copiedObjects.set(obj, copy); 3926 Reflect.set(copy, objKey, getDeepCopyOfObjectRecursive(obj[objKey])); 3927 stack.pop(); 3928 } 3929 } 3930 return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, null) : copy; 3931 } 3932 } 3933} 3934// class definitions for backward compatibility 3935class SynchedPropertySimpleOneWayPU extends SynchedPropertyOneWayPU { 3936} 3937class SynchedPropertyObjectOneWayPU extends SynchedPropertyOneWayPU { 3938} 3939/* 3940 * Copyright (c) 2022 Huawei Device Co., Ltd. 3941 * Licensed under the Apache License, Version 2.0 (the "License"); 3942 * you may not use this file except in compliance with the License. 3943 * You may obtain a copy of the License at 3944 * 3945 * http://www.apache.org/licenses/LICENSE-2.0 3946 * 3947 * Unless required by applicable law or agreed to in writing, software 3948 * distributed under the License is distributed on an "AS IS" BASIS, 3949 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3950 * See the License for the specific language governing permissions and 3951 * limitations under the License. 3952 */ 3953/** 3954 * SynchedPropertyObjectTwoWayPU 3955 * implementation of @Link and @Consume decorated variables of type class object 3956 * 3957 * all definitions in this file are framework internal 3958*/ 3959class SynchedPropertyTwoWayPU extends ObservedPropertyAbstractPU { 3960 constructor(source, owningChildView, thisPropertyName) { 3961 super(owningChildView, thisPropertyName); 3962 this.changeNotificationIsOngoing_ = false; 3963 this.source_ = source; 3964 if (this.source_) { 3965 // register to the parent property 3966 this.source_.addSubscriber(this); 3967 } 3968 else { 3969 throw new SyntaxError(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: constructor @Link/@Consume source variable in parent/ancestor @ Component must be defined. Application error!`); 3970 } 3971 } 3972 /* 3973 like a destructor, need to call this before deleting 3974 the property. 3975 */ 3976 aboutToBeDeleted() { 3977 // unregister from parent of this link 3978 if (this.source_) { 3979 this.source_.removeSubscriber(this); 3980 // unregister from the ObservedObject 3981 ObservedObject.removeOwningProperty(this.source_.getUnmonitored(), this); 3982 } 3983 super.aboutToBeDeleted(); 3984 } 3985 isStorageLinkProp() { 3986 return (this.source_ && this.source_ instanceof ObservedPropertyAbstract && (!(this.source_ instanceof ObservedPropertyAbstractPU))); 3987 } 3988 setObject(newValue) { 3989 if (!this.source_) { 3990 throw new SyntaxError(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: setObject (assign a new value), \ 3991 @Link/@Consume: no source variable in parent/ancestor @Component. Application error.`); 3992 } 3993 if (this.getUnmonitored() === newValue) { 3994 3995 return; 3996 } 3997 3998 if (this.checkIsSupportedValue(newValue)) { 3999 // the source_ ObservedProperty will call: this.syncPeerHasChanged(newValue); 4000 this.source_.set(newValue); 4001 } 4002 } 4003 /** 4004 * Called when sync peer ObservedPropertyObject or SynchedPropertyObjectTwoWay has changed value 4005 * that peer can be in either parent or child component if 'this' is used for a @Link 4006 * that peer can be in either ancestor or descendant component if 'this' is used for a @Consume 4007 * @param eventSource 4008 */ 4009 syncPeerHasChanged(eventSource) { 4010 if (!this.changeNotificationIsOngoing_) { 4011 4012 this.notifyPropertyHasChangedPU(); 4013 } 4014 } 4015 /** 4016 * called when wrapped ObservedObject has changed poperty 4017 * @param souceObject 4018 * @param changedPropertyName 4019 */ 4020 objectPropertyHasChangedPU(sourceObject, changedPropertyName) { 4021 4022 this.notifyPropertyHasChangedPU(); 4023 } 4024 objectPropertyHasBeenReadPU(sourceObject, changedPropertyName) { 4025 4026 this.notifyPropertyHasBeenReadPU(); 4027 } 4028 getUnmonitored() { 4029 4030 // unmonitored get access , no call to otifyPropertyRead ! 4031 return (this.source_ ? this.source_.getUnmonitored() : undefined); 4032 } 4033 // get 'read through` from the ObservedProperty 4034 get() { 4035 4036 this.notifyPropertyHasBeenReadPU(); 4037 return this.getUnmonitored(); 4038 } 4039 // set 'writes through` to the ObservedProperty 4040 set(newValue) { 4041 if (this.getUnmonitored() === newValue) { 4042 4043 return; 4044 } 4045 4046 // avoid circular notifications @Link -> source @State -> other but also back to same @Link 4047 this.changeNotificationIsOngoing_ = true; 4048 this.setObject(newValue); 4049 this.notifyPropertyHasChangedPU(); 4050 this.changeNotificationIsOngoing_ = false; 4051 } 4052} 4053// class definitions for backward compatibility 4054class SynchedPropertyObjectTwoWayPU extends SynchedPropertyTwoWayPU { 4055} 4056class SynchedPropertySimpleTwoWayPU extends SynchedPropertyTwoWayPU { 4057} 4058/* 4059 * Copyright (c) 2022 Huawei Device Co., Ltd. 4060 * Licensed under the Apache License, Version 2.0 (the "License"); 4061 * you may not use this file except in compliance with the License. 4062 * You may obtain a copy of the License at 4063 * 4064 * http://www.apache.org/licenses/LICENSE-2.0 4065 * 4066 * Unless required by applicable law or agreed to in writing, software 4067 * distributed under the License is distributed on an "AS IS" BASIS, 4068 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4069 * See the License for the specific language governing permissions and 4070 * limitations under the License. 4071 */ 4072/** 4073 * SynchedPropertyNestedObjectPU 4074 * implementation of @ObjectLink decorated variables 4075 * 4076 * all definitions in this file are framework internal 4077 * 4078 */ 4079class SynchedPropertyNestedObjectPU extends ObservedPropertyAbstractPU { 4080 /** 4081 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 4082 * example 4083 * this.b.$a with b of type PC and a of type C, or 4084 * this.$b[5] with this.b of type PC and array item b[5] of type C; 4085 * 4086 * @param subscribeMe 4087 * @param propName 4088 */ 4089 constructor(obsObject, owningChildView, propertyName) { 4090 super(owningChildView, propertyName); 4091 this.obsObject_ = obsObject; 4092 this.setValueInternal(obsObject); 4093 } 4094 /* 4095 like a destructor, need to call this before deleting 4096 the property. 4097 */ 4098 aboutToBeDeleted() { 4099 // unregister from the ObservedObject 4100 ObservedObject.removeOwningProperty(this.obsObject_, this); 4101 super.aboutToBeDeleted(); 4102 } 4103 objectPropertyHasChangedPU(eventSource, changedPropertyName) { 4104 4105 this.notifyPropertyHasChangedPU(); 4106 } 4107 objectPropertyHasBeenReadPU(sourceObject, changedPropertyName) { 4108 4109 this.notifyPropertyHasBeenReadPU(); 4110 } 4111 getUnmonitored() { 4112 4113 // unmonitored get access , no call to notifyPropertyRead ! 4114 return this.obsObject_; 4115 } 4116 // get 'read through` from the ObservedProperty 4117 get() { 4118 4119 this.notifyPropertyHasBeenReadPU(); 4120 return this.obsObject_; 4121 } 4122 // set 'writes through` to the ObservedProperty 4123 set(newValue) { 4124 if (this.obsObject_ === newValue) { 4125 4126 return; 4127 } 4128 4129 if (this.setValueInternal(newValue)) { 4130 // notify value change to subscribing View 4131 this.notifyPropertyHasChangedPU(); 4132 } 4133 } 4134 setValueInternal(newValue) { 4135 if (!this.checkIsObject(newValue)) { 4136 return false; 4137 } 4138 if (this.obsObject_ != undefined) { 4139 if (this.obsObject_ instanceof SubscribableAbstract) { 4140 // unregister from SubscribableAbstract object 4141 this.obsObject_.removeOwningProperty(this); 4142 } 4143 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 4144 // unregister from the ObservedObject 4145 ObservedObject.removeOwningProperty(this.obsObject_, this); 4146 } 4147 } 4148 this.obsObject_ = newValue; 4149 if (this.obsObject_ != undefined) { 4150 if (this.obsObject_ instanceof SubscribableAbstract) { 4151 // register to SubscribableAbstract object 4152 this.obsObject_.addOwningProperty(this); 4153 } 4154 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 4155 // register to the ObservedObject 4156 ObservedObject.addOwningProperty(this.obsObject_, this); 4157 } 4158 else { 4159 stateMgmtConsole.error(`SynchedPropertyNestedObjectPU[${this.id__()}, '${this.info() || "unknown"}']: set/init: @ObjectLink value is neither ObservedObject nor SubscribableAbstract. \ 4160 value changes will bot be observed and UI will not update. forgot @Observed class decorator? Application error.`); 4161 } 4162 } 4163 return true; 4164 } 4165} 4166/** backward compatibility after typo in classname fix */ 4167class SynchedPropertyNesedObjectPU extends SynchedPropertyNestedObjectPU { 4168} 4169/* 4170 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 4171 * Licensed under the Apache License, Version 2.0 (the "License"); 4172 * you may not use this file except in compliance with the License. 4173 * You may obtain a copy of the License at 4174 * 4175 * http://www.apache.org/licenses/LICENSE-2.0 4176 * 4177 * Unless required by applicable law or agreed to in writing, software 4178 * distributed under the License is distributed on an "AS IS" BASIS, 4179 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4180 * See the License for the specific language governing permissions and 4181 * limitations under the License. 4182 * 4183 * * ViewPU - View for Partial Update 4184 * 4185* all definitions in this file are framework internal 4186*/ 4187// denotes a missing elemntId, this is the case during initial render 4188const UndefinedElmtId = -1; 4189// NativeView 4190// implemented in C++ for release 4191// and in utest/view_native_mock.ts for testing 4192class ViewPU extends NativeViewPartialUpdate { 4193 get localStorage_() { 4194 if (!this.localStoragebackStore_ && this.parent_) { 4195 4196 this.localStoragebackStore_ = this.parent_.localStorage_; 4197 } 4198 if (!this.localStoragebackStore_) { 4199 4200 this.localStoragebackStore_ = new LocalStorage({ /* empty */}); 4201 } 4202 return this.localStoragebackStore_; 4203 } 4204 set localStorage_(instance) { 4205 if (!instance) { 4206 // setting to undefined not allowed 4207 return; 4208 } 4209 if (this.localStoragebackStore_) { 4210 stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); 4211 } 4212 this.localStoragebackStore_ = instance; 4213 } 4214 /** 4215 * Create a View 4216 * 4217 * 1. option: top level View, specify 4218 * - compilerAssignedUniqueChildId must specify 4219 * - parent=undefined 4220 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 4221 * in this View or descendant Views. 4222 * 4223 * 2. option: not a top level View 4224 * - compilerAssignedUniqueChildId must specify 4225 * - parent must specify 4226 * - localStorage do not specify, will inherit from parent View. 4227 * 4228 */ 4229 constructor(parent, localStorage, elmtId = -1) { 4230 super(); 4231 this.parent_ = undefined; 4232 this.childrenWeakrefMap_ = new Map(); 4233 // flag for initgial rendering or re-render on-going. 4234 this.isRenderInProgress = false; 4235 // flag if active of inActive 4236 // inActive means updates are delayed 4237 this.isActive_ = true; 4238 // flag if {aboutToBeDeletedInternal} is called and the instance of ViewPU has not been GC. 4239 this.isDeleting_ = false; 4240 this.watchedProps = new Map(); 4241 this.recycleManager = undefined; 4242 // Set of dependent elmtIds that need partial update 4243 // during next re-render 4244 this.dirtDescendantElementIds_ = new Set(); 4245 // registry of update functions 4246 // the key is the elementId of the Component/Element that's the result of this function 4247 this.updateFuncByElmtId = new Map(); 4248 // set of all @Local/StorageLink/Prop variables owned by this ViwPU 4249 this.ownStorageLinksProps_ = new Set(); 4250 // my LocalStorage instance, shared with ancestor Views. 4251 // create a default instance on demand if none is initialized 4252 this.localStoragebackStore_ = undefined; 4253 // if set use the elmtId also as the ViewPU object's subscribable id. 4254 // these matching is requiremrnt for updateChildViewById(elmtId) being able to 4255 // find the child ViewPU object by given elmtId 4256 this.id_ = elmtId == -1 ? SubscriberManager.MakeId() : elmtId; 4257 this.providedVars_ = parent ? new Map(parent.providedVars_) 4258 : new Map(); 4259 this.localStoragebackStore_ = undefined; 4260 if (parent) { 4261 // this View is not a top-level View 4262 this.setCardId(parent.getCardId()); 4263 // Call below will set this.parent_ to parent as well 4264 parent.addChild(this); 4265 } 4266 else if (localStorage) { 4267 this.localStorage_ = localStorage; 4268 4269 } 4270 SubscriberManager.Add(this); 4271 4272 } 4273 // globally unique id, this is different from compilerAssignedUniqueChildId! 4274 id__() { 4275 return this.id_; 4276 } 4277 updateId(elmtId) { 4278 this.id_ = elmtId; 4279 } 4280 // super class will call this function from 4281 // its aboutToBeDeleted implementation 4282 aboutToBeDeletedInternal() { 4283 // When a custom component is deleted, need to notify the C++ side to clean the corresponding deletion cache Map, 4284 // because after the deletion, can no longer clean the RemoveIds cache on the C++ side through the 4285 // updateDirtyElements function. 4286 let removedElmtIds = []; 4287 this.updateFuncByElmtId.forEach((value, key) => { 4288 this.purgeVariableDependenciesOnElmtId(key); 4289 removedElmtIds.push(key); 4290 }); 4291 this.deletedElmtIdsHaveBeenPurged(removedElmtIds); 4292 if (this.hasRecycleManager()) { 4293 this.getRecycleManager().purgeAllCachedRecycleNode(); 4294 } 4295 this.updateFuncByElmtId.clear(); 4296 this.watchedProps.clear(); 4297 this.providedVars_.clear(); 4298 this.ownStorageLinksProps_.clear(); 4299 if (this.parent_) { 4300 this.parent_.removeChild(this); 4301 } 4302 this.localStoragebackStore_ = undefined; 4303 this.isDeleting_ = true; 4304 } 4305 /** 4306 * ArkUI engine will call this function when the corresponding CustomNode's active status change. 4307 * @param active true for active, false for inactive 4308 */ 4309 setActiveInternal(active) { 4310 if (this.isActive_ == active) { 4311 4312 return; 4313 } 4314 4315 this.isActive_ = active; 4316 if (this.isActive_) { 4317 this.onActiveInternal(); 4318 } 4319 else { 4320 this.onInactiveInternal(); 4321 } 4322 } 4323 onActiveInternal() { 4324 if (!this.isActive_) { 4325 return; 4326 } 4327 4328 this.performDelayedUpdate(); 4329 for (const child of this.childrenWeakrefMap_.values()) { 4330 const childViewPU = child.deref(); 4331 if (childViewPU) { 4332 childViewPU.setActiveInternal(this.isActive_); 4333 } 4334 } 4335 } 4336 onInactiveInternal() { 4337 if (this.isActive_) { 4338 return; 4339 } 4340 4341 for (const storageProp of this.ownStorageLinksProps_) { 4342 storageProp.enableDelayedNotification(); 4343 } 4344 for (const child of this.childrenWeakrefMap_.values()) { 4345 const childViewPU = child.deref(); 4346 if (childViewPU) { 4347 childViewPU.setActiveInternal(this.isActive_); 4348 } 4349 } 4350 } 4351 setParent(parent) { 4352 if (this.parent_ && parent) { 4353 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).setChild: changing parent to '${parent.constructor.name}', id ${parent.id__()} (unsafe operation)`); 4354 } 4355 this.parent_ = parent; 4356 } 4357 /** 4358 * add given child and set 'this' as its parent 4359 * @param child child to add 4360 * @returns returns false if child with given child's id already exists 4361 * 4362 * framework internal function 4363 * Note: Use of WeakRef ensures child and parent do not generate a cycle dependency. 4364 * The add. Set<ids> is required to reliably tell what children still exist. 4365 */ 4366 addChild(child) { 4367 if (this.childrenWeakrefMap_.has(child.id__())) { 4368 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).addChild '${child.constructor.name}' id already exists ${child.id__()} !`); 4369 return false; 4370 } 4371 this.childrenWeakrefMap_.set(child.id__(), new WeakRef(child)); 4372 child.setParent(this); 4373 return true; 4374 } 4375 /** 4376 * remove given child and remove 'this' as its parent 4377 * @param child child to add 4378 * @returns returns false if child with given child's id does not exist 4379 */ 4380 removeChild(child) { 4381 const hasBeenDeleted = this.childrenWeakrefMap_.delete(child.id__()); 4382 if (!hasBeenDeleted) { 4383 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).removeChild '${child.constructor.name}', child id ${child.id__()} not known!`); 4384 } 4385 else { 4386 child.setParent(undefined); 4387 } 4388 return hasBeenDeleted; 4389 } 4390 /** 4391 * Retrieve child by given id 4392 * @param id 4393 * @returns child if in map and weak ref can still be downreferenced 4394 */ 4395 getChildById(id) { 4396 const childWeakRef = this.childrenWeakrefMap_.get(id); 4397 return childWeakRef ? childWeakRef.deref() : undefined; 4398 } 4399 updateStateVars(params) { 4400 stateMgmtConsole.warn("ViewPU.updateStateVars unimplemented. Pls upgrade to latest eDSL transpiler version."); 4401 } 4402 initialRenderView() { 4403 this.isRenderInProgress = true; 4404 this.initialRender(); 4405 this.isRenderInProgress = false; 4406 } 4407 UpdateElement(elmtId) { 4408 if (elmtId == this.id__()) { 4409 // do not attempt to update itself. 4410 // a @Prop can add a dependency of the ViewPU onto itself. Ignore it. 4411 return; 4412 } 4413 // do not process an Element that has been marked to be deleted 4414 const updateFunc = this.updateFuncByElmtId.get(elmtId); 4415 if ((updateFunc == undefined) || (typeof updateFunc !== "function")) { 4416 stateMgmtConsole.error(`${this.constructor.name}[${this.id__()}]: update function of ElementId ${elmtId} not found, internal error!`); 4417 } 4418 else { 4419 4420 this.isRenderInProgress = true; 4421 updateFunc(elmtId, /* isFirstRender */ false); 4422 // continue in native JSView 4423 // Finish the Update in JSView::JsFinishUpdateFunc 4424 // this function appends no longer used elmtIds (as recrded by VSP) to the given allRmElmtIds array 4425 this.finishUpdateFunc(elmtId); 4426 this.isRenderInProgress = false; 4427 4428 } 4429 } 4430 /** 4431 * force a complete rerender / update by executing all update functions 4432 * exec a regular rerender first 4433 * 4434 * @param deep recurse all children as well 4435 * 4436 * framework internal functions, apps must not call 4437 */ 4438 forceCompleteRerender(deep = false) { 4439 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).forceCompleteRerender - start.`); 4440 // request list of all (gloabbly) deleted elmtIds; 4441 let deletedElmtIds = []; 4442 this.getDeletedElemtIds(deletedElmtIds); 4443 // see which elmtIds are managed by this View 4444 // and clean up all book keeping for them 4445 this.purgeDeletedElmtIds(deletedElmtIds); 4446 Array.from(this.updateFuncByElmtId.keys()).sort(ViewPU.compareNumber).forEach(elmtId => this.UpdateElement(elmtId)); 4447 if (deep) { 4448 this.childrenWeakrefMap_.forEach((weakRefChild) => { 4449 const child = weakRefChild.deref(); 4450 if (child) { 4451 child.forceCompleteRerender(true); 4452 } 4453 }); 4454 } 4455 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).forceCompleteRerender - end`); 4456 } 4457 /** 4458 * force a complete rerender / update on specific node by executing update function. 4459 * 4460 * @param elmtId which node needs to update. 4461 * 4462 * framework internal functions, apps must not call 4463 */ 4464 forceRerenderNode(elmtId) { 4465 // request list of all (gloabbly) deleted elmtIds; 4466 let deletedElmtIds = []; 4467 this.getDeletedElemtIds(deletedElmtIds); 4468 // see which elmtIds are managed by this View 4469 // and clean up all book keeping for them 4470 this.purgeDeletedElmtIds(deletedElmtIds); 4471 this.UpdateElement(elmtId); 4472 // remove elemtId from dirtDescendantElementIds. 4473 this.dirtDescendantElementIds_.delete(elmtId); 4474 } 4475 updateStateVarsOfChildByElmtId(elmtId, params) { 4476 4477 if (elmtId < 0) { 4478 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).updateChildViewById(${elmtId}) - invalid elmtId - internal error!`); 4479 return; 4480 } 4481 let child = this.getChildById(elmtId); 4482 if (!child) { 4483 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).updateChildViewById(${elmtId}) - no child with this elmtId - internal error!`); 4484 return; 4485 } 4486 child.updateStateVars(params); 4487 4488 } 4489 // implements IMultiPropertiesChangeSubscriber 4490 viewPropertyHasChanged(varName, dependentElmtIds) { 4491 stateMgmtTrace.scopedTrace(() => { 4492 if (this.isRenderInProgress) { 4493 stateMgmtConsole.error(`@Component '${this.constructor.name}' (id: ${this.id__()}) State variable '${varName}' has changed during render! It's illegal to change @Component state while build (initial render or re-render) is on-going. Application error!`); 4494 } 4495 4496 this.syncInstanceId(); 4497 if (dependentElmtIds.size && !this.isFirstRender()) { 4498 if (!this.dirtDescendantElementIds_.size) { 4499 // mark ComposedElement dirty when first elmtIds are added 4500 // do not need to do this every time 4501 this.markNeedUpdate(); 4502 } 4503 4504 for (const elmtId of dependentElmtIds) { 4505 this.dirtDescendantElementIds_.add(elmtId); 4506 } 4507 4508 } 4509 let cb = this.watchedProps.get(varName); 4510 if (cb) { 4511 4512 cb.call(this, varName); 4513 } 4514 this.restoreInstanceId(); 4515 }, "ViewPU.viewPropertyHasChanged", this.constructor.name, varName, dependentElmtIds.size); 4516 } 4517 performDelayedUpdate() { 4518 stateMgmtTrace.scopedTrace(() => { 4519 4520 this.syncInstanceId(); 4521 for (const storageProp of this.ownStorageLinksProps_) { 4522 const changedElmtIds = storageProp.moveElmtIdsForDelayedUpdate(); 4523 if (changedElmtIds) { 4524 const varName = storageProp.info(); 4525 if (changedElmtIds.size && !this.isFirstRender()) { 4526 for (const elmtId of changedElmtIds) { 4527 this.dirtDescendantElementIds_.add(elmtId); 4528 } 4529 } 4530 4531 const cb = this.watchedProps.get(varName); 4532 if (cb) { 4533 4534 cb.call(this, varName); 4535 } 4536 } 4537 } // for all ownStorageLinksProps_ 4538 this.restoreInstanceId(); 4539 if (this.dirtDescendantElementIds_.size) { 4540 this.markNeedUpdate(); 4541 } 4542 }, "ViewPU.performDelayedUpdate", this.constructor.name); 4543 } 4544 /** 4545 * Function to be called from the constructor of the sub component 4546 * to register a @Watch varibale 4547 * @param propStr name of the variable. Note from @Provide and @Consume this is 4548 * the variable name and not the alias! 4549 * @param callback application defined member function of sub-class 4550 */ 4551 declareWatch(propStr, callback) { 4552 this.watchedProps.set(propStr, callback); 4553 } 4554 /** 4555 * This View @Provide's a variable under given name 4556 * Call this function from the constructor of the sub class 4557 * @param providedPropName either the variable name or the alias defined as 4558 * decorator param 4559 * @param store the backing store object for this variable (not the get/set variable!) 4560 */ 4561 addProvidedVar(providedPropName, store) { 4562 if (this.providedVars_.has(providedPropName)) { 4563 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. 4564 Property with this name is provided by one of the ancestor Views already.`); 4565 } 4566 this.providedVars_.set(providedPropName, store); 4567 } 4568 /** 4569 * Method for the sub-class to call from its constructor for resolving 4570 * a @Consume variable and initializing its backing store 4571 * with the SyncedPropertyTwoWay<T> object created from the 4572 * @Provide variable's backing store. 4573 * @param providedPropName the name of the @Provide'd variable. 4574 * This is either the @Consume decorator parameter, or variable name. 4575 * @param consumeVarName the @Consume variable name (not the 4576 * @Consume decorator parameter) 4577 * @returns initializing value of the @Consume backing store 4578 */ 4579 initializeConsume(providedPropName, consumeVarName) { 4580 let providedVarStore = this.providedVars_.get(providedPropName); 4581 if (providedVarStore === undefined) { 4582 throw new ReferenceError(`${this.constructor.name}: missing @Provide property with name ${providedPropName}. 4583 Fail to resolve @Consume(${providedPropName}).`); 4584 } 4585 const factory = (source) => { 4586 const result = ((source instanceof ObservedPropertySimple) || (source instanceof ObservedPropertySimplePU)) 4587 ? new SynchedPropertyObjectTwoWayPU(source, this, consumeVarName) 4588 : new SynchedPropertyObjectTwoWayPU(source, this, consumeVarName); 4589 return result; 4590 }; 4591 return providedVarStore.createSync(factory); 4592 } 4593 /** 4594 * given the elmtId of a child or child of child within this custom component 4595 * remember this component needs a partial update 4596 * @param elmtId 4597 */ 4598 markElemenDirtyById(elmtId) { 4599 // TODO ace-ets2bundle, framework, compilated apps need to update together 4600 // this function will be removed after a short transiition periode 4601 stateMgmtConsole.error(`markElemenDirtyById no longer supported. 4602 Please update your ace-ets2bundle and recompile your application!`); 4603 } 4604 /** 4605 * For each recorded dirty Element in this custom component 4606 * run its update function 4607 * 4608 */ 4609 updateDirtyElements() { 4610 do { 4611 4612 // request list of all (gloabbly) deleteelmtIds; 4613 let deletedElmtIds = []; 4614 this.getDeletedElemtIds(deletedElmtIds); 4615 // see which elmtIds are managed by this View 4616 // and clean up all book keeping for them 4617 this.purgeDeletedElmtIds(deletedElmtIds); 4618 // process all elmtIds marked as needing update in ascending order. 4619 // ascending order ensures parent nodes will be updated before their children 4620 // prior cleanup ensure no already deleted Elements have their update func executed 4621 Array.from(this.dirtDescendantElementIds_).sort(ViewPU.compareNumber).forEach(elmtId => { 4622 this.UpdateElement(elmtId); 4623 this.dirtDescendantElementIds_.delete(elmtId); 4624 }); 4625 if (this.dirtDescendantElementIds_.size) { 4626 stateMgmtConsole.error(`@Component '${this.constructor.name}' (id: ${this.id__()}): New UINode objects added to update queue while re-render! \ 4627 Likely caused by @Component state change during build phase, not allowed. Application error!`); 4628 } 4629 } while (this.dirtDescendantElementIds_.size); 4630 } 4631 // given a list elementIds removes these from state variables dependency list and from elmtId -> updateFunc map 4632 purgeDeletedElmtIds(rmElmtIds) { 4633 if (rmElmtIds.length == 0) { 4634 return; 4635 } 4636 4637 // rmElmtIds is the array of ElemntIds that 4638 let removedElmtIds = []; 4639 rmElmtIds.forEach((elmtId) => { 4640 // remove entry from Map elmtId -> update function 4641 if (this.updateFuncByElmtId.delete(elmtId)) { 4642 // for each state var, remove dependent elmtId (if present) 4643 // purgeVariableDependenciesOnElmtId needs to be generated by the compiler 4644 this.purgeVariableDependenciesOnElmtId(elmtId); 4645 // keep track of elmtId that has been de-registered 4646 removedElmtIds.push(elmtId); 4647 } 4648 }); 4649 this.deletedElmtIdsHaveBeenPurged(removedElmtIds); 4650 4651 4652 } 4653 // executed on first render only 4654 // kept for backward compatibility with old ace-ets2bundle 4655 observeComponentCreation(compilerAssignedUpdateFunc) { 4656 if (this.isDeleting_) { 4657 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destrucion, will not execute observeComponentCreation `); 4658 return; 4659 } 4660 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 4661 4662 compilerAssignedUpdateFunc(elmtId, /* is first render */ true); 4663 this.updateFuncByElmtId.set(elmtId, compilerAssignedUpdateFunc); 4664 4665 } 4666 // executed on first render only 4667 // added July 2023, replaces observeComponentCreation 4668 // classObject is the ES6 class object , mandatory to specify even the class lacks the pop function. 4669 // - prototype : Object is present for every ES6 class 4670 // - pop : () => void, static function present for JSXXX classes such as Column, TapGesture, etc. 4671 observeComponentCreation2(compilerAssignedUpdateFunc, classObject) { 4672 if (this.isDeleting_) { 4673 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destrucion, will not execute observeComponentCreation2 `); 4674 return; 4675 } 4676 const _componentName = (classObject && "name" in classObject) ? classObject.name : "unspecified UINode"; 4677 const _popFunc = (classObject && "pop" in classObject) ? classObject.pop : () => { }; 4678 const updateFunc = (elmtId, isFirstRender) => { 4679 4680 ViewStackProcessor.StartGetAccessRecordingFor(elmtId); 4681 compilerAssignedUpdateFunc(elmtId, isFirstRender); 4682 if (!isFirstRender) { 4683 _popFunc(); 4684 } 4685 ViewStackProcessor.StopGetAccessRecording(); 4686 4687 }; 4688 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 4689 4690 updateFunc(elmtId, /* is first render */ true); 4691 this.updateFuncByElmtId.set(elmtId, updateFunc); 4692 } 4693 getOrCreateRecycleManager() { 4694 if (!this.recycleManager) { 4695 this.recycleManager = new RecycleManager; 4696 } 4697 return this.recycleManager; 4698 } 4699 getRecycleManager() { 4700 return this.recycleManager; 4701 } 4702 hasRecycleManager() { 4703 return !(this.recycleManager === undefined); 4704 } 4705 initRecycleManager() { 4706 if (this.recycleManager) { 4707 4708 return; 4709 } 4710 this.recycleManager = new RecycleManager; 4711 } 4712 /** 4713 * @function observeRecycleComponentCreation 4714 * @description custom node recycle creation 4715 * @param name custom node name 4716 * @param recycleUpdateFunc custom node recycle update which can be converted to a normal update function 4717 * @return void 4718 */ 4719 observeRecycleComponentCreation(name, recycleUpdateFunc) { 4720 // convert recycle update func to update func 4721 const compilerAssignedUpdateFunc = (element, isFirstRender) => { 4722 recycleUpdateFunc(element, isFirstRender, undefined); 4723 }; 4724 let node; 4725 // if there is no suitable recycle node, run a normal creation function. 4726 if (!this.hasRecycleManager() || !(node = this.getRecycleManager().popRecycleNode(name))) { 4727 4728 this.observeComponentCreation(compilerAssignedUpdateFunc); 4729 return; 4730 } 4731 // if there is a suitable recycle node, run a recycle update function. 4732 const newElmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 4733 const oldElmtId = node.id__(); 4734 // store the current id and origin id, used for dirty element sort in {compareNumber} 4735 recycleUpdateFunc(newElmtId, /* is first render */ true, node); 4736 this.updateFuncByElmtId.delete(oldElmtId); 4737 this.updateFuncByElmtId.set(newElmtId, compilerAssignedUpdateFunc); 4738 node.updateId(newElmtId); 4739 node.updateRecycleElmtId(oldElmtId, newElmtId); 4740 SubscriberManager.UpdateRecycleElmtId(oldElmtId, newElmtId); 4741 } 4742 // add current JS object to it's parent recycle manager 4743 recycleSelf(name) { 4744 if (this.parent_ && !this.parent_.isDeleting_) { 4745 this.parent_.getOrCreateRecycleManager().pushRecycleNode(name, this); 4746 } 4747 else { 4748 this.resetRecycleCustomNode(); 4749 stateMgmtConsole.error(`${this.constructor.name}[${this.id__()}]: recycleNode must have a parent`); 4750 } 4751 } 4752 // performs the update on a branch within if() { branch } else if (..) { branch } else { branch } 4753 ifElseBranchUpdateFunction(branchId, branchfunc) { 4754 const oldBranchid = If.getBranchId(); 4755 if (branchId == oldBranchid) { 4756 4757 return; 4758 } 4759 // branchid identifies uniquely the if .. <1> .. else if .<2>. else .<3>.branch 4760 // ifElseNode stores the most recent branch, so we can compare 4761 // removedChildElmtIds will be filled with the elmtIds of all childten and their children will be deleted in response to if .. else chnage 4762 let removedChildElmtIds = new Array(); 4763 If.branchId(branchId, removedChildElmtIds); 4764 // purging these elmtIds from state mgmt will make sure no more update function on any deleted child wi;ll be executed 4765 4766 this.purgeDeletedElmtIds(removedChildElmtIds); 4767 // option 2 below: 4768 // this solution also works if the C++ side adds the UINodes to ElementRegister right away 4769 // I understand (to be conformed) adding can have a delay if there is an animation 4770 // then above solution will still work (should get a test case) 4771 branchfunc(); 4772 } 4773 /** 4774 Partial updates for ForEach. 4775 * @param elmtId ID of element. 4776 * @param itemArray Array of items for use of itemGenFunc. 4777 * @param itemGenFunc Item generation function to generate new elements. If index parameter is 4778 * given set itemGenFuncUsesIndex to true. 4779 * @param idGenFunc ID generation function to generate unique ID for each element. If index parameter is 4780 * given set idGenFuncUsesIndex to true. 4781 * @param itemGenFuncUsesIndex itemGenFunc optional index parameter is given or not. 4782 * @param idGenFuncUsesIndex idGenFunc optional index parameter is given or not. 4783 */ 4784 forEachUpdateFunction(elmtId, itemArray, itemGenFunc, idGenFunc, itemGenFuncUsesIndex = false, idGenFuncUsesIndex = false) { 4785 4786 if (itemArray === null || itemArray === undefined) { 4787 stateMgmtConsole.error(`ForEach input array is null or undefined error.`); 4788 return; 4789 } 4790 if (itemGenFunc === null || itemGenFunc === undefined) { 4791 stateMgmtConsole.error(`Error: Item generation function not defined in forEach function.`); 4792 return; 4793 } 4794 if (idGenFunc === undefined) { 4795 4796 idGenFuncUsesIndex = true; 4797 // catch possible error caused by Stringify and re-throw an Error with a meaningful (!) error message 4798 idGenFunc = (item, index) => { 4799 try { 4800 return `${index}__${JSON.stringify(item)}`; 4801 } 4802 catch (e) { 4803 throw new Error(`${this.constructor.name}[${this.id__()}]: ForEach id ${elmtId}: use of default id generator function not possble on provided data structure. Need to specify id generator function (ForEach 3rd parameter).`); 4804 } 4805 }; 4806 } 4807 let diffIndexArray = []; // New indexes compared to old one. 4808 let newIdArray = []; 4809 let idDuplicates = []; 4810 const arr = itemArray; // just to trigger a 'get' onto the array 4811 // ID gen is with index. 4812 if (idGenFuncUsesIndex) { 4813 4814 // Create array of new ids. 4815 arr.forEach((item, indx) => { 4816 newIdArray.push(idGenFunc(item, indx)); 4817 }); 4818 } 4819 else { 4820 // Create array of new ids. 4821 4822 arr.forEach((item, index) => { 4823 newIdArray.push(`${itemGenFuncUsesIndex ? index + '_' : ''}` + idGenFunc(item)); 4824 }); 4825 } 4826 // Set new array on C++ side. 4827 // C++ returns array of indexes of newly added array items. 4828 // these are indexes in new child list. 4829 ForEach.setIdArray(elmtId, newIdArray, diffIndexArray, idDuplicates); 4830 // Its error if there are duplicate IDs. 4831 if (idDuplicates.length > 0) { 4832 idDuplicates.forEach((indx) => { 4833 stateMgmtConsole.error(`Error: ${newIdArray[indx]} generated for ${indx}${indx < 4 ? indx == 2 ? "nd" : "rd" : "th"} array item ${arr[indx]}.`); 4834 }); 4835 stateMgmtConsole.error(`Ids generated by the ForEach id gen function must be unique, error.`); 4836 } 4837 4838 // Item gen is with index. 4839 4840 // Create new elements if any. 4841 diffIndexArray.forEach((indx) => { 4842 ForEach.createNewChildStart(newIdArray[indx], this); 4843 if (itemGenFuncUsesIndex) { 4844 itemGenFunc(arr[indx], indx); 4845 } 4846 else { 4847 itemGenFunc(arr[indx]); 4848 } 4849 ForEach.createNewChildFinish(newIdArray[indx], this); 4850 }); 4851 } 4852 /** 4853 * CreateStorageLink and CreateStorageLinkPU are used by the implementation of @StorageLink and 4854 * @LocalStotrageLink in full update and partial update solution respectively. 4855 * These are not part of the public AppStorage API , apps should not use. 4856 * @param storagePropName - key in LocalStorage 4857 * @param defaultValue - value to use when creating a new prop in the LocalStotage 4858 * @param owningView - the View/ViewPU owning the @StorageLink/@LocalStorageLink variable 4859 * @param viewVariableName - @StorageLink/@LocalStorageLink variable name 4860 * @returns SynchedPropertySimple/ObjectTwoWay/PU 4861 */ 4862 createStorageLink(storagePropName, defaultValue, viewVariableName) { 4863 const appStorageLink = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 4864 ? undefined 4865 : (source instanceof ObservedPropertySimple) 4866 ? new SynchedPropertyObjectTwoWayPU(source, this, viewVariableName) 4867 : new SynchedPropertyObjectTwoWayPU(source, this, viewVariableName)); 4868 this.ownStorageLinksProps_.add(appStorageLink); 4869 return appStorageLink; 4870 } 4871 createStorageProp(storagePropName, defaultValue, viewVariableName) { 4872 const appStorageProp = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 4873 ? undefined 4874 : (source instanceof ObservedPropertySimple) 4875 ? new SynchedPropertyObjectOneWayPU(source, this, viewVariableName) 4876 : new SynchedPropertyObjectOneWayPU(source, this, viewVariableName)); 4877 this.ownStorageLinksProps_.add(appStorageProp); 4878 return appStorageProp; 4879 } 4880 createLocalStorageLink(storagePropName, defaultValue, viewVariableName) { 4881 const localStorageLink = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 4882 ? undefined 4883 : (source instanceof ObservedPropertySimple) 4884 ? new SynchedPropertyObjectTwoWayPU(source, this, viewVariableName) 4885 : new SynchedPropertyObjectTwoWayPU(source, this, viewVariableName)); 4886 this.ownStorageLinksProps_.add(localStorageLink); 4887 return localStorageLink; 4888 } 4889 createLocalStorageProp(storagePropName, defaultValue, viewVariableName) { 4890 const localStorageProp = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 4891 ? undefined 4892 : (source instanceof ObservedPropertySimple) 4893 ? new SynchedPropertyObjectOneWayPU(source, this, viewVariableName) 4894 : new SynchedPropertyObjectOneWayPU(source, this, viewVariableName)); 4895 this.ownStorageLinksProps_.add(localStorageProp); 4896 return localStorageProp; 4897 } 4898} 4899// Array.sort() converts array items to string to compare them, sigh! 4900ViewPU.compareNumber = (a, b) => { 4901 return (a < b) ? -1 : (a > b) ? 1 : 0; 4902}; 4903/* 4904 * Copyright (c) 2023 Huawei Device Co., Ltd. 4905 * Licensed under the Apache License, Version 2.0 (the "License"); 4906 * you may not use this file except in compliance with the License. 4907 * You may obtain a copy of the License at 4908 * 4909 * http://www.apache.org/licenses/LICENSE-2.0 4910 * 4911 * Unless required by applicable law or agreed to in writing, software 4912 * distributed under the License is distributed on an "AS IS" BASIS, 4913 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4914 * See the License for the specific language governing permissions and 4915 * limitations under the License. 4916 * 4917 * * RecycleManager - Recycle cache manager 4918 * 4919* all definitions in this file are framework internal 4920*/ 4921/** 4922 * @class RecycleManager 4923 * @description manage the JS object cached of current node 4924 */ 4925class RecycleManager { 4926 constructor() { 4927 // key: recycle node name 4928 // value: recycle node JS object 4929 this.cachedRecycleNodes = undefined; 4930 this.cachedRecycleNodes = new Map(); 4931 } 4932 pushRecycleNode(name, node) { 4933 var _a; 4934 if (!this.cachedRecycleNodes.get(name)) { 4935 this.cachedRecycleNodes.set(name, new Array()); 4936 } 4937 (_a = this.cachedRecycleNodes.get(name)) === null || _a === void 0 ? void 0 : _a.push(node); 4938 } 4939 popRecycleNode(name) { 4940 var _a; 4941 return (_a = this.cachedRecycleNodes.get(name)) === null || _a === void 0 ? void 0 : _a.pop(); 4942 } 4943 // When parent JS View is deleted, release all cached nodes 4944 purgeAllCachedRecycleNode() { 4945 this.cachedRecycleNodes.forEach((nodes, _) => { 4946 nodes.forEach((node) => { 4947 node.resetRecycleCustomNode(); 4948 }); 4949 }); 4950 this.cachedRecycleNodes.clear(); 4951 } 4952} 4953/* 4954 * Copyright (c) 2022 Huawei Device Co., Ltd. 4955 * Licensed under the Apache License, Version 2.0 (the "License"); 4956 * you may not use this file except in compliance with the License. 4957 * You may obtain a copy of the License at 4958 * 4959 * http://www.apache.org/licenses/LICENSE-2.0 4960 * 4961 * Unless required by applicable law or agreed to in writing, software 4962 * distributed under the License is distributed on an "AS IS" BASIS, 4963 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4964 * See the License for the specific language governing permissions and 4965 * limitations under the License. 4966 * 4967 * * ViewPU - View for Partial Update 4968 * 4969* all definitions in this file are framework internal 4970*/ 4971/** 4972 given parameters for calling a @Builder function 4973 this function wraps the Object of type T inside a ES6 Proxy. 4974 Each param, i.e. Object property is either a function or a value. 4975 If it is a function the function can either return a value of expected 4976 parameter type or an ObservedPropertyabstract<T> where T is the exected 4977 parameter type. The latter is the case when passing a state variable by 4978 reference. 4979 4980 Two purposes: 4981 1 - @Builder function boxy accesses params a '$$.paramA' 4982 However paramA can be a function, so to obtain the value the 4983 access would need to be '$$.param()' The proxy executes 4984 the function and return s the result 4985 2 - said function returns to ObservedPropertyAbstract backing store of 4986 a calling @Component state variable (whenever the state var is 4987 provided to the @Builder function). For this case the proxy can provide 4988 - the value by executing paramA() to return the ObservedPropertyAbstract 4989 and further (monitored!) get() to read its value 4990 - when requested to return '__param1' it returns the ObservedPropertyAbstract 4991 object. The scenario is to use to init a @Link source. 4992 */ 4993function makeBuilderParameterProxy(builderName, source) { 4994 return new Proxy(source, { 4995 set(target, prop, val) { 4996 throw Error(`@Builder '${builderName}': Invalid attempt to set(write to) parameter '${prop.toString()}' error!`); 4997 }, 4998 get(target, prop) { 4999 const prop1 = prop.toString().trim().startsWith("__") 5000 ? prop.toString().trim().substring(2) 5001 : prop.toString().trim(); 5002 5003 if (!(typeof target === "object") && (prop1 in target)) { 5004 throw Error(`@Builder '${builderName}': '${prop1}' used but not a function parameter error!`); 5005 } 5006 const value = target[prop1]; 5007 if (typeof value !== "function") { 5008 5009 return value; 5010 } 5011 const funcRet = value(); 5012 if ((typeof funcRet === "object") && ('get' in funcRet)) { 5013 if (prop1 !== prop) { 5014 5015 return funcRet; 5016 } 5017 else { 5018 5019 const result = funcRet.get(); 5020 5021 return result; 5022 } 5023 } 5024 5025 return funcRet; 5026 } // get 5027 }); // new Proxy 5028} 5029/* 5030 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 5031 * Licensed under the Apache License, Version 2.0 (the "License"); 5032 * you may not use this file except in compliance with the License. 5033 * You may obtain a copy of the License at 5034 * 5035 * http://www.apache.org/licenses/LICENSE-2.0 5036 * 5037 * Unless required by applicable law or agreed to in writing, software 5038 * distributed under the License is distributed on an "AS IS" BASIS, 5039 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5040 * See the License for the specific language governing permissions and 5041 * limitations under the License. 5042 */ 5043 5044PersistentStorage.configureBackend(new Storage()); 5045Environment.configureBackend(new EnvironmentSetting()); 5046 5047