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 * ConfigureStateMgmt keeps track if V2 @ObservedV2 and @Trace are used. 17 * If yes, it enables object deep observation mechanisms need with @ObservedV2. 18 */ 19class ConfigureStateMgmt { 20 constructor() { 21 this.v2ObservedTrackInUse_ = false; 22 this.puObservedTrackInUse_ = false; 23 } 24 static get instance() { 25 return ConfigureStateMgmt.instance__ 26 ? ConfigureStateMgmt.instance__ 27 : (ConfigureStateMgmt.instance__ = new ConfigureStateMgmt()); 28 } 29 /** 30 * framework code call this function when it sees use of a stateMgmt V2 @ObservedV2 @Trace 31 * 32 * @param feature specify feature separately from context of use, so that in future decision can be made 33 * for individual features, not use permit either use of V1 or V2. 34 * @param contextOfUse purely for error messages. Give enough info that use is able to local the feature use in source code. 35 * @returns true if no mix of features detected, false if mix is detected 36 */ 37 usingV2ObservedTrack(feature, contextOfUse = '') { 38 this.v2ObservedTrackInUse_ = true; 39 40 } 41 /** 42 * framework code call this function when it sees use of a stateMgmt PU Observed / @Track 43 * 44 * @param feature specify feature separately from context of use, so that in future decision can be made 45 * for individual features, not use permit either use of V1 or V2. 46 * @param contextOfUse purely for error messages. Give enough info that use is able to local the feature use in source code. 47 * @returns true if no mix of features detected, false if mix is detected 48 */ 49 usingPUObservedTrack(feature, contextOfUse = '') { 50 this.puObservedTrackInUse_ = true; 51 52 } 53 /** 54 * Return true if object deep observation mechanisms need to be enabled 55 * that is when seen V2 @ObservedV2, @Trace, or @Monitor decorator used in at least one class 56 * (we could but we do not check for class object instance creation for performance reasons) 57 * @returns 58 */ 59 needsV2Observe() { 60 return this.v2ObservedTrackInUse_; 61 } 62} // ConfigureStateMgmt 63ConfigureStateMgmt.HOW_TO_SAY = `Your application uses both state management V1 and V2 features! - It is strongly recommended not to mix V1 and V2. Consult the rules how state management V1 and V2 can be mixed in the same app.`; 64/* 65 * Copyright (c) 2023 Huawei Device Co., Ltd. 66 * Licensed under the Apache License, Version 2.0 (the "License"); 67 * you may not use this file except in compliance with the License. 68 * You may obtain a copy of the License at 69 * 70 * http://www.apache.org/licenses/LICENSE-2.0 71 * 72 * Unless required by applicable law or agreed to in writing, software 73 * distributed under the License is distributed on an "AS IS" BASIS, 74 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 75 * See the License for the specific language governing permissions and 76 * limitations under the License. 77 */ 78class stateMgmtProfiler { 79 static begin(blockName) { 80 var _a; 81 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.begin(blockName); 82 } 83 static end() { 84 var _a; 85 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.end(); 86 } 87 static report() { 88 var _a; 89 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.report(); 90 } 91 static clear() { 92 var _a; 93 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.clear(); 94 } 95 static init(instance) { 96 stateMgmtProfiler.instance = instance; 97 } 98} 99stateMgmtProfiler.instance = undefined; 100/* 101 * Copyright (c) 2021 Huawei Device Co., Ltd. 102 * Licensed under the Apache License, Version 2.0 (the "License"); 103 * you may not use this file except in compliance with the License. 104 * You may obtain a copy of the License at 105 * 106 * http://www.apache.org/licenses/LICENSE-2.0 107 * 108 * Unless required by applicable law or agreed to in writing, software 109 * distributed under the License is distributed on an "AS IS" BASIS, 110 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 111 * See the License for the specific language governing permissions and 112 * limitations under the License. 113 */ 114/* 115 * Copyright (c) 2021 Huawei Device Co., Ltd. 116 * Licensed under the Apache License, Version 2.0 (the "License"); 117 * you may not use this file except in compliance with the License. 118 * You may obtain a copy of the License at 119 * 120 * http://www.apache.org/licenses/LICENSE-2.0 121 * 122 * Unless required by applicable law or agreed to in writing, software 123 * distributed under the License is distributed on an "AS IS" BASIS, 124 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 125 * See the License for the specific language governing permissions and 126 * limitations under the License. 127 */ 128/* 129 * Copyright (c) 2023 Huawei Device Co., Ltd. 130 * Licensed under the Apache License, Version 2.0 (the "License"); 131 * you may not use this file except in compliance with the License. 132 * You may obtain a copy of the License at 133 * 134 * http://www.apache.org/licenses/LICENSE-2.0 135 * 136 * Unless required by applicable law or agreed to in writing, software 137 * distributed under the License is distributed on an "AS IS" BASIS, 138 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139 * See the License for the specific language governing permissions and 140 * limitations under the License. 141 */ 142/* 143 * Copyright (c) 2021 Huawei Device Co., Ltd. 144 * Licensed under the Apache License, Version 2.0 (the "License"); 145 * you may not use this file except in compliance with the License. 146 * You may obtain a copy of the License at 147 * 148 * http://www.apache.org/licenses/LICENSE-2.0 149 * 150 * Unless required by applicable law or agreed to in writing, software 151 * distributed under the License is distributed on an "AS IS" BASIS, 152 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 153 * See the License for the specific language governing permissions and 154 * limitations under the License. 155 */ 156/* 157 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 158 * Licensed under the Apache License, Version 2.0 (the "License"); 159 * you may not use this file except in compliance with the License. 160 * You may obtain a copy of the License at 161 * 162 * http://www.apache.org/licenses/LICENSE-2.0 163 * 164 * Unless required by applicable law or agreed to in writing, software 165 * distributed under the License is distributed on an "AS IS" BASIS, 166 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 167 * See the License for the specific language governing permissions and 168 * limitations under the License. 169 */ 170/** 171 * 172 * LocalStorage 173 * 174 * Class implements a Map of ObservableObjectBase UI state variables. 175 * Instances can be created to manage UI state within a limited "local" 176 * access, and life cycle as defined by the app. 177 * AppStorage singleton is sub-class of LocalStorage for 178 * UI state of app-wide access and same life cycle as the app. 179 * 180 * @since 9 181 */ 182class LocalStorage extends NativeLocalStorage { 183 /** 184 * Construct new instance of LocalStorage 185 * initialzie with all properties and their values that Object.keys(params) returns 186 * Property values must not be undefined for API 9 and lower, undefined allowed for API10 187 * @param initializingProperties Object containing keys and values. @see set() for valid values 188 * 189 * @since 9 190 */ 191 constructor(initializingProperties = {}) { 192 // This is edited for the statibility issue that "construtor is false", which meaning that the super() is not callable 193 // It is just the debug log using ArkTools print. 194 try { 195 super(); 196 } 197 catch (error) { 198 stateMgmtConsole.error(`An error occurred in the constructor of LocalStorage ${error.message}`); 199 ArkTools.print("NativeLocalStorage", NativeLocalStorage); 200 throw error; 201 } 202 203 this.storage_ = new Map(); 204 if (Object.keys(initializingProperties).length) { 205 this.initializeProps(initializingProperties); 206 } 207 } 208 /* 209 get access to provded LocalStorage instance thru Stake model 210 @StageModelOnly 211 @form 212 @since 10 213 */ 214 static getShared() { 215 return LocalStorage.GetShared(); 216 } 217 /** 218 * clear storage and init with given properties 219 * @param initializingProperties 220 * 221 * not a public / sdk function 222 */ 223 initializeProps(initializingProperties = {}) { 224 225 this.storage_.clear(); 226 Object.keys(initializingProperties) 227 .filter((propName) => (initializingProperties[propName] != null || Utils.isApiVersionEQAbove(12))) 228 .forEach((propName) => this.addNewPropertyInternal(propName, initializingProperties[propName])); 229 } 230 /** 231 * Use before deleting owning Ability, window, or service UI 232 * (letting it go out of scope). 233 * 234 * This method orderly closes down a LocalStorage instance by calling @see clear(). 235 * This requires that no property is left with one or more subscribers. 236 * @see clear() and @see delete() 237 * @returns true if all properties could be removed from storage 238 */ 239 aboutToBeDeleted() { 240 return this.clear(); 241 } 242 /** 243 * Check if LocalStorage has a property with given name 244 * return true if prooperty with given name exists 245 * same as ES6 Map.prototype.has() 246 * @param propName searched property 247 * @returns true if property with such name exists in LocalStorage 248 * 249 * @since 9 250 */ 251 has(propName) { 252 return this.storage_.has(propName); 253 } 254 /** 255 * Provide names of all properties in LocalStorage 256 * same as ES6 Map.prototype.keys() 257 * @returns return a Map Iterator 258 * 259 * @since 9 260 */ 261 keys() { 262 return this.storage_.keys(); 263 } 264 /** 265 * Returns number of properties in LocalStorage 266 * same as Map.prototype.size() 267 * @param propName 268 * @returns return number of properties 269 * 270 * @since 9 271 */ 272 size() { 273 return this.storage_.size; 274 } 275 /** 276 * Returns value of given property 277 * return undefined if no property with this name 278 * @param propName 279 * @returns property value if found or undefined 280 * 281 * @since 9 282 */ 283 get(propName) { 284 let p = this.storage_.get(propName); 285 return (p) ? p.get() : undefined; 286 } 287 /** 288 * Set value of given property in LocalStorage 289 * Methosd sets nothing and returns false if property with this name does not exist 290 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 291 * @param propName 292 * @param newValue must be of type T and must not be undefined or null 293 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 294 * 295 * @since 9 296 */ 297 set(propName, newValue) { 298 299 if (newValue === undefined && !Utils.isApiVersionEQAbove(12)) { 300 301 302 return false; 303 } 304 var p = this.storage_.get(propName); 305 if (p === undefined) { 306 307 308 return false; 309 } 310 p.set(newValue); 311 312 return true; 313 } 314 /** 315 * Set value of given property, if it exists, @see set() . 316 * Add property if no property with given name and initialize with given value. 317 * Do nothing and return false if newValuue is undefined or null 318 * (undefined, null value is not allowed for state variables) 319 * @param propName 320 * @param newValue must be of type T and must not be undefined or null 321 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 322 * 323 * @since 9 324 */ 325 setOrCreate(propName, newValue) { 326 327 if (newValue == undefined && !Utils.isApiVersionEQAbove(12)) { 328 329 330 return false; 331 } 332 let p = this.storage_.get(propName); 333 if (p) { 334 335 p.set(newValue); 336 } 337 else { 338 339 this.addNewPropertyInternal(propName, newValue); 340 } 341 342 return true; 343 } 344 /** 345 * Obtain a handle or an alias to LocalStorage property with given name. 346 * 347 * @param propName LocalStorage property name 348 * @returns AbstractProperty object is property with given name exists 349 * undefined otherwise 350 */ 351 ref(propName) { 352 return this.storage_.get(propName); 353 } 354 /** 355 * Obtain a handle or an alias to LocalStorage property with given name. 356 * 357 * If property does not exist in LocalStorage, create it with given default value. 358 * 359 * @param propName LocalStorage property name 360 * @param defaultValue If property does not exist in LocalStorage, 361 * create it with given default value. 362 * @returns AbstractProperty object 363 */ 364 setAndRef(propName, defaultValue) { 365 if (!this.has(propName)) { 366 this.addNewPropertyInternal(propName, defaultValue); 367 } 368 return this.storage_.get(propName); 369 } 370 /** 371 * Internal use helper function to create and initialize a new property. 372 * caller needs to be all the checking beforehand 373 * @param propName 374 * @param value 375 * 376 * Not a public / sdk method. 377 */ 378 addNewPropertyInternal(propName, value) { 379 let newProp; 380 if (ViewStackProcessor.UsesNewPipeline()) { 381 newProp = new ObservedPropertyPU(value, undefined, propName); 382 } 383 else { 384 newProp = (typeof value === 'object') ? 385 new ObservedPropertyObject(value, undefined, propName) 386 : new ObservedPropertySimple(value, undefined, propName); 387 } 388 this.storage_.set(propName, newProp); 389 return newProp; 390 } 391 /** 392 * create and return a two-way sync "(link") to named property 393 * @param propName name of source property in LocalStorage 394 * @param linkUser IPropertySubscriber to be notified when source changes, 395 * @param subscribersName optional, the linkUser (subscriber) uses this name for the property 396 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 397 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 398 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 399 * return undefiend if named property does not already exist in LocalStorage 400 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 401 * return undefiend if named property does not already exist in LocalStorage 402 * 403 * @since 9 404 */ 405 link(propName, linkUser, subscribersName) { 406 407 var p = this.storage_.get(propName); 408 if (p == undefined) { 409 410 411 return undefined; 412 } 413 let linkResult; 414 if (ViewStackProcessor.UsesNewPipeline()) { 415 linkResult = new SynchedPropertyTwoWayPU(p, linkUser, propName); 416 } 417 else { 418 linkResult = p.createLink(linkUser, propName); 419 } 420 linkResult.setInfo(subscribersName); 421 422 return linkResult; 423 } 424 /** 425 * Like @see link(), but will create and initialize a new source property in LocalStorge if missing 426 * @param propName name of source property in LocalStorage 427 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage 428 * default value must be of type S, must not be undefined or null. 429 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 430 * @param subscribersName the linkUser (subscriber) uses this name for the property 431 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 432 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 433 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 434 * 435 * @since 9 436 */ 437 setAndLink(propName, defaultValue, linkUser, subscribersName) { 438 439 var p = this.storage_.get(propName); 440 if (!p) { 441 this.setOrCreate(propName, defaultValue); 442 } 443 const link = this.link(propName, linkUser, subscribersName); 444 445 return link; 446 } 447 /** 448 * create and return a one-way sync ('prop') to named property 449 * @param propName name of source property in LocalStorage 450 * @param propUser IPropertySubscriber to be notified when source changes, 451 * @param subscribersName the linkUser (subscriber) uses this name for the property 452 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 453 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 454 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 455 * return undefiend if named property does not already exist in LocalStorage. 456 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 457 * return undefiend if named property does not already exist in LocalStorage. 458 * @since 9 459 */ 460 prop(propName, propUser, subscribersName) { 461 462 var p = this.storage_.get(propName); 463 if (p == undefined) { 464 465 466 return undefined; 467 } 468 let propResult; 469 if (ViewStackProcessor.UsesNewPipeline()) { 470 propResult = new SynchedPropertyOneWayPU(p, propUser, propName); 471 } 472 else { 473 propResult = p.createProp(propUser, propName); 474 } 475 propResult.setInfo(subscribersName); 476 477 return propResult; 478 } 479 /** 480 * Like @see prop(), will create and initialize a new source property in LocalStorage if missing 481 * @param propName name of source property in LocalStorage 482 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage. 483 * default value must be of type S, must not be undefined or null. 484 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 485 * @param subscribersName the propUser (subscriber) uses this name for the property 486 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 487 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 488 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 489 * @since 9 490 */ 491 setAndProp(propName, defaultValue, propUser, subscribersName) { 492 493 let p = this.storage_.get(propName); 494 if (!p) { 495 this.setOrCreate(propName, defaultValue); 496 } 497 const prop = this.prop(propName, propUser, subscribersName); 498 499 return prop; 500 } 501 /** 502 * Delete property from StorageBase 503 * Use with caution: 504 * Before deleting a prop from LocalStorage all its subscribers need to 505 * unsubscribe from the property. 506 * This method fails and returns false if given property still has subscribers 507 * Another reason for failing is unkmown property. 508 * 509 * Developer advise: 510 * Subscribers are created with @see link(), @see prop() 511 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 512 * That means as long as their is a @Component instance that uses such decorated variable 513 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 514 * (and also should not!) be deleted from LocalStorage. 515 * 516 * @param propName 517 * @returns false if method failed 518 * 519 * @since 9 520 */ 521 delete(propName) { 522 523 let p = this.storage_.get(propName); 524 if (p) { 525 if (p.numberOfSubscrbers()) { 526 stateMgmtConsole.error(`${this.constructor.name}: Attempt to delete property ${propName} that has \ 527 ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion.`); 528 529 return false; 530 } 531 p.aboutToBeDeleted(); 532 this.storage_.delete(propName); 533 534 return true; 535 } 536 else { 537 538 539 return false; 540 } 541 } 542 /** 543 * delete all properties from the LocalStorage instance 544 * @see delete(). 545 * precondition is that there are no subscribers. 546 * method returns false and deletes no poperties if there is any property 547 * that still has subscribers 548 * 549 * @since 9 550 */ 551 clear() { 552 553 for (let propName of this.keys()) { 554 var p = this.storage_.get(propName); 555 if (p.numberOfSubscrbers()) { 556 stateMgmtConsole.error(`${this.constructor.name}.deleteAll: Attempt to delete property ${propName} that \ 557 has ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion. 558 Any @Component instance with a @StorageLink/Prop or @LocalStorageLink/Prop is a subscriber.`); 559 560 return false; 561 } 562 } 563 for (let propName of this.keys()) { 564 var p = this.storage_.get(propName); 565 p.aboutToBeDeleted(); 566 } 567 this.storage_.clear(); 568 569 570 return true; 571 } 572 /** 573 * Subscribe to value change notifications of named property 574 * Any object implementing ISinglePropertyChangeSubscriber interface 575 * and registerign itself to SubscriberManager can register 576 * Caution: do remember to unregister, otherwise the property will block 577 * cleanup, @see delete() and @see clear() 578 * 579 * @param propName property in LocalStorage to subscribe to 580 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 581 * @returns false if named property does not exist 582 * 583 * @since 9 584 */ 585 subscribeToChangesOf(propName, subscriber) { 586 var p = this.storage_.get(propName); 587 if (p) { 588 p.addSubscriber(subscriber); 589 return true; 590 } 591 return false; 592 } 593 /** 594 * inverse of @see subscribeToChangesOf 595 * @param propName property in LocalStorage to subscribe to 596 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 597 * @returns false if named property does not exist 598 * 599 * @since 9 600 */ 601 unsubscribeFromChangesOf(propName, subscriberId) { 602 var p = this.storage_.get(propName); 603 if (p) { 604 p.removeSubscriber(null, subscriberId); 605 return true; 606 } 607 return false; 608 } 609 __createSync(storagePropName, defaultValue, factoryFunc) { 610 let p = this.storage_.get(storagePropName); 611 if (p == undefined) { 612 // property named 'storagePropName' not yet in storage 613 // add new property to storage 614 // We do not want to add undefined to older API verions, but null is added 615 if (defaultValue === undefined && !Utils.isApiVersionEQAbove(12)) { 616 stateMgmtConsole.error(`${this.constructor.name}.__createSync(${storagePropName}, non-existing property and undefined default value. ERROR.`); 617 return undefined; 618 } 619 p = this.addNewPropertyInternal(storagePropName, defaultValue); 620 } 621 return factoryFunc(p); 622 } 623} 624/* 625 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 626 * Licensed under the Apache License, Version 2.0 (the "License"); 627 * you may not use this file except in compliance with the License. 628 * You may obtain a copy of the License at 629 * 630 * http://www.apache.org/licenses/LICENSE-2.0 631 * 632 * Unless required by applicable law or agreed to in writing, software 633 * distributed under the License is distributed on an "AS IS" BASIS, 634 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 635 * See the License for the specific language governing permissions and 636 * limitations under the License. 637 */ 638/** 639 * 640 * AppStorage 641 * 642 * Class implements a Map of ObservableObjectBase UI state variables. 643 * AppStorage singleton is sub-class of @see LocalStorage for 644 * UI state of app-wide access and same life cycle as the app. 645 * 646 * @since 7 647 */ 648class AppStorage extends LocalStorage { 649 /** singleton class, app can not create instances 650 * 651 * not a public / sdk function 652 */ 653 constructor(initializingProperties) { 654 super(initializingProperties); 655 } 656 /** 657 * create and initialize singleton 658 * initialzie with all properties and their values that Object.keys(params) returns 659 * Property values must not be undefined. 660 * 661 * not a public / sdk function 662 */ 663 static createSingleton(initializingPropersties) { 664 if (!AppStorage.instance_) { 665 666 AppStorage.instance_ = new AppStorage(initializingPropersties); 667 } 668 else { 669 stateMgmtConsole.error('AppStorage.createNewInstance(..): instance exists already, internal error!'); 670 } 671 } 672 /** 673 * Obtain a handle or an alias to AppStorage property with given name. 674 * 675 * @param propName AppStorage property name 676 * @returns AbstractProperty object is property with given name exists 677 * undefined otherwise 678 * 679 * @since 12 680 */ 681 static ref(propName) { 682 return AppStorage.getOrCreate().ref(propName); 683 } 684 /** 685 * Obtain a handle or an alias to AppStorage property with given name. 686 * 687 * If property does not exist in AppStorage, create it with given default value. 688 * 689 * @param propName LocalStorage property name 690 * @param defaultValue If property does not exist in AppStorage, 691 * create it with given default value. 692 * @returns AbstractProperty object 693 * 694 * @since 12 695 */ 696 static setAndRef(propName, defaultValue) { 697 return AppStorage.getOrCreate().setAndRef(propName, defaultValue); 698 } 699 /** 700 * create and return a two-way sync "(link") to named property 701 * 702 * Same as @see LocalStorage.link() 703 * 704 * @param propName name of source property in AppStorage 705 * @param linkUser IPropertySubscriber to be notified when source changes, 706 * @param subscribersName the linkUser (subscriber) uses this name for the property 707 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 708 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 709 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 710 * return undefiend if named property does not already exist in AppStorage 711 * 712 * @since 10 713 */ 714 static link(key, linkUser, subscribersName) { 715 return AppStorage.getOrCreate().link(key, linkUser, subscribersName); 716 } 717 /** 718 * @see link 719 * @since 7 720 * @deprecated 721 */ 722 static Link(key, linkUser, subscribersName) { 723 return AppStorage.getOrCreate().link(key, linkUser, subscribersName); 724 } 725 /** 726 * Like @see link(), but will create and initialize a new source property in LocalStorage if missing 727 * 728 * Same as @see LocalStorage.setAndLink() 729 * 730 * @param propName name of source property in AppStorage 731 * @param defaultValue value to be used for initializing if new creating new property in AppStorage 732 * default value must be of type S, must not be undefined or null. 733 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 734 * @param subscribersName the linkUser (subscriber) uses this name for the property 735 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 736 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 737 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 738 * 739 * @since 10 740 */ 741 static setAndLink(key, defaultValue, linkUser, subscribersName) { 742 return AppStorage.getOrCreate().setAndLink(key, defaultValue, linkUser, subscribersName); 743 } 744 /** 745 * @see setAndLink 746 * @since 7 747 * @deprecated 748 */ 749 static SetAndLink(key, defaultValue, linkUser, subscribersName) { 750 return AppStorage.getOrCreate().setAndLink(key, defaultValue, linkUser, subscribersName); 751 } 752 /** 753 * create and return a one-way sync ('prop') to named property 754 * 755 * Same as @see LocalStorage.prop() 756 * 757 * @param propName name of source property in AppStorage 758 * @param propUser IPropertySubscriber to be notified when source changes, 759 * @param subscribersName the linkUser (subscriber) uses this name for the property 760 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 761 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 762 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 763 * return undefiend if named property does not already exist in AppStorage. 764 * @since 10 765 */ 766 static prop(propName, propUser, subscribersName) { 767 return AppStorage.getOrCreate().prop(propName, propUser, subscribersName); 768 } 769 /** 770 * @see prop 771 * @since 7 772 * @deprecated 773 */ 774 static Prop(propName, propUser, subscribersName) { 775 return AppStorage.getOrCreate().prop(propName, propUser, subscribersName); 776 } 777 /** 778 * Like @see prop(), will create and initialize a new source property in AppStorage if missing 779 * 780 * Same as @see LocalStorage.setAndProp() 781 * 782 * @param propName name of source property in AppStorage 783 * @param defaultValue value to be used for initializing if new creating new property in AppStorage. 784 * default value must be of type S, must not be undefined or null. 785 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 786 * @param subscribersName the propUser (subscriber) uses this name for the property 787 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 788 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 789 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 790 * 791 * @since 10 792 */ 793 static setAndProp(key, defaultValue, propUser, subscribersName) { 794 return AppStorage.getOrCreate().setAndProp(key, defaultValue, propUser, subscribersName); 795 } 796 /** 797 * @see setAndProp 798 * @since 7 799 * @deprecated 800 */ 801 static SetAndProp(key, defaultValue, propUser, subscribersName) { 802 return AppStorage.getOrCreate().setAndProp(key, defaultValue, propUser, subscribersName); 803 } 804 /** 805 * Check if AppStorage has a property with given name 806 * return true if property with given name exists 807 * same as ES6 Map.prototype.has() 808 * 809 * Same as @see LocalStorage.has() 810 * 811 * @param propName searched property 812 * @returns true if property with such name exists in AppStorage 813 * 814 * @since 10 815 */ 816 static has(key) { 817 return AppStorage.getOrCreate().has(key); 818 } 819 /** 820 * @see has() 821 * @since 7 822 * @deprecated 823 */ 824 static Has(key) { 825 return AppStorage.getOrCreate().has(key); 826 } 827 /** 828 * Returns value of given property 829 * return undefined if no property with this name 830 * 831 * @Same as see LocalStorage.get() 832 * 833 * @param propName 834 * @returns property value if found or undefined 835 * 836 * @since 10 837 * 838 */ 839 static get(key) { 840 return AppStorage.getOrCreate().get(key); 841 } 842 /** 843 * @see get 844 * @since 7 845 * @deprecated 846 * 847 */ 848 static Get(key) { 849 return AppStorage.getOrCreate().get(key); 850 } 851 /** 852 * Set value of given property in AppStorage 853 * Method sets nothing and returns false if property with this name does not exist 854 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 855 * 856 * Same as @see LocalStorage.set 857 * 858 * @param propName 859 * @param newValue must be of type T and must not be undefined or null 860 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 861 * 862 * @since 10 863 */ 864 static set(key, newValue) { 865 return AppStorage.getOrCreate().set(key, newValue); 866 } 867 /** 868 * @see set 869 * @since 7 870 * @deprecated 871 */ 872 static Set(key, newValue) { 873 return AppStorage.getOrCreate().set(key, newValue); 874 } 875 /** 876 * Set value of given property, if it exists, @see set() . 877 * Add property if no property with given name and initialize with given value. 878 * Do nothing and return false if newValuue is undefined or null 879 * (undefined, null value is not allowed for state variables) 880 * 881 * @see LocalStorage.setOrCreate() 882 * 883 * @param propName 884 * @param newValue must be of type T and must not be undefined or null 885 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 886 * 887 * @since 10 888 */ 889 static setOrCreate(key, newValue) { 890 AppStorage.getOrCreate().setOrCreate(key, newValue); 891 } 892 /** 893 * @see setOrCreate 894 * @since 7 895 * @deprecated 896 */ 897 static SetOrCreate(key, newValue) { 898 AppStorage.getOrCreate().setOrCreate(key, newValue); 899 } 900 /** 901 * Delete property from StorageBase 902 * Use with caution: 903 * Before deleting a prop from AppStorage all its subscribers need to 904 * unsubscribe from the property. 905 * This method fails and returns false if given property still has subscribers 906 * Another reason for failing is unkmown property. 907 * 908 * Developer advise: 909 * Subscribers are created with @see link(), @see prop() 910 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 911 * That means as long as their is a @Component instance that uses such decorated variable 912 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 913 * (and also should not!) be deleted from AppStorage. 914 * 915 * Same as @see LocalStorage.delete() 916 * 917 * @param propName 918 * @returns false if method failed 919 * 920 * @since 10 921 */ 922 static delete(key) { 923 return AppStorage.getOrCreate().delete(key); 924 } 925 /** 926 * @see delete 927 * @since 7 928 * @deprecated 929 */ 930 static Delete(key) { 931 return AppStorage.getOrCreate().delete(key); 932 } 933 /** 934 * Provide names of all properties in AppStorage 935 * same as ES6 Map.prototype.keys() 936 * 937 * Same as @see LocalStorage.keys() 938 * 939 * @returns return a Map Iterator 940 * 941 * @since 10 942 */ 943 static keys() { 944 return AppStorage.getOrCreate().keys(); 945 } 946 /** 947 * @see keys 948 * @since 7 949 * @deprecated 950 */ 951 static Keys() { 952 return AppStorage.getOrCreate().keys(); 953 } 954 /** 955 * Returns number of properties in AppStorage 956 * same as Map.prototype.size() 957 * 958 * Same as @see LocalStorage.size() 959 * 960 * @param propName 961 * @returns return number of properties 962 * 963 * @since 10 964 */ 965 static size() { 966 return AppStorage.getOrCreate().size(); 967 } 968 /** 969 * @see size 970 * @since 7 971 * @deprecated 972 */ 973 static Size() { 974 return AppStorage.getOrCreate().size(); 975 } 976 /** 977 * delete all properties from the AppStorage 978 * 979 * @see delete(), same as @see LocalStorage.clear() 980 * 981 * precondition is that there are no subscribers. 982 * method returns false and deletes no poperties if there is any property 983 * that still has subscribers 984 * 985 * @since 10 986 */ 987 static clear() { 988 return AppStorage.getOrCreate().clear(); 989 } 990 /** 991 * @see clear 992 * @since 7 993 * @deprecated 994 */ 995 static Clear() { 996 return AppStorage.getOrCreate().clear(); 997 } 998 /** 999 * Same as @see clear(). 1000 * 1001 * @since 7, deprecated, used clear() instead! 1002 * 1003 */ 1004 static StaticClear() { 1005 return AppStorage.clear(); 1006 } 1007 /** 1008 * not a public / sdk function 1009 */ 1010 static aboutToBeDeleted() { 1011 AppStorage.getOrCreate().aboutToBeDeleted(); 1012 } 1013 /** 1014 * Subscribe to value change notifications of named property 1015 * Any object implementing ISinglePropertyChangeSubscriber interface 1016 * and registerign itself to SubscriberManager can register 1017 * Caution: do remember to unregister, otherwise the property will block 1018 * cleanup, @see delete() and @see clear() 1019 * 1020 * Same as @see LocalStorage.subscribeToChangesOf() 1021 * 1022 * @param propName property in AppStorage to subscribe to 1023 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 1024 * @returns false if named property does not exist 1025 * 1026 * @since 10 1027 */ 1028 static subscribeToChangesOf(propName, subscriber) { 1029 return AppStorage.getOrCreate().subscribeToChangesOf(propName, subscriber); 1030 } 1031 /** 1032 * @see subscribeToChangesOf 1033 * @since 7 1034 * @deprecated 1035 */ 1036 static SubscribeToChangesOf(propName, subscriber) { 1037 return AppStorage.getOrCreate().subscribeToChangesOf(propName, subscriber); 1038 } 1039 /** 1040 * inverse of @see SubscribeToChangesOf, 1041 * same as @see LocalStorage.subscribeToChangesOf() 1042 * 1043 * @param propName property in AppStorage to subscribe to 1044 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 1045 * @returns false if named property does not exist 1046 * 1047 * @since 10 1048 */ 1049 static unsubscribeFromChangesOf(propName, subscriberId) { 1050 return AppStorage.getOrCreate().unsubscribeFromChangesOf(propName, subscriberId); 1051 } 1052 /** 1053 * @see unsubscribeFromChangesOf 1054 * @since 7 1055 * @deprecated 1056 */ 1057 static UnsubscribeFromChangesOf(propName, subscriberId) { 1058 return AppStorage.getOrCreate().unsubscribeFromChangesOf(propName, subscriberId); 1059 } 1060 /** 1061 * Unimplemented, currently all properties of AppStorage are mutable. 1062 * 1063 * @since 7, deprecated 1064 */ 1065 static IsMutable(key) { 1066 return true; 1067 } 1068 /** 1069 * not a public / sdk function 1070 */ 1071 static __createSync(storagePropName, defaultValue, factoryFunc) { 1072 return AppStorage.getOrCreate().__createSync(storagePropName, defaultValue, factoryFunc); 1073 } 1074 /** 1075 * not a public / sdk function 1076 */ 1077 static getOrCreate() { 1078 if (!AppStorage.instance_) { 1079 AppStorage.instance_ = new AppStorage({}); 1080 } 1081 return AppStorage.instance_; 1082 } 1083} 1084// instance functions below: 1085// Should all be protected, but TS lang does not allow access from static member to protected member 1086AppStorage.instance_ = undefined; 1087/* 1088 * Copyright (c) 2022 Huawei Device Co., Ltd. 1089 * Licensed under the Apache License, Version 2.0 (the "License"); 1090 * you may not use this file except in compliance with the License. 1091 * You may obtain a copy of the License at 1092 * 1093 * http://www.apache.org/licenses/LICENSE-2.0 1094 * 1095 * Unless required by applicable law or agreed to in writing, software 1096 * distributed under the License is distributed on an "AS IS" BASIS, 1097 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1098 * See the License for the specific language governing permissions and 1099 * limitations under the License. 1100 */ 1101/** 1102 * Singleton class SubscriberManager implements IPropertySubscriberLookup 1103 * public API to manage IPropertySubscriber 1104 */ 1105class SubscriberManager { 1106 /** 1107 * SubscriberManager is a singleton created by the framework 1108 * do not use 1109 * 1110 * internal method 1111 */ 1112 constructor() { 1113 this.subscriberById_ = new Map(); 1114 1115 } 1116 /** 1117 * check subscriber is known 1118 * same as ES6 Map.prototype.has() 1119 * 1120 * @since 9 1121 */ 1122 static Has(id) { 1123 return SubscriberManager.GetInstance().has(id); 1124 } 1125 /** 1126 * 1127 * retrieve subscriber by id 1128 * same as ES6 Map.prototype.get() 1129 * 1130 * @since 9 1131 */ 1132 static Find(id) { 1133 return SubscriberManager.GetInstance().get(id); 1134 } 1135 /** 1136 * unregister a subscriber 1137 * same as ES6 Map.prototype.delete() 1138 * @return boolean success or failure to delete 1139 * 1140 * @since 9 1141 */ 1142 static Delete(id) { 1143 return SubscriberManager.GetInstance().delete(id); 1144 } 1145 /** 1146 * add a new subscriber. 1147 * The subscriber must have a new (unused) id (@see MakeId() ) 1148 * for add() to succeed. 1149 * same as Map.prototype.set() 1150 * 1151 * @since 9 1152 */ 1153 static Add(newSubsriber) { 1154 return SubscriberManager.GetInstance().add(newSubsriber); 1155 } 1156 /** 1157 * Update recycle custom node element id. 1158 */ 1159 static UpdateRecycleElmtId(oldId, newId) { 1160 return SubscriberManager.GetInstance().updateRecycleElmtId(oldId, newId); 1161 } 1162 /** 1163 * 1164 * @returns a globally unique id to be assigned to a IPropertySubscriber objet 1165 * Use MakeId() to assign a IPropertySubscriber object an id before calling @see add() . 1166 * 1167 * @since 9 1168 */ 1169 static MakeId() { 1170 return SubscriberManager.GetInstance().makeId(); 1171 } 1172 /** 1173 * 1174 * @returns a global unique id for state variables. 1175 * Unlike MakeId, no need to get id from native side. 1176 * 1177 * @since 12 1178 */ 1179 static MakeStateVariableId() { 1180 return SubscriberManager.nextId_--; 1181 } 1182 /** 1183 * Check number of registered Subscriber / registered IDs. 1184 * @returns number of registered unique ids. 1185 * 1186 * @since 9 1187 */ 1188 static NumberOfSubscribers() { 1189 return SubscriberManager.GetInstance().numberOfSubscribers(); 1190 } 1191 /** 1192 * 1193 * internal (non-SDK) methods below 1194 * 1195 */ 1196 /** 1197 * Get singleton, create it on first call 1198 * @returns SubscriberManager singleton 1199 * 1200 * internal function 1201 * This function will be removed soon, use static functions instead! 1202 * Note: Fnction gets used by transpiler output for both full update and partial update 1203 */ 1204 static Get() { 1205 if (!SubscriberManager.instance_) { 1206 SubscriberManager.instance_ = new SubscriberManager(); 1207 } 1208 return SubscriberManager.instance_; 1209 } 1210 /** 1211 * Get singleton, create it on first call 1212 * @returns SubscriberManager singleton 1213 * 1214 * internal function 1215 */ 1216 static GetInstance() { 1217 if (!SubscriberManager.instance_) { 1218 SubscriberManager.instance_ = new SubscriberManager(); 1219 } 1220 return SubscriberManager.instance_; 1221 } 1222 /** 1223 * for debug purposes dump all known subscriber's info to comsole 1224 * 1225 * not a public / sdk function 1226 */ 1227 static DumpSubscriberInfo() { 1228 SubscriberManager.GetInstance().dumpSubscriberInfo(); 1229 } 1230 /** 1231 * not a public / sdk function 1232 * @see Has 1233 */ 1234 has(id) { 1235 return this.subscriberById_.has(id); 1236 } 1237 /** 1238 * not a public / sdk function 1239 * @see Get 1240 */ 1241 get(id) { 1242 return this.subscriberById_.get(id); 1243 } 1244 /** 1245 * not a public / sdk function 1246 * @see Delete 1247 */ 1248 delete(id) { 1249 if (!this.has(id)) { 1250 stateMgmtConsole.warn(`SubscriberManager.delete unknown id ${id} `); 1251 return false; 1252 } 1253 return this.subscriberById_.delete(id); 1254 } 1255 /** 1256 * not a public / sdk function 1257 * @see Add 1258 */ 1259 add(newSubsriber) { 1260 if (this.has(newSubsriber.id__())) { 1261 return false; 1262 } 1263 this.subscriberById_.set(newSubsriber.id__(), newSubsriber); 1264 return true; 1265 } 1266 updateRecycleElmtId(oldId, newId) { 1267 if (!this.has(oldId)) { 1268 return false; 1269 } 1270 const subscriber = this.get(oldId); 1271 this.subscriberById_.delete(oldId); 1272 this.subscriberById_.set(newId, subscriber); 1273 return true; 1274 } 1275 /** 1276 * Method for testing purposes 1277 * @returns number of subscribers 1278 * 1279 * not a public / sdk function 1280 */ 1281 numberOfSubscribers() { 1282 return this.subscriberById_.size; 1283 } 1284 /** 1285 * for debug purposes dump all known subscriber's info to comsole 1286 * 1287 * not a public / sdk function 1288 */ 1289 dumpSubscriberInfo() { 1290 1291 for (let [id, subscriber] of this.subscriberById_) { 1292 1293 } 1294 1295 } 1296 /** 1297 * 1298 * @returns a globally unique id to be assigned to a Subscriber 1299 */ 1300 makeId() { 1301 return ViewStackProcessor.MakeUniqueId(); 1302 } 1303} 1304SubscriberManager.nextId_ = 0; 1305/* 1306 * Copyright (c) 2022 Huawei Device Co., Ltd. 1307 * Licensed under the Apache License, Version 2.0 (the "License"); 1308 * you may not use this file except in compliance with the License. 1309 * You may obtain a copy of the License at 1310 * 1311 * http://www.apache.org/licenses/LICENSE-2.0 1312 * 1313 * Unless required by applicable law or agreed to in writing, software 1314 * distributed under the License is distributed on an "AS IS" BASIS, 1315 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1316 * See the License for the specific language governing permissions and 1317 * limitations under the License. 1318 */ 1319/** 1320 * 1321 * SubscribedAbstractProperty is base class of ObservedPropertyAbstract 1322 * and includes these 3 functions that are part of the SDK. 1323 * 1324 * SubscribedAbstractProperty<T> is the return type of 1325 * - AppStorage static functions Link(), Prop(), SetAndLink(), and SetAndProp() 1326 * - LocalStorage methods link(), prop(), setAndLink(), and setAndProp() 1327 * 1328 * 'T' can be boolean, string, number or custom class. 1329 * 1330 * Main functions 1331 * @see get() reads the linked AppStorage/LocalStorage property value, 1332 * @see set(newValue) write a new value to the synched AppStorage/LocalStorage property value 1333 * @see aboutToBeDeleted() ends the sync relationship with the AppStorage/LocalStorage property 1334 * The app must call this function before the SubscribedAbstractProperty<T> object 1335 * goes out of scope. 1336 * 1337 * @since 7 1338*/ 1339class SubscribedAbstractProperty { 1340} 1341/* 1342 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 1343 * Licensed under the Apache License, Version 2.0 (the "License"); 1344 * you may not use this file except in compliance with the License. 1345 * You may obtain a copy of the License at 1346 * 1347 * http://www.apache.org/licenses/LICENSE-2.0 1348 * 1349 * Unless required by applicable law or agreed to in writing, software 1350 * distributed under the License is distributed on an "AS IS" BASIS, 1351 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1352 * See the License for the specific language governing permissions and 1353 * limitations under the License. 1354 */ 1355/** 1356 * 1357 * SubscribableAbstract 1358 * 1359 * This class is part of the SDK. 1360 * @since 10 1361 * 1362 * SubscribableAbstract is an abstract class that manages subscribers 1363 * to value changes. These subscribers are the implementation of 1364 * @State, @Link, @Provide, @Consume decorated variables inside the 1365 * framework. Each using @State, @Link, etc., decorated variable in 1366 * a @Component will make its own subscription. When the component 1367 * is created the subscription is added, and before the component 1368 * is deleted it unsubscribes 1369 * 1370 * An application may extend SubscribableAbstract for a custom class 1371 * that manages state data. @State, @Link, @Provide, @Consume 1372 * decorated variables can hold an Object that is instance of 1373 * SubscribableAbstract. 1374 * 1375 * About lifecycle: It is legal use for two @Components with two @State 1376 * decorated variables to share the same SubscribableAbstract object. 1377 * Each such decorated variable implementation makes its own 1378 * subscription to the SubscribableAbstract object. Hence, when both variables 1379 * have unsubscribed the SubscribableAbstract custom class may do its own 1380 * de-initialization, e.g. release held external resources. 1381 * 1382 * How to extend: 1383 * A subclass manages the get and set to one or several properties on its own. 1384 * The subclass needs to notify all relevant value changes to the framework for the 1385 * UI to be updated. Notification should only be given for class properties that 1386 * are used to generate the UI. 1387 * 1388 * A subclass must call super() in its constructor to let this base class 1389 * initialize itself. 1390 * 1391 * A subclass must call 'notifyPropertyHasChanged*(' after the relevant property 1392 * has changes. The framework will notify all dependent components to re-render. 1393 * 1394 * A sub-class may overwrite the 'addOwningProperty' function to add own 1395 * functionality, but it must call super.addowningOwningProperty(..). E.g. 1396 * the sub-class could connect to external resources upon the first subscriber. 1397 * 1398 * A sub-class may also overwrite the 'removeOwningProperty' function or 1399 * 'removeOwningPropertyById' function to add own functionality, 1400 * but it must call super.removeOwningProperty(..). 1401 * E.g. the sub-class could release held external resources upon loosing the 1402 * last subscriber. 1403 * 1404 */ 1405class SubscribableAbstract { 1406 /** 1407 * make sure to call super() from subclass constructor! 1408 * 1409 * @since 10 1410 */ 1411 constructor() { 1412 this.owningProperties_ = new Set(); 1413 1414 } 1415 /** 1416 * A subsclass must call this function whenever one of its properties has 1417 * changed that is used to construct the UI. 1418 * @param propName name of the change property 1419 * @param newValue the property value after the change 1420 * 1421 * @since 10 1422 */ 1423 notifyPropertyHasChanged(propName, newValue) { 1424 1425 this.owningProperties_.forEach((subscribedId) => { 1426 let owningProperty = SubscriberManager.Find(subscribedId); 1427 if (!owningProperty) { 1428 stateMgmtConsole.error(`SubscribableAbstract: notifyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 1429 return; 1430 } 1431 // PU code path 1432 if ('onTrackedObjectPropertyCompatModeHasChangedPU' in owningProperty) { 1433 owningProperty.onTrackedObjectPropertyCompatModeHasChangedPU(this, propName); 1434 } 1435 // FU code path 1436 if ('hasChanged' in owningProperty) { 1437 owningProperty.hasChanged(newValue); 1438 } 1439 if ('propertyHasChanged' in owningProperty) { 1440 owningProperty.propertyHasChanged(propName); 1441 } 1442 }); 1443 } 1444 /** 1445 * Provides the current number of subscribers. 1446 * Application may use this function to determine a shared object has no more remaining subscribers and can be deleted. 1447 * @returns number of current subscribers 1448 * 1449 * @since 10 1450 */ 1451 numberOfSubscribers() { 1452 return this.owningProperties_.size; 1453 } 1454 /** 1455 * Method used by the framework to add subscribing decorated variables 1456 * Subclass may overwrite this function but must call the function of the base 1457 * class from its own implementation. 1458 * @param subscriber new subscriber that implements ISinglePropertyChangeSubscriber 1459 * and/or IMultiPropertiesChangeSubscriber interfaces 1460 * 1461 * @since 10 1462 */ 1463 addOwningProperty(subscriber) { 1464 1465 this.owningProperties_.add(subscriber.id__()); 1466 } 1467 /** 1468 * Method used by the framework to unsubscribing decorated variables 1469 * Subclass may overwrite this function but must call the function of the base 1470 * class from its own implementation. 1471 * @param subscriber subscriber that implements ISinglePropertyChangeSubscriber 1472 * and/or IMultiPropertiesChangeSubscriber interfaces 1473 * 1474 * @since 10 1475 */ 1476 removeOwningProperty(property) { 1477 return this.removeOwningPropertyById(property.id__()); 1478 } 1479 /** 1480 * Same as @see removeOwningProperty() but by Subscriber id. 1481 * @param subscriberId 1482 * 1483 * framework internal function, not to be used by applications. 1484 */ 1485 removeOwningPropertyById(subscriberId) { 1486 1487 this.owningProperties_.delete(subscriberId); 1488 } 1489 /** 1490 * flush all subscribers / owning properties 1491 * This is needed when copying a SubscribableAbstract object to the localObject or @prop / SynchedPropertyObjectOneWay 1492 * - shallowCopy: copies the _reference to original_ Set. Hence, we must not modify this Set but assign a new Set 1493 * - deepCopy also (deep-) copies this class' owningProperties_ Set, incl. the numbers it includes. Assigning a new Set fixes. 1494 * 1495 */ 1496 clearOwningProperties() { 1497 this.owningProperties_ = new Set(); 1498 } 1499} 1500/** 1501 * SubscribaleAbstract class with typo in its nam,e 1502 * 1503 * @depreciated, use SubscribableAbstract 1504 */ 1505class SubscribaleAbstract extends SubscribableAbstract { 1506} 1507/* 1508 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1509 * Licensed under the Apache License, Version 2.0 (the "License"); 1510 * you may not use this file except in compliance with the License. 1511 * You may obtain a copy of the License at 1512 * 1513 * http://www.apache.org/licenses/LICENSE-2.0 1514 * 1515 * Unless required by applicable law or agreed to in writing, software 1516 * distributed under the License is distributed on an "AS IS" BASIS, 1517 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1518 * See the License for the specific language governing permissions and 1519 * limitations under the License. 1520 */ 1521/** 1522 * file version 1523 * 1524 * To indicate the file formate 1525 * 1526 */ 1527var ObjectVersion; 1528(function (ObjectVersion) { 1529 ObjectVersion[ObjectVersion["NewVersion"] = 0] = "NewVersion"; 1530 ObjectVersion[ObjectVersion["CompatibleVersion"] = 1] = "CompatibleVersion"; 1531 ObjectVersion[ObjectVersion["Default"] = 2] = "Default"; 1532})(ObjectVersion || (ObjectVersion = {})); 1533class MapInfo { 1534 constructor(mapReplacer, keyToValue) { 1535 this.mapReplacer = mapReplacer; 1536 this.keyToValue = keyToValue; 1537 } 1538 // Check if the given object is of type MapInfo 1539 static isObject(obj) { 1540 const typedObject = obj; 1541 if ('mapReplacer' in typedObject && typedObject.mapReplacer === MapInfo.replacer) { 1542 return ObjectVersion.NewVersion; 1543 } 1544 if ('mapReplacer' in typedObject && typedObject.mapReplacer === MapInfo.replacerCompatible) { 1545 return ObjectVersion.CompatibleVersion; 1546 } 1547 return ObjectVersion.Default; 1548 } 1549 // Convert Map to Object 1550 static toObject(map) { 1551 let mapItems = []; 1552 map.forEach((val, key) => { 1553 mapItems.push({ key: key, value: val }); 1554 }); 1555 return new MapInfo(MapInfo.replacer, mapItems); 1556 } 1557 // Convert Object to Map 1558 static toMap(obj) { 1559 return new Map(obj.keyToValue.map((item) => [item.key, item.value])); 1560 } 1561 static toMapCompatible(obj) { 1562 return new Map(obj.keys.map((key, i) => [key, obj.values[i]])); 1563 } 1564} 1565MapInfo.replacer = '_____map_replacer__'; 1566MapInfo.replacerCompatible = 'ace_engine_state_mgmt_map_replacer'; 1567/** 1568 * SetInfo 1569 * 1570 * Helper class to persist Set in Persistent storage 1571 * 1572 */ 1573class SetInfo { 1574 constructor(setReplacer, values) { 1575 this.setReplacer = setReplacer; 1576 this.values = values; 1577 } 1578 // Check if the given object is of type SetInfo 1579 static isObject(obj) { 1580 const typedObject = obj; 1581 if ('setReplacer' in typedObject && 1582 (typedObject.setReplacer === SetInfo.replacer || typedObject.setReplacer === SetInfo.replacerCompatible)) { 1583 return true; 1584 } 1585 return false; 1586 } 1587 // Convert Set to Object 1588 static toObject(set) { 1589 const values = Array.from(set.values()); 1590 return new SetInfo(SetInfo.replacer, values); 1591 } 1592 // Convert Object to Set 1593 static toSet(obj) { 1594 return new Set(obj.values); 1595 } 1596} 1597SetInfo.replacer = '_____set_replacer__'; 1598SetInfo.replacerCompatible = "ace_engine_state_mgmt_set_replacer"; 1599/** 1600 * DateInfo 1601 * 1602 * Helper class to persist Date in Persistent storage 1603 * 1604 */ 1605class DateInfo { 1606 constructor(dateReplacer, date) { 1607 this.dateReplacer = dateReplacer; 1608 this.date = date; 1609 } 1610 // Check if the given object is of type DateInfo 1611 static isObject(obj) { 1612 const typedObject = obj; 1613 if ('dateReplacer' in typedObject && 1614 (typedObject.dateReplacer === DateInfo.replacer || typedObject.dateReplacer === DateInfo.replacerCompatible)) { 1615 return true; 1616 } 1617 return false; 1618 } 1619 // Convert Date to Object 1620 static toObject(date) { 1621 return new DateInfo(DateInfo.replacer, date.toISOString()); 1622 } 1623 // Convert Object to Date 1624 static toDate(obj) { 1625 return new Date(obj.date); 1626 } 1627} 1628DateInfo.replacer = '_____date_replacer__'; 1629DateInfo.replacerCompatible = "ace_engine_state_mgmt_date_replacer"; 1630/** 1631 * PersistentStorage 1632 * 1633 * Keeps current values of select AppStorage property properties persisted to file. 1634 * 1635 * since 9 1636 */ 1637class PersistentStorage { 1638 /** 1639 * all following methods are framework internal 1640 */ 1641 constructor() { 1642 this.links_ = new Map(); 1643 this.id_ = SubscriberManager.MakeId(); 1644 SubscriberManager.Add(this); 1645 } 1646 /** 1647 * 1648 * @param storage method to be used by the framework to set the backend 1649 * this is to be done during startup 1650 * 1651 * internal function, not part of the SDK 1652 * 1653 */ 1654 static configureBackend(storage) { 1655 PersistentStorage.storage_ = storage; 1656 } 1657 /** 1658 * private, use static functions! 1659 */ 1660 static getOrCreate() { 1661 if (PersistentStorage.instance_) { 1662 // already initialized 1663 return PersistentStorage.instance_; 1664 } 1665 PersistentStorage.instance_ = new PersistentStorage(); 1666 return PersistentStorage.instance_; 1667 } 1668 /** 1669 * 1670 * internal function, not part of the SDK 1671 */ 1672 static aboutToBeDeleted() { 1673 if (!PersistentStorage.instance_) { 1674 return; 1675 } 1676 PersistentStorage.getOrCreate().aboutToBeDeleted(); 1677 PersistentStorage.instance_ = undefined; 1678 } 1679 /** 1680 * Add property 'key' to AppStorage properties whose current value will be 1681 * persistent. 1682 * If AppStorage does not include this property it will be added and initializes 1683 * with given value 1684 * 1685 * @since 10 1686 * 1687 * @param key property name 1688 * @param defaultValue If AppStorage does not include this property it will be initialized with this value 1689 * 1690 */ 1691 static persistProp(key, defaultValue) { 1692 PersistentStorage.getOrCreate().persistProp(key, defaultValue); 1693 } 1694 /** 1695 * @see persistProp 1696 * @deprecated 1697 */ 1698 static PersistProp(key, defaultValue) { 1699 PersistentStorage.getOrCreate().persistProp(key, defaultValue); 1700 } 1701 /** 1702 * Reverse of @see persistProp 1703 * @param key no longer persist the property named key 1704 * 1705 * @since 10 1706 */ 1707 static deleteProp(key) { 1708 PersistentStorage.getOrCreate().deleteProp(key); 1709 } 1710 /** 1711 * @see deleteProp 1712 * @deprecated 1713 */ 1714 static DeleteProp(key) { 1715 PersistentStorage.getOrCreate().deleteProp(key); 1716 } 1717 /** 1718 * Persist given AppStorage properties with given names. 1719 * If a property does not exist in AppStorage, add it and initialize it with given value 1720 * works as @see persistProp for multiple properties. 1721 * 1722 * @param properties 1723 * 1724 * @since 10 1725 * 1726 */ 1727 static persistProps(properties) { 1728 PersistentStorage.getOrCreate().persistProps(properties); 1729 } 1730 /** 1731 * @see persistProps 1732 * @deprecated 1733 */ 1734 static PersistProps(properties) { 1735 PersistentStorage.getOrCreate().persistProps(properties); 1736 } 1737 /** 1738 * Inform persisted AppStorage property names 1739 * @returns array of AppStorage keys 1740 * 1741 * @since 10 1742 */ 1743 static keys() { 1744 let result = []; 1745 const it = PersistentStorage.getOrCreate().keys(); 1746 let val = it.next(); 1747 while (!val.done) { 1748 result.push(val.value); 1749 val = it.next(); 1750 } 1751 return result; 1752 } 1753 /** 1754 * @see keys 1755 * @deprecated 1756 */ 1757 static Keys() { 1758 return PersistentStorage.keys(); 1759 } 1760 /** 1761 * This methid offers a way to force writing the property value with given 1762 * key to persistent storage. 1763 * In the general case this is unnecessary as the framework observed changes 1764 * and triggers writing to disk by itself. For nested objects (e.g. array of 1765 * objects) however changes of a property of a property as not observed. This 1766 * is the case where the application needs to signal to the framework. 1767 * 1768 * @param key property that has changed 1769 * 1770 * @since 10 1771 * 1772 */ 1773 static notifyHasChanged(propName) { 1774 1775 PersistentStorage.getOrCreate().writeToPersistentStorage(propName, PersistentStorage.getOrCreate().links_.get(propName).get()); 1776 } 1777 /** 1778 * @see notifyHasChanged 1779 * @deprecated 1780 */ 1781 static NotifyHasChanged(propName) { 1782 1783 PersistentStorage.getOrCreate().writeToPersistentStorage(propName, PersistentStorage.getOrCreate().links_.get(propName).get()); 1784 } 1785 keys() { 1786 return this.links_.keys(); 1787 } 1788 persistProp(propName, defaultValue) { 1789 if (this.persistProp1(propName, defaultValue)) { 1790 // persist new prop 1791 1792 this.writeToPersistentStorage(propName, this.links_.get(propName).get()); 1793 } 1794 } 1795 // helper function to persist a property 1796 // does everything except writing prop to disk 1797 persistProp1(propName, defaultValue) { 1798 1799 if (defaultValue == null && !Utils.isApiVersionEQAbove(12)) { 1800 stateMgmtConsole.error(`PersistentStorage: persistProp for ${propName} called with 'null' or 'undefined' default value!`); 1801 return false; 1802 } 1803 if (this.links_.get(propName)) { 1804 stateMgmtConsole.warn(`PersistentStorage: persistProp: ${propName} is already persisted`); 1805 return false; 1806 } 1807 let link = AppStorage.link(propName, this); 1808 if (link) { 1809 1810 this.links_.set(propName, link); 1811 } 1812 else { 1813 let returnValue; 1814 if (!PersistentStorage.storage_.has(propName)) { 1815 1816 returnValue = defaultValue; 1817 } 1818 else { 1819 returnValue = this.readFromPersistentStorage(propName); 1820 } 1821 link = AppStorage.setAndLink(propName, returnValue, this); 1822 if (link === undefined) { 1823 1824 return false; 1825 } 1826 this.links_.set(propName, link); 1827 1828 } 1829 return true; 1830 } 1831 persistProps(properties) { 1832 properties.forEach(property => this.persistProp1(property.key, property.defaultValue)); 1833 this.write(); 1834 } 1835 deleteProp(propName) { 1836 let link = this.links_.get(propName); 1837 if (link) { 1838 link.aboutToBeDeleted(); 1839 this.links_.delete(propName); 1840 PersistentStorage.storage_.delete(propName); 1841 1842 } 1843 else { 1844 stateMgmtConsole.warn(`PersistentStorage: '${propName}' is not a persisted property warning.`); 1845 } 1846 } 1847 write() { 1848 this.links_.forEach((link, propName, map) => { 1849 1850 this.writeToPersistentStorage(propName, link.get()); 1851 }); 1852 } 1853 // helper function to write to the persistent storage 1854 // any additional check and formatting can to be done here 1855 writeToPersistentStorage(propName, value) { 1856 if (value instanceof Map) { 1857 value = MapInfo.toObject(value); 1858 } 1859 else if (value instanceof Set) { 1860 value = SetInfo.toObject(value); 1861 } 1862 else if (value instanceof Date) { 1863 value = DateInfo.toObject(value); 1864 } 1865 PersistentStorage.storage_.set(propName, value); 1866 } 1867 // helper function to read from the persistent storage 1868 // any additional check and formatting can to be done here 1869 readFromPersistentStorage(propName) { 1870 let newValue = PersistentStorage.storage_.get(propName); 1871 if (newValue instanceof Object) { 1872 if (MapInfo.isObject(newValue) === ObjectVersion.NewVersion) { 1873 newValue = MapInfo.toMap(newValue); 1874 } 1875 else if (MapInfo.isObject(newValue) === ObjectVersion.CompatibleVersion) { 1876 newValue = MapInfo.toMapCompatible(newValue); 1877 } 1878 else if (SetInfo.isObject(newValue)) { 1879 newValue = SetInfo.toSet(newValue); 1880 } 1881 else if (DateInfo.isObject(newValue)) { 1882 newValue = DateInfo.toDate(newValue); 1883 } 1884 } 1885 return newValue; 1886 } 1887 // FU code path method 1888 propertyHasChanged(info) { 1889 1890 this.write(); 1891 } 1892 // PU code path method 1893 syncPeerHasChanged(eventSource) { 1894 1895 this.write(); 1896 } 1897 // public required by the interface, use the static method instead! 1898 aboutToBeDeleted() { 1899 1900 this.links_.forEach((val, key, map) => { 1901 1902 val.aboutToBeDeleted(); 1903 }); 1904 this.links_.clear(); 1905 SubscriberManager.Delete(this.id__()); 1906 PersistentStorage.storage_.clear(); 1907 } 1908 id__() { 1909 return this.id_; 1910 } 1911} 1912PersistentStorage.instance_ = undefined; 1913; 1914/* 1915 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1916 * Licensed under the Apache License, Version 2.0 (the "License"); 1917 * you may not use this file except in compliance with the License. 1918 * You may obtain a copy of the License at 1919 * 1920 * http://www.apache.org/licenses/LICENSE-2.0 1921 * 1922 * Unless required by applicable law or agreed to in writing, software 1923 * distributed under the License is distributed on an "AS IS" BASIS, 1924 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1925 * See the License for the specific language governing permissions and 1926 * limitations under the License. 1927 */ 1928/** 1929 * Environment 1930 * 1931 * Injects device properties ("environment") into AppStorage 1932 * 1933 */ 1934class Environment { 1935 constructor() { 1936 this.props_ = new Map(); 1937 Environment.envBackend_.onValueChanged(this.onValueChanged.bind(this)); 1938 } 1939 static getOrCreate() { 1940 if (Environment.instance_) { 1941 // already initialized 1942 return Environment.instance_; 1943 } 1944 Environment.instance_ = new Environment(); 1945 return Environment.instance_; 1946 } 1947 static configureBackend(envBackend) { 1948 Environment.envBackend_ = envBackend; 1949 } 1950 /** 1951 * @see configureBackend 1952 * @deprecated 1953 */ 1954 static ConfigureBackend(envBackend) { 1955 Environment.envBackend_ = envBackend; 1956 } 1957 static aboutToBeDeleted() { 1958 if (!Environment.instance_) { 1959 return; 1960 } 1961 Environment.getOrCreate().aboutToBeDeleted(); 1962 Environment.instance_ = undefined; 1963 } 1964 /** 1965 * @see aboutToBeDeleted 1966 * @deprecated 1967 */ 1968 static AboutToBeDeleted() { 1969 Environment.aboutToBeDeleted(); 1970 } 1971 static envProp(key, value) { 1972 return Environment.getOrCreate().envProp(key, value); 1973 } 1974 /** 1975 * @see envProp 1976 * @deprecated 1977 */ 1978 static EnvProp(key, value) { 1979 return Environment.getOrCreate().envProp(key, value); 1980 } 1981 static envProps(props) { 1982 Environment.getOrCreate().envProps(props); 1983 } 1984 /** 1985 * @see envProps 1986 * @deprecated 1987 */ 1988 static EnvProps(props) { 1989 Environment.getOrCreate().envProps(props); 1990 } 1991 static keys() { 1992 return Environment.getOrCreate().keys(); 1993 } 1994 /** 1995 * @see keys 1996 * @deprecated 1997 */ 1998 static Keys() { 1999 return Environment.getOrCreate().keys(); 2000 } 2001 envProp(key, value) { 2002 let prop = AppStorage.prop(key); 2003 if (prop) { 2004 stateMgmtConsole.warn(`Environment: envProp '${key}': Property already exists in AppStorage. Not using environment property.`); 2005 return false; 2006 } 2007 let tmp; 2008 switch (key) { 2009 case 'accessibilityEnabled': 2010 tmp = Environment.envBackend_.getAccessibilityEnabled(); 2011 break; 2012 case 'colorMode': 2013 tmp = Environment.envBackend_.getColorMode(); 2014 break; 2015 case 'fontScale': 2016 tmp = Environment.envBackend_.getFontScale(); 2017 break; 2018 case 'fontWeightScale': 2019 tmp = Environment.envBackend_.getFontWeightScale().toFixed(2); 2020 break; 2021 case 'layoutDirection': 2022 tmp = Environment.envBackend_.getLayoutDirection(); 2023 break; 2024 case 'languageCode': 2025 tmp = Environment.envBackend_.getLanguageCode(); 2026 break; 2027 default: 2028 tmp = value; 2029 } 2030 if (!tmp && tmp !== 0) { 2031 tmp = value; 2032 } 2033 prop = AppStorage.setAndProp(key, tmp); 2034 if (!prop) { 2035 stateMgmtConsole.warn(`Environment: envProp '${key}': AppStorage setAndProp failed.`); 2036 return false; 2037 } 2038 this.props_.set(key, prop); 2039 2040 return true; 2041 } 2042 envProps(properties) { 2043 properties.forEach(property => { 2044 this.envProp(property.key, property.defaultValue); 2045 2046 }); 2047 } 2048 keys() { 2049 let result = []; 2050 const it = this.props_.keys(); 2051 let val = it.next(); 2052 while (!val.done) { 2053 result.push(val.value); 2054 val = it.next(); 2055 } 2056 return result; 2057 } 2058 onValueChanged(key, value) { 2059 let ok = AppStorage.set(key, value); 2060 if (ok) { 2061 2062 } 2063 else { 2064 stateMgmtConsole.warn(`Environment: onValueChanged: error changing ${key}! See results above.`); 2065 } 2066 } 2067 aboutToBeDeleted() { 2068 this.props_.forEach((val, key, map) => { 2069 val.aboutToBeDeleted(); 2070 AppStorage.delete(key); 2071 }); 2072 } 2073} 2074Environment.instance_ = undefined; 2075/* 2076 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 2077 * Licensed under the Apache License, Version 2.0 (the "License"); 2078 * you may not use this file except in compliance with the License. 2079 * You may obtain a copy of the License at 2080 * 2081 * http://www.apache.org/licenses/LICENSE-2.0 2082 * 2083 * Unless required by applicable law or agreed to in writing, software 2084 * distributed under the License is distributed on an "AS IS" BASIS, 2085 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2086 * See the License for the specific language governing permissions and 2087 * limitations under the License. 2088 */ 2089/* 2090 global function Repeat() 2091 returns an object that retains the state of Repeat instance between render calls 2092 exec attribute functions on this instance. 2093*/ 2094const Repeat = (arr, owningView) => { 2095 if (!owningView) { 2096 throw new Error("Transpilation error, Repeat lacks 2nd parameter owningView"); 2097 } 2098 return owningView.__mkRepeatAPI(arr); 2099}; 2100/* 2101 * Copyright (c) 2023 Huawei Device Co., Ltd. 2102 * Licensed under the Apache License, Version 2.0 (the "License"); 2103 * you may not use this file except in compliance with the License. 2104 * You may obtain a copy of the License at 2105 * 2106 * http://www.apache.org/licenses/LICENSE-2.0 2107 * 2108 * Unless required by applicable law or agreed to in writing, software 2109 * distributed under the License is distributed on an "AS IS" BASIS, 2110 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2111 * See the License for the specific language governing permissions and 2112 * limitations under the License. 2113 */ 2114/** 2115 * state mgmt library uses its own class for logging 2116* allows to remap separately from other use of aceConsole 2117* 2118* everything in this file is framework internal 2119*/ 2120var LogTag; 2121(function (LogTag) { 2122 LogTag[LogTag["STATE_MGMT"] = 0] = "STATE_MGMT"; 2123})(LogTag || (LogTag = {})); 2124class stateMgmtConsole { 2125 static log(...args) { 2126 aceConsole.log(LogTag.STATE_MGMT, ...args); 2127 } 2128 static debug(...args) { 2129 aceConsole.debug(LogTag.STATE_MGMT, ...args); 2130 } 2131 static info(...args) { 2132 aceConsole.info(LogTag.STATE_MGMT, ...args); 2133 } 2134 static warn(...args) { 2135 aceConsole.warn(LogTag.STATE_MGMT, ...args); 2136 } 2137 static error(...args) { 2138 aceConsole.error(LogTag.STATE_MGMT, ...args); 2139 } 2140 static propertyAccess(...args) { 2141 // enable for fine grain debugging variable observation 2142 // aceConsole.error(...args) 2143 } 2144 static applicationError(...args) { 2145 aceConsole.error(LogTag.STATE_MGMT, `FIX THIS APPLICATION ERROR: `, ...args); 2146 } 2147 static applicationWarn(...args) { 2148 aceConsole.warn(LogTag.STATE_MGMT, ...args); 2149 } 2150 static featureCombinationError(msg) { 2151 aceConsole.warn(LogTag.STATE_MGMT, msg); 2152 } 2153} 2154class stateMgmtTrace { 2155 static scopedTrace(codeBlock, arg1, ...args) { 2156 aceTrace.begin(arg1, ...args); 2157 let result = codeBlock(); 2158 aceTrace.end(); 2159 return result; 2160 } 2161} 2162class errorReport { 2163 static varValueCheckFailed(params) { 2164 let msg = `@Component '${params.customComponent}': Illegal variable value error with decorated variable ${params.variableDeco} '${params.variableName}': `; 2165 msg += `failed validation: '${params.expectedType}`; 2166 try { 2167 msg += `, attempt to assign value type: '${typeof params.value}'`; 2168 msg += `, value: '${JSON.stringify(params.value, null, 4)}'`; 2169 } 2170 catch (e) { } 2171 msg += '!'; 2172 stateMgmtConsole.applicationError(msg); 2173 throw new TypeError(msg); 2174 } 2175 static varObservationFailed(params) { 2176 let msg = `@Component '${params.customComponent}': decorated variable ${params.variableDeco} '${params.variableName}': `; 2177 msg += `its class is neither decorated with '@Observed' nor it is an instance of 'SubscribableAbstract'`; 2178 try { 2179 msg += `, attempt to assign value type: '${typeof params.value}'`; 2180 msg += `, value: '${JSON.stringify(params.value, null, 4)}'`; 2181 } 2182 catch (e) { } 2183 msg += '!'; 2184 throw new TypeError(msg); 2185 } 2186} 2187/* 2188 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 2189 * Licensed under the Apache License, Version 2.0 (the "License"); 2190 * you may not use this file except in compliance with the License. 2191 * You may obtain a copy of the License at 2192 * 2193 * http://www.apache.org/licenses/LICENSE-2.0 2194 * 2195 * Unless required by applicable law or agreed to in writing, software 2196 * distributed under the License is distributed on an "AS IS" BASIS, 2197 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2198 * See the License for the specific language governing permissions and 2199 * limitations under the License. 2200 */ 2201/** 2202* @Observed class decorator 2203* 2204* usage: 2205* @Observed class ClassA { ... } 2206* 2207* Causes every instance of decorated clss to be automatically wrapped inside an ObservedObject. 2208* 2209* Implemented by extending the decroaetd class by class named 'ObservableObjectClass'. 2210* 2211* It is permisstable to decorate the base and the extended class like thisNote: I 2212* @Observed class ClassA { ...} 2213* @Observed class ClassB extends ClassA { ... } 2214* and use 2215* a = new ClassA(); 2216* b = new ClassB(); 2217* Only one ES6 Proxy is added. 2218* 2219* 2220* Take note the decorator implementation extends the prototype chain. 2221* 2222* The prototype chain of a in above example is 2223* - ObservableObjectClass prototype 2224* - ClassA prototype 2225* - Object prototype 2226* 2227* Snd the prototype chain of b is 2228* - ObservableObjectClass prototype 2229* - ClassB prototype 2230* - ObservableObjectClass prototype 2231* - ClassA prototype 2232* - Object prototype 2233* 2234* The @Observed decorator is public, part of the SDK, starting from API 9. 2235* 2236*/ 2237// define just once to get just one Symbol 2238const __IS_OBSERVED_PROXIED = Symbol('_____is_observed_proxied__'); 2239function Observed(BaseClass) { 2240 2241 // prevent use of V1 @Track inside V2 @ObservedV2 class 2242 if (BaseClass.prototype && Reflect.has(BaseClass.prototype, ObserveV2.SYMBOL_REFS)) { 2243 const error = `'@Observed class ${BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name}': invalid use of V1 @Track decorator inside V2 @ObservedV2 class. Need to fix class definition to use @Track.`; 2244 stateMgmtConsole.error(error); 2245 throw new Error(error); 2246 } 2247 return class extends BaseClass { 2248 constructor(...args) { 2249 super(...args); 2250 2251 ConfigureStateMgmt.instance.usingPUObservedTrack(`@Observed`, BaseClass.name); 2252 let isProxied = Reflect.has(this, __IS_OBSERVED_PROXIED); 2253 Object.defineProperty(this, __IS_OBSERVED_PROXIED, { 2254 value: true, 2255 enumerable: false, 2256 configurable: false, 2257 writable: false 2258 }); 2259 if (isProxied) { 2260 2261 return this; 2262 } 2263 else { 2264 2265 return ObservedObject.createNewInternal(this, undefined); 2266 } 2267 } 2268 }; 2269} 2270class SubscribableHandler { 2271 constructor(owningProperty) { 2272 this.owningProperties_ = new Set(); 2273 if (owningProperty) { 2274 this.addOwningProperty(owningProperty); 2275 } 2276 2277 } 2278 isPropertyTracked(obj, property) { 2279 return Reflect.has(obj, `___TRACKED_${property}`) || 2280 property === TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY || 2281 property === TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY; 2282 } 2283 addOwningProperty(subscriber) { 2284 if (subscriber) { 2285 2286 this.owningProperties_.add(subscriber.id__()); 2287 } 2288 else { 2289 stateMgmtConsole.warn(`SubscribableHandler: addOwningProperty: undefined subscriber.`); 2290 } 2291 } 2292 /* 2293 the inverse function of createOneWaySync or createTwoWaySync 2294 */ 2295 removeOwningProperty(property) { 2296 return this.removeOwningPropertyById(property.id__()); 2297 } 2298 removeOwningPropertyById(subscriberId) { 2299 2300 this.owningProperties_.delete(subscriberId); 2301 } 2302 notifyObjectPropertyHasChanged(propName, newValue) { 2303 2304 this.owningProperties_.forEach((subscribedId) => { 2305 const owningProperty = SubscriberManager.Find(subscribedId); 2306 if (!owningProperty) { 2307 stateMgmtConsole.warn(`SubscribableHandler: notifyObjectPropertyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 2308 return; 2309 } 2310 // PU code path 2311 if ('onTrackedObjectPropertyCompatModeHasChangedPU' in owningProperty) { 2312 owningProperty.onTrackedObjectPropertyCompatModeHasChangedPU(this, propName); 2313 return; 2314 } 2315 // FU code path 2316 if ('hasChanged' in owningProperty) { 2317 owningProperty.hasChanged(newValue); 2318 } 2319 if ('propertyHasChanged' in owningProperty) { 2320 owningProperty.propertyHasChanged(propName); 2321 } 2322 }); 2323 } 2324 notifyTrackedObjectPropertyHasChanged(propName) { 2325 2326 this.owningProperties_.forEach((subscribedId) => { 2327 const owningProperty = SubscriberManager.Find(subscribedId); 2328 if (owningProperty && 'onTrackedObjectPropertyHasChangedPU' in owningProperty) { 2329 // PU code path with observed object property change tracking optimization 2330 owningProperty.onTrackedObjectPropertyHasChangedPU(this, propName); 2331 } 2332 else { 2333 stateMgmtConsole.warn(`SubscribableHandler: notifyTrackedObjectPropertyHasChanged: subscriber.'${subscribedId}' lacks method 'trackedObjectPropertyHasChangedPU' internal error!.`); 2334 } 2335 }); 2336 // no need to support FU code path when app uses @Track 2337 } 2338 has(target, property) { 2339 2340 return (property === ObservedObject.__IS_OBSERVED_OBJECT) ? true : Reflect.has(target, property); 2341 } 2342 get(target, property, receiver) { 2343 switch (property) { 2344 case ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT: 2345 return target; 2346 break; 2347 case SubscribableHandler.COUNT_SUBSCRIBERS: 2348 return this.owningProperties_.size; 2349 break; 2350 case ObserveV2.SYMBOL_REFS: 2351 case ObserveV2.V2_DECO_META: 2352 case ObserveV2.SYMBOL_MAKE_OBSERVED: 2353 // return result unmonitored 2354 return Reflect.get(target, property, receiver); 2355 break; 2356 default: 2357 const result = Reflect.get(target, property, receiver); 2358 let propertyStr = String(property); 2359 if (this.readCbFunc_ && typeof result !== 'function' && this.obSelf_ !== undefined) { 2360 let isTracked = this.isPropertyTracked(target, propertyStr); 2361 2362 this.readCbFunc_.call(this.obSelf_, receiver, propertyStr, isTracked); 2363 } 2364 else { 2365 // result is function or in compatibility mode (in compat mode cbFunc will never be set) 2366 2367 } 2368 return result; 2369 break; 2370 } 2371 } 2372 set(target, property, newValue) { 2373 switch (property) { 2374 case SubscribableHandler.SUBSCRIBE: 2375 // assignment obsObj[SubscribableHandler.SUBSCRIBE] = subscriber 2376 this.addOwningProperty(newValue); 2377 return true; 2378 break; 2379 case SubscribableHandler.UNSUBSCRIBE: 2380 // assignment obsObj[SubscribableHandler.UNSUBSCRIBE] = subscriber 2381 this.removeOwningProperty(newValue); 2382 return true; 2383 break; 2384 case SubscribableHandler.SET_ONREAD_CB: 2385 // assignment obsObj[SubscribableHandler.SET_ONREAD_CB] = readCallbackFunc 2386 2387 this.readCbFunc_ = TrackedObject.isCompatibilityMode(target) ? undefined : newValue; 2388 return true; 2389 break; 2390 case SubscribableHandler.RAW_THIS: 2391 this.obSelf_ = TrackedObject.isCompatibilityMode(target) ? undefined : newValue; 2392 return true; 2393 break; 2394 default: 2395 // this is added for stability test: Reflect.get target is not object 2396 try { 2397 if (Reflect.get(target, property) === newValue) { 2398 return true; 2399 } 2400 } 2401 catch (error) { 2402 ArkTools.print('SubscribableHandler: set', target); 2403 stateMgmtConsole.error(`An error occurred in SubscribableHandler set, target type is: ${typeof target}, ${error.message}`); 2404 throw error; 2405 } 2406 Reflect.set(target, property, newValue); 2407 const propString = String(property); 2408 if (TrackedObject.isCompatibilityMode(target)) { 2409 2410 this.notifyObjectPropertyHasChanged(propString, newValue); 2411 } 2412 else { 2413 if (this.isPropertyTracked(target, propString)) { 2414 2415 this.notifyTrackedObjectPropertyHasChanged(propString); 2416 } 2417 else { 2418 2419 } 2420 } 2421 return true; 2422 break; 2423 } 2424 // unreachable 2425 return false; 2426 } 2427} 2428SubscribableHandler.SUBSCRIBE = Symbol('_____subscribe__'); 2429SubscribableHandler.UNSUBSCRIBE = Symbol('_____unsubscribe__'); 2430SubscribableHandler.COUNT_SUBSCRIBERS = Symbol('____count_subscribers__'); 2431SubscribableHandler.SET_ONREAD_CB = Symbol('_____set_onread_cb__'); 2432SubscribableHandler.RAW_THIS = Symbol('_____raw_this'); 2433class SubscribableMapSetHandler extends SubscribableHandler { 2434 constructor(owningProperty) { 2435 super(owningProperty); 2436 // In-place Map/Set modification functions 2437 this.mutatingFunctions = new Set([ 2438 /*Map functions*/ 2439 'set', 'clear', 'delete', 2440 /*Set functions*/ 2441 'add', 'clear', 'delete', 2442 ]); 2443 this.proxiedFunctions = new Set([ 2444 /*Map functions*/ 2445 'set', 2446 /*Set functions*/ 2447 'add' 2448 ]); 2449 } 2450 /** 2451 * Get trap for Map/Set type proxy 2452 * Functions that modify Map/Set in-place are intercepted and replaced with a function 2453 * that executes the original function and notifies the handler of a change. 2454 * @param target Original Map/Set object 2455 * @param property 2456 * @param receiver Proxied Map/Set object 2457 * @returns 2458 */ 2459 get(target, property, receiver) { 2460 if (property === ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT) { 2461 return target; 2462 } 2463 //receiver will fail for internal slot methods of Set and Map 2464 //So assign the target as receiver in this case. 2465 if (property === Symbol.iterator || property === 'size') { 2466 receiver = target; 2467 } 2468 let ret = super.get(target, property, receiver); 2469 if (ret && typeof ret === 'function') { 2470 const self = this; 2471 return function () { 2472 // execute original function with given arguments 2473 const result = ret.apply(target, arguments); 2474 if (self.mutatingFunctions.has(property)) { 2475 self.notifyObjectPropertyHasChanged(property, target); 2476 } 2477 // Only calls to inserting items can be chained, so returning the 'proxiedObject' 2478 // ensures that when chain calls also 2nd function call operates on the proxied object. 2479 // Otherwise return the original result of the function. 2480 return self.proxiedFunctions.has(property) ? receiver : result; 2481 }.bind(receiver); 2482 } 2483 return ret; 2484 } 2485} 2486class SubscribableDateHandler extends SubscribableHandler { 2487 constructor(owningProperty) { 2488 super(owningProperty); 2489 this.dateSetFunctions = new Set(['setFullYear', 'setMonth', 'setDate', 'setHours', 'setMinutes', 'setSeconds', 2490 'setMilliseconds', 'setTime', 'setUTCFullYear', 'setUTCMonth', 'setUTCDate', 'setUTCHours', 'setUTCMinutes', 2491 'setUTCSeconds', 'setUTCMilliseconds']); 2492 } 2493 /** 2494 * Get trap for Date type proxy 2495 * Functions that modify Date in-place are intercepted and replaced with a function 2496 * that executes the original function and notifies the handler of a change. 2497 * @param target Original Date object 2498 * @param property 2499 * @returns 2500 */ 2501 get(target, property) { 2502 let ret = super.get(target, property); 2503 if (typeof ret === 'function') { 2504 if (this.dateSetFunctions.has(property)) { 2505 const self = this; 2506 return function () { 2507 // execute original function with given arguments 2508 let result = ret.apply(this, arguments); 2509 self.notifyObjectPropertyHasChanged(property.toString(), this); 2510 return result; 2511 // bind 'this' to target inside the function 2512 }.bind(target); 2513 } 2514 return ret.bind(target); 2515 } 2516 return ret; 2517 } 2518} 2519class SubscribableArrayHandler extends SubscribableHandler { 2520 constructor(owningProperty) { 2521 super(owningProperty); 2522 // In-place array modification functions 2523 this.mutatingFunctions = new Set(['splice', 'copyWithin', 'fill', 'reverse', 'sort']); 2524 // 'splice' and 'pop' self modifies the array, returns deleted array items 2525 // means, alike other self-modifying functions, splice does not return the array itself. 2526 this.specialFunctions = new Set(['splice', 'pop']); 2527 } 2528 /** 2529 * Get trap for Array type proxy 2530 * Functions that modify Array in-place are intercepted and replaced with a function 2531 * that executes the original function and notifies the handler of a change. 2532 * @param target Original Array object 2533 * @param property 2534 * @param receiver Proxied Array object 2535 * @returns 2536 */ 2537 get(target, property, receiver) { 2538 if (property === ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT) { 2539 return target; 2540 } 2541 let ret = super.get(target, property, receiver); 2542 if (ret && typeof ret === 'function') { 2543 const self = this; 2544 const prop = property.toString(); 2545 if (self.mutatingFunctions.has(prop)) { 2546 return function () { 2547 const result = ret.apply(target, arguments); 2548 // prop is the function name here 2549 // and result is the function return value 2550 // function modifies none or more properties 2551 self.notifyObjectPropertyHasChanged(prop, self.specialFunctions.has(prop) ? target : result); 2552 // returning the 'receiver(proxied object)' ensures that when chain calls also 2nd function call 2553 // operates on the proxied object. 2554 return self.specialFunctions.has(prop) ? result : receiver; 2555 }.bind(receiver); 2556 } 2557 // binding the proxiedObject ensures that modifying functions like push() operate on the 2558 // proxied array and each array change is notified. 2559 return ret.bind(receiver); 2560 } 2561 return ret; 2562 } 2563} 2564class ExtendableProxy { 2565 constructor(obj, handler) { 2566 return new Proxy(obj, handler); 2567 } 2568} 2569class ObservedObject extends ExtendableProxy { 2570 /** 2571 * To create a new ObservableObject use CreateNew function 2572 * 2573 * constructor create a new ObservableObject and subscribe its owner to propertyHasChanged 2574 * notifications 2575 * @param obj raw Object, if obj is a ObservableOject throws an error 2576 * @param objectOwner 2577 */ 2578 constructor(obj, handler, objectOwningProperty) { 2579 super(obj, handler); 2580 if (ObservedObject.IsObservedObject(obj)) { 2581 stateMgmtConsole.error('ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already'); 2582 } 2583 if (objectOwningProperty) { 2584 this[SubscribableHandler.SUBSCRIBE] = objectOwningProperty; 2585 } 2586 } // end of constructor 2587 /** 2588 * Factory function for ObservedObjects / 2589 * wrapping of objects for proxying 2590 * 2591 * @param rawObject unproxied Object or ObservedObject 2592 * @param objOwner owner of this Object to sign uop for propertyChange 2593 * notifications 2594 * @returns the rawObject if object is already an ObservedObject, 2595 * otherwise the newly created ObservedObject 2596 */ 2597 static createNew(rawObject, owningProperty) { 2598 if (rawObject === null || rawObject === undefined) { 2599 stateMgmtConsole.error(`ObservedObject.CreateNew, input object must not be null or undefined.`); 2600 return rawObject; 2601 } 2602 if (ObservedObject.IsObservedObject(rawObject)) { 2603 ObservedObject.addOwningProperty(rawObject, owningProperty); 2604 return rawObject; 2605 } 2606 return ObservedObject.createNewInternal(rawObject, owningProperty); 2607 } 2608 static createNewInternal(rawObject, owningProperty) { 2609 let proxiedObject; 2610 if (rawObject instanceof Map || rawObject instanceof Set) { 2611 proxiedObject = new ObservedObject(rawObject, new SubscribableMapSetHandler(owningProperty), owningProperty); 2612 } 2613 else if (rawObject instanceof Date) { 2614 proxiedObject = new ObservedObject(rawObject, new SubscribableDateHandler(owningProperty), owningProperty); 2615 } 2616 else if (Array.isArray(rawObject)) { 2617 proxiedObject = new ObservedObject(rawObject, new SubscribableArrayHandler(owningProperty), owningProperty); 2618 } 2619 else { 2620 proxiedObject = new ObservedObject(rawObject, new SubscribableHandler(owningProperty), owningProperty); 2621 } 2622 return proxiedObject; 2623 } 2624 /* 2625 Return the unproxied object 'inside' the ObservedObject / the ES6 Proxy 2626 no set observation, no notification of changes! 2627 Use with caution, do not store any references 2628 */ 2629 static GetRawObject(obj) { 2630 return !ObservedObject.IsObservedObject(obj) ? obj : obj[ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT]; 2631 } 2632 /** 2633 * 2634 * @param obj anything 2635 * @returns true if the parameter is an Object wrpped with a ObservedObject 2636 * Note: Since ES6 Proying is transparent, 'instance of' will not work. Use 2637 * this static function instead. 2638 */ 2639 static IsObservedObject(obj) { 2640 return (obj && (typeof obj === 'object') && Reflect.has(obj, ObservedObject.__IS_OBSERVED_OBJECT)); 2641 } 2642 /** 2643 * add a subscriber to given ObservedObject 2644 * due to the proxy nature this static method approach needs to be used instead of a member 2645 * function 2646 * @param obj 2647 * @param subscriber 2648 * @returns false if given object is not an ObservedObject 2649 */ 2650 static addOwningProperty(obj, subscriber) { 2651 if (!ObservedObject.IsObservedObject(obj) || !subscriber) { 2652 return false; 2653 } 2654 obj[SubscribableHandler.SUBSCRIBE] = subscriber; 2655 return true; 2656 } 2657 /** 2658 * remove a subscriber to given ObservedObject 2659 * due to the proxy nature this static method approach needs to be used instead of a member 2660 * function 2661 * @param obj 2662 * @param subscriber 2663 * @returns false if given object is not an ObservedObject 2664 */ 2665 static removeOwningProperty(obj, subscriber) { 2666 if (!ObservedObject.IsObservedObject(obj)) { 2667 return false; 2668 } 2669 obj[SubscribableHandler.UNSUBSCRIBE] = subscriber; 2670 return true; 2671 } 2672 /** 2673 * 2674 * @param obj any Object 2675 * @returns return number of subscribers to the given ObservedObject 2676 * or false if given object is not an ObservedObject 2677 */ 2678 static countSubscribers(obj) { 2679 return ObservedObject.IsObservedObject(obj) ? obj[SubscribableHandler.COUNT_SUBSCRIBERS] : false; 2680 } 2681 /* 2682 set or unset callback function to be called when a property has been called 2683 */ 2684 static registerPropertyReadCb(obj, readPropCb, obSelf) { 2685 if (!ObservedObject.IsObservedObject(obj)) { 2686 return false; 2687 } 2688 obj[SubscribableHandler.SET_ONREAD_CB] = readPropCb; 2689 obj[SubscribableHandler.RAW_THIS] = obSelf; 2690 return true; 2691 } 2692 static unregisterPropertyReadCb(obj) { 2693 if (!ObservedObject.IsObservedObject(obj)) { 2694 return false; 2695 } 2696 obj[SubscribableHandler.SET_ONREAD_CB] = undefined; 2697 obj[SubscribableHandler.RAW_THIS] = undefined; 2698 return true; 2699 } 2700 /** 2701 * Utility function for debugging the prototype chain of given Object 2702 * The given object can be any Object, it is not required to be an ObservedObject 2703 * @param object 2704 * @returns multi-line string containing info about the prototype chain 2705 * on class in class hiararchy per line 2706 */ 2707 static tracePrototypeChainOfObject(object) { 2708 let proto = Object.getPrototypeOf(object); 2709 let result = ''; 2710 let sepa = ''; 2711 while (proto) { 2712 result += `${sepa}${ObservedObject.tracePrototype(proto)}`; 2713 proto = Object.getPrototypeOf(proto); 2714 sepa = ',\n'; 2715 } 2716 return result; 2717 } 2718 /** 2719 * Utility function for debugging all functions of given Prototype. 2720 * @returns string containing containing names of all functions and members of given Prototype 2721 */ 2722 static tracePrototype(proto) { 2723 if (!proto) { 2724 return ''; 2725 } 2726 let result = `${proto.constructor && proto.constructor.name ? proto.constructor.name : '<no class>'}: `; 2727 let sepa = ''; 2728 for (let name of Object.getOwnPropertyNames(proto)) { 2729 result += `${sepa}${name}`; 2730 sepa = ', '; 2731 } 2732 ; 2733 return result; 2734 } 2735 /** 2736 * @Observed decorator extends the decorated class. This function returns the prototype of the decorated class 2737 * @param proto 2738 * @returns prototype of the @Observed decorated class or 'proto' parameter if not @Observed decorated 2739 */ 2740 static getPrototypeOfObservedClass(proto) { 2741 return (proto.constructor && proto.constructor.name === 'ObservedClass') 2742 ? Object.getPrototypeOf(proto.constructor.prototype) 2743 : proto; 2744 } 2745} 2746ObservedObject.__IS_OBSERVED_OBJECT = Symbol('_____is_observed_object__'); 2747ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT = Symbol('_____raw_object__'); 2748/* 2749 * Copyright (c) 2021 Huawei Device Co., Ltd. 2750 * Licensed under the Apache License, Version 2.0 (the "License"); 2751 * you may not use this file except in compliance with the License. 2752 * You may obtain a copy of the License at 2753 * 2754 * http://www.apache.org/licenses/LICENSE-2.0 2755 * 2756 * Unless required by applicable law or agreed to in writing, software 2757 * distributed under the License is distributed on an "AS IS" BASIS, 2758 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2759 * See the License for the specific language governing permissions and 2760 * limitations under the License. 2761 */ 2762/* 2763 manage subscriptions to a property 2764 managing the property is left to sub 2765 classes 2766 Extended by ObservedProperty, SyncedPropertyOneWay 2767 and SyncedPropertyTwoWay 2768*/ 2769class ObservedPropertyAbstract extends SubscribedAbstractProperty { 2770 constructor(subscribeMe, info) { 2771 super(); 2772 this.subscribers_ = new Set(); 2773 this.id_ = SubscriberManager.MakeStateVariableId(); 2774 SubscriberManager.Add(this); 2775 if (subscribeMe) { 2776 this.subscribers_.add(subscribeMe.id__()); 2777 } 2778 if (info) { 2779 this.info_ = info; 2780 } 2781 } 2782 aboutToBeDeleted() { 2783 SubscriberManager.Delete(this.id__()); 2784 } 2785 id__() { 2786 return this.id_; 2787 } 2788 info() { 2789 return this.info_; 2790 } 2791 setInfo(propName) { 2792 if (propName && propName !== '') { 2793 this.info_ = propName; 2794 } 2795 } 2796 // Partial Update "*PU" classes will overwrite 2797 getUnmonitored() { 2798 return this.get(); 2799 } 2800 // update the element id for recycle custom component 2801 updateElmtId(oldElmtId, newElmtId) { 2802 if (this.subscribers_.has(oldElmtId)) { 2803 this.subscribers_.delete(oldElmtId); 2804 this.subscribers_.add(newElmtId); 2805 } 2806 } 2807 // Method name is used to check object is of type ObservedPropertyAbstract 2808 // Do NOT override in derived classed, use addSubscriber 2809 subscribeMe(subscriber) { 2810 2811 this.subscribers_.add(subscriber.id__()); 2812 } 2813 /* 2814 the inverse function of createOneWaySync or createTwoWaySync 2815 Do NOT override in derived classed, use removeSubscriber 2816 */ 2817 unlinkSuscriber(subscriberId) { 2818 this.subscribers_.delete(subscriberId); 2819 } 2820 /* 2821 Virtualized version of the subscription mechanism - add subscriber 2822 */ 2823 addSubscriber(subscriber) { 2824 if (subscriber) { 2825 this.subscribeMe(subscriber); 2826 } 2827 } 2828 /* 2829 Virtualized version of the subscription mechanism - remove subscriber 2830 */ 2831 removeSubscriber(subscriber, id) { 2832 if (id) { 2833 this.unlinkSuscriber(id); 2834 } 2835 else if (subscriber) { 2836 this.unlinkSuscriber(subscriber.id__()); 2837 } 2838 } 2839 // FU code path callback 2840 notifyHasChanged(newValue) { 2841 2842 2843 this.subscribers_.forEach((subscribedId) => { 2844 let subscriber = SubscriberManager.Find(subscribedId); 2845 if (subscriber) { 2846 // FU code path 2847 if ('hasChanged' in subscriber) { 2848 subscriber.hasChanged(newValue); 2849 } 2850 if ('propertyHasChanged' in subscriber) { 2851 subscriber.propertyHasChanged(this.info_); 2852 } 2853 } 2854 else { 2855 stateMgmtConsole.warn(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || 'unknown'}']: notifyHasChanged: unknown subscriber ID '${subscribedId}' error!`); 2856 } 2857 }); 2858 2859 } 2860 notifyPropertyRead() { 2861 2862 2863 this.subscribers_.forEach((subscribedId) => { 2864 var subscriber = SubscriberManager.Find(subscribedId); 2865 if (subscriber) { 2866 if ('propertyRead' in subscriber) { 2867 subscriber.propertyRead(this.info_); 2868 } 2869 } 2870 }); 2871 2872 } 2873 /* 2874 return numebr of subscribers to this property 2875 mostly useful for unit testin 2876 */ 2877 numberOfSubscrbers() { 2878 return this.subscribers_.size; 2879 } 2880 /** 2881 * provide a factory function that creates a SynchedPropertyXXXX of choice 2882 * that uses 'this' as source 2883 * @param factoryFunc 2884 * @returns 2885 */ 2886 createSync(factoryFunc) { 2887 return factoryFunc(this); 2888 } 2889 /** 2890 * depreciated SDK function, not used anywhere by the framework 2891 */ 2892 createTwoWaySync(subscribeMe, info) { 2893 stateMgmtConsole.warn("Using depreciated method 'createTwoWaySync'!"); 2894 return this.createLink(subscribeMe, info); 2895 } 2896 /** 2897 * depreciated SDK function, not used anywhere by the framework 2898 */ 2899 createOneWaySync(subscribeMe, info) { 2900 stateMgmtConsole.warn("Using depreciated method 'createOneWaySync' !"); 2901 return this.createProp(subscribeMe, info); 2902 } 2903 /** 2904 * factory function for concrete 'object' or 'simple' ObservedProperty object 2905 * depending if value is Class object 2906 * or simple type (boolean | number | string) 2907 * @param value 2908 * @param owningView 2909 * @param thisPropertyName 2910 * @returns either 2911 */ 2912 static CreateObservedObject(value, owningView, thisPropertyName) { 2913 return (typeof value === 'object') ? 2914 new ObservedPropertyObject(value, owningView, thisPropertyName) 2915 : new ObservedPropertySimple(value, owningView, thisPropertyName); 2916 } 2917} 2918/* 2919 * Copyright (c) 2024 Huawei Device Co., Ltd. 2920 * Licensed under the Apache License, Version 2.0 (the "License"); 2921 * you may not use this file except in compliance with the License. 2922 * You may obtain a copy of the License at 2923 * 2924 * http://www.apache.org/licenses/LICENSE-2.0 2925 * 2926 * Unless required by applicable law or agreed to in writing, software 2927 * distributed under the License is distributed on an "AS IS" BASIS, 2928 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2929 * See the License for the specific language governing permissions and 2930 * limitations under the License. 2931 */ 2932class CustomDialogController extends NativeCustomDialogController { 2933 constructor(arg, view) { 2934 super(arg, view); 2935 this.arg_ = arg; 2936 this.view_ = view; 2937 } 2938} 2939/* 2940 * Copyright (c) 2024 Huawei Device Co., Ltd. 2941 * Licensed under the Apache License, Version 2.0 (the "License"); 2942 * you may not use this file except in compliance with the License. 2943 * You may obtain a copy of the License at 2944 * 2945 * http://www.apache.org/licenses/LICENSE-2.0 2946 * 2947 * Unless required by applicable law or agreed to in writing, software 2948 * distributed under the License is distributed on an "AS IS" BASIS, 2949 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2950 * See the License for the specific language governing permissions and 2951 * limitations under the License. 2952 * 2953 */ 2954class Utils { 2955 static getApiVersion() { 2956 return typeof ViewStackProcessor["getApiVersion"] === "function" 2957 ? ViewStackProcessor["getApiVersion"]() 2958 : undefined; 2959 } 2960 static isApiVersionEQAbove(target) { 2961 let version = Utils.getApiVersion(); 2962 if (version == null) { 2963 return false; 2964 } 2965 if (typeof version === "number") { 2966 version = version % 1000; 2967 } 2968 return version >= target; 2969 } 2970} 2971/* 2972 * Copyright (c) 2024 Huawei Device Co., Ltd. 2973 * Licensed under the Apache License, Version 2.0 (the "License"); 2974 * you may not use this file except in compliance with the License. 2975 * You may obtain a copy of the License at 2976 * 2977 * http://www.apache.org/licenses/LICENSE-2.0 2978 * 2979 * Unless required by applicable law or agreed to in writing, software 2980 * distributed under the License is distributed on an "AS IS" BASIS, 2981 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2982 * See the License for the specific language governing permissions and 2983 * limitations under the License. 2984 */ 2985class stateMgmtDFX { 2986 static getObservedPropertyInfo(observedProp, isProfiler, changedTrackPropertyName) { 2987 return { 2988 decorator: observedProp.debugInfoDecorator(), propertyName: observedProp.info(), id: observedProp.id__(), 2989 changedTrackPropertyName: changedTrackPropertyName, 2990 value: stateMgmtDFX.getRawValue(observedProp), 2991 inRenderingElementId: stateMgmtDFX.inRenderingElementId.length === 0 ? 2992 -1 : stateMgmtDFX.inRenderingElementId[stateMgmtDFX.inRenderingElementId.length - 1], 2993 dependentElementIds: observedProp.dumpDependentElmtIdsObj(typeof observedProp.getUnmonitored() === 'object' ? 2994 !TrackedObject.isCompatibilityMode(observedProp.getUnmonitored()) : false, isProfiler), 2995 owningView: observedProp.getOwningView(), 2996 length: stateMgmtDFX.getRawValueLength(observedProp), 2997 syncPeers: observedProp.dumpSyncPeers(isProfiler, changedTrackPropertyName) 2998 }; 2999 } 3000 static getType(item) { 3001 try { 3002 return Object.prototype.toString.call(item); 3003 } 3004 catch (e) { 3005 stateMgmtConsole.warn(`Cannot get the type of current value, error message is: ${e.message}`); 3006 return 'unknown type'; 3007 } 3008 } 3009 /** 3010 * Dump 10 items at most. 3011 * If length > 10, the output will be the first 7 and last 3 items. 3012 * eg: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 3013 * output: [0, 1, 2, 3, 4, 5, 6, '...', 9, 10, 11] 3014 * 3015 */ 3016 static dumpItems(arr) { 3017 let dumpArr = arr.slice(0, stateMgmtDFX.DUMP_MAX_LENGTH); 3018 if (arr.length > stateMgmtDFX.DUMP_MAX_LENGTH) { 3019 dumpArr.splice(stateMgmtDFX.DUMP_MAX_LENGTH - stateMgmtDFX.DUMP_LAST_LENGTH, stateMgmtDFX.DUMP_LAST_LENGTH, '...', ...arr.slice(-stateMgmtDFX.DUMP_LAST_LENGTH)); 3020 } 3021 return dumpArr.map(item => typeof item === 'object' ? this.getType(item) : item); 3022 } 3023 static dumpMap(map) { 3024 let dumpKey = this.dumpItems(Array.from(map.keys())); 3025 let dumpValue = this.dumpItems(Array.from(map.values())); 3026 return dumpKey.map((item, index) => [item, dumpValue[index]]); 3027 } 3028 static dumpObjectProperty(value) { 3029 let tempObj = {}; 3030 try { 3031 let properties = Object.getOwnPropertyNames(value); 3032 properties 3033 .slice(0, stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT) 3034 .forEach((varName) => { 3035 const propertyValue = Reflect.get(value, varName); 3036 tempObj[varName] = typeof propertyValue === 'object' ? this.getType(propertyValue) : propertyValue; 3037 }); 3038 if (properties.length > stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT) { 3039 tempObj['...'] = '...'; 3040 } 3041 } 3042 catch (e) { 3043 stateMgmtConsole.warn(`can not dump Obj, error msg ${e.message}`); 3044 return "unknown type"; 3045 } 3046 return tempObj; 3047 } 3048 static getRawValue(observedProp) { 3049 let wrappedValue = observedProp.getUnmonitored(); 3050 if (typeof wrappedValue !== 'object') { 3051 return wrappedValue; 3052 } 3053 let rawObject = ObservedObject.GetRawObject(wrappedValue); 3054 if (rawObject instanceof Map) { 3055 return stateMgmtDFX.dumpMap(rawObject); 3056 } 3057 else if (rawObject instanceof Set) { 3058 return stateMgmtDFX.dumpItems(Array.from(rawObject.values())); 3059 } 3060 else if (rawObject instanceof Array) { 3061 return stateMgmtDFX.dumpItems(Array.from(rawObject)); 3062 } 3063 else if (rawObject instanceof Date) { 3064 return rawObject; 3065 } 3066 else { 3067 return stateMgmtDFX.dumpObjectProperty(rawObject); 3068 } 3069 } 3070 static getRawValueLength(observedProp) { 3071 let wrappedValue = observedProp.getUnmonitored(); 3072 if (typeof wrappedValue !== 'object') { 3073 return -1; 3074 } 3075 let rawObject = ObservedObject.GetRawObject(wrappedValue); 3076 if (rawObject instanceof Map || rawObject instanceof Set) { 3077 return rawObject.size; 3078 } 3079 else if (rawObject instanceof Array) { 3080 return rawObject.length; 3081 } 3082 try { 3083 return Object.getOwnPropertyNames(rawObject).length; 3084 } 3085 catch (e) { 3086 return -1; 3087 } 3088 } 3089} 3090// enable profile 3091stateMgmtDFX.enableProfiler = false; 3092stateMgmtDFX.inRenderingElementId = new Array(); 3093stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT = 50; 3094stateMgmtDFX.DUMP_MAX_LENGTH = 10; 3095stateMgmtDFX.DUMP_LAST_LENGTH = 3; 3096function setProfilerStatus(profilerStatus) { 3097 stateMgmtConsole.warn(`${profilerStatus ? `start` : `stop`} stateMgmt Profiler`); 3098 stateMgmtDFX.enableProfiler = profilerStatus; 3099} 3100class DumpInfo { 3101 constructor() { 3102 this.observedPropertiesInfo = []; 3103 } 3104} 3105/* 3106 * Copyright (c) 2021 Huawei Device Co., Ltd. 3107 * Licensed under the Apache License, Version 2.0 (the "License"); 3108 * you may not use this file except in compliance with the License. 3109 * You may obtain a copy of the License at 3110 * 3111 * http://www.apache.org/licenses/LICENSE-2.0 3112 * 3113 * Unless required by applicable law or agreed to in writing, software 3114 * distributed under the License is distributed on an "AS IS" BASIS, 3115 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3116 * See the License for the specific language governing permissions and 3117 * limitations under the License. 3118 */ 3119/** 3120 * ObservedPropertyObjectAbstract 3121 * 3122 * all definitions in this file are framework internal 3123 * 3124 * common base class of ObservedPropertyObject and 3125 * SyncedObjectPropertyTwoWay 3126 * adds the createObjectLink to the ObservedPropertyAbstract base 3127 */ 3128class ObservedPropertyObjectAbstract extends ObservedPropertyAbstract { 3129 constructor(owningView, thisPropertyName) { 3130 super(owningView, thisPropertyName); 3131 } 3132} 3133/* 3134 * Copyright (c) 2021 Huawei Device Co., Ltd. 3135 * Licensed under the Apache License, Version 2.0 (the "License"); 3136 * you may not use this file except in compliance with the License. 3137 * You may obtain a copy of the License at 3138 * 3139 * http://www.apache.org/licenses/LICENSE-2.0 3140 * 3141 * Unless required by applicable law or agreed to in writing, software 3142 * distributed under the License is distributed on an "AS IS" BASIS, 3143 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3144 * See the License for the specific language governing permissions and 3145 * limitations under the License. 3146 */ 3147/** 3148 * 3149 * ObservedPropertySimpleAbstract 3150 * 3151 * all definitions in this file are framework internal 3152 */ 3153class ObservedPropertySimpleAbstract extends ObservedPropertyAbstract { 3154 constructor(owningView, propertyName) { 3155 super(owningView, propertyName); 3156 } 3157} 3158/* 3159 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3160 * Licensed under the Apache License, Version 2.0 (the "License"); 3161 * you may not use this file except in compliance with the License. 3162 * You may obtain a copy of the License at 3163 * 3164 * http://www.apache.org/licenses/LICENSE-2.0 3165 * 3166 * Unless required by applicable law or agreed to in writing, software 3167 * distributed under the License is distributed on an "AS IS" BASIS, 3168 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3169 * See the License for the specific language governing permissions and 3170 * limitations under the License. 3171 */ 3172/** 3173 * ObservedPropertyObject 3174 * 3175 * all definitions in this file are framework internal 3176 * 3177 * class that holds an actual property value of type T 3178 * uses its base class to manage subscribers to this 3179 * property. 3180*/ 3181class ObservedPropertyObject extends ObservedPropertyObjectAbstract { 3182 constructor(value, owningView, propertyName) { 3183 super(owningView, propertyName); 3184 this.setValueInternal(value); 3185 } 3186 aboutToBeDeleted(unsubscribeMe) { 3187 this.unsubscribeFromOwningProperty(); 3188 if (unsubscribeMe) { 3189 this.unlinkSuscriber(unsubscribeMe.id__()); 3190 } 3191 super.aboutToBeDeleted(); 3192 } 3193 // notification from ObservedObject value one of its 3194 // props has chnaged. Implies the ObservedProperty has changed 3195 // Note: this function gets called when in this case: 3196 // thisProp.aObsObj.aProp = 47 a object prop gets changed 3197 // It is NOT called when 3198 // thisProp.aObsObj = new ClassA 3199 hasChanged(newValue) { 3200 3201 this.notifyHasChanged(this.wrappedValue_); 3202 } 3203 unsubscribeFromOwningProperty() { 3204 if (this.wrappedValue_) { 3205 if (this.wrappedValue_ instanceof SubscribaleAbstract) { 3206 this.wrappedValue_.removeOwningProperty(this); 3207 } 3208 else { 3209 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 3210 } 3211 } 3212 } 3213 /* 3214 actually update this.wrappedValue_ 3215 called needs to do value change check 3216 and also notify with this.aboutToChange(); 3217 */ 3218 setValueInternal(newValue) { 3219 if (typeof newValue !== 'object') { 3220 3221 return false; 3222 } 3223 this.unsubscribeFromOwningProperty(); 3224 if (ObservedObject.IsObservedObject(newValue)) { 3225 3226 ObservedObject.addOwningProperty(newValue, this); 3227 this.wrappedValue_ = newValue; 3228 } 3229 else if (newValue instanceof SubscribaleAbstract) { 3230 3231 this.wrappedValue_ = newValue; 3232 this.wrappedValue_.addOwningProperty(this); 3233 } 3234 else { 3235 3236 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 3237 } 3238 return true; 3239 } 3240 get() { 3241 3242 this.notifyPropertyRead(); 3243 return this.wrappedValue_; 3244 } 3245 set(newValue) { 3246 if (this.wrappedValue_ == newValue) { 3247 3248 return; 3249 } 3250 3251 this.setValueInternal(newValue); 3252 this.notifyHasChanged(newValue); 3253 } 3254 /** 3255 * These functions are used 3256 * LocalStorage.link (also in partial update config) 3257 * (FU)View.initializeConsumeinitializeConsume 3258 */ 3259 createLink(subscribeOwner, linkPropName) { 3260 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 3261 } 3262 createProp(subscribeOwner, linkPropName) { 3263 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 3264 } 3265} 3266/* 3267 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3268 * Licensed under the Apache License, Version 2.0 (the "License"); 3269 * you may not use this file except in compliance with the License. 3270 * You may obtain a copy of the License at 3271 * 3272 * http://www.apache.org/licenses/LICENSE-2.0 3273 * 3274 * Unless required by applicable law or agreed to in writing, software 3275 * distributed under the License is distributed on an "AS IS" BASIS, 3276 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3277 * See the License for the specific language governing permissions and 3278 * limitations under the License. 3279 */ 3280/** 3281 * ObservedPropertySimple 3282 * 3283 * all definitions in this file are framework internal 3284 */ 3285class ObservedPropertySimple extends ObservedPropertySimpleAbstract { 3286 constructor(value, owningView, propertyName) { 3287 super(owningView, propertyName); 3288 if (typeof value === 'object') { 3289 throw new SyntaxError('ObservedPropertySimple value must not be an object'); 3290 } 3291 this.setValueInternal(value); 3292 } 3293 aboutToBeDeleted(unsubscribeMe) { 3294 if (unsubscribeMe) { 3295 this.unlinkSuscriber(unsubscribeMe.id__()); 3296 } 3297 super.aboutToBeDeleted(); 3298 } 3299 hasChanged(newValue) { 3300 3301 this.notifyHasChanged(this.wrappedValue_); 3302 } 3303 /* 3304 actually update this.wrappedValue_ 3305 called needs to do value change check 3306 and also notify with this.aboutToChange(); 3307 */ 3308 setValueInternal(newValue) { 3309 3310 this.wrappedValue_ = newValue; 3311 } 3312 get() { 3313 3314 this.notifyPropertyRead(); 3315 return this.wrappedValue_; 3316 } 3317 set(newValue) { 3318 if (this.wrappedValue_ === newValue) { 3319 3320 return; 3321 } 3322 3323 this.setValueInternal(newValue); 3324 this.notifyHasChanged(newValue); 3325 } 3326 /** 3327 * These functions are meant for use in connection with the App Stoage and 3328 * business logic implementation. 3329 * the created Link and Prop will update when 'this' property value 3330 * changes. 3331 */ 3332 createLink(subscribeOwner, linkPropName) { 3333 return new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 3334 } 3335 createProp(subscribeOwner, linkPropName) { 3336 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, linkPropName); 3337 } 3338} 3339/* 3340 * Copyright (c) 2021 Huawei Device Co., Ltd. 3341 * Licensed under the Apache License, Version 2.0 (the "License"); 3342 * you may not use this file except in compliance with the License. 3343 * You may obtain a copy of the License at 3344 * 3345 * http://www.apache.org/licenses/LICENSE-2.0 3346 * 3347 * Unless required by applicable law or agreed to in writing, software 3348 * distributed under the License is distributed on an "AS IS" BASIS, 3349 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3350 * See the License for the specific language governing permissions and 3351 * limitations under the License. 3352 */ 3353/** 3354 * SynchedPropertyObjectTwoWay 3355 * 3356 * all definitions in this file are framework internal 3357 */ 3358class SynchedPropertyObjectTwoWay extends ObservedPropertyObjectAbstract { 3359 constructor(linkSource, owningChildView, thisPropertyName) { 3360 super(owningChildView, thisPropertyName); 3361 this.changeNotificationIsOngoing_ = false; 3362 this.linkedParentProperty_ = linkSource; 3363 if (this.linkedParentProperty_) { 3364 // register to the parent property 3365 this.linkedParentProperty_.subscribeMe(this); 3366 } 3367 // register to the ObservedObject 3368 ObservedObject.addOwningProperty(this.getObject(), this); 3369 } 3370 /* 3371 like a destructor, need to call this before deleting 3372 the property. 3373 */ 3374 aboutToBeDeleted() { 3375 if (this.linkedParentProperty_) { 3376 // unregister from parent of this link 3377 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 3378 // unregister from the ObservedObject 3379 ObservedObject.removeOwningProperty(this.getObject(), this); 3380 } 3381 super.aboutToBeDeleted(); 3382 } 3383 getObject() { 3384 this.notifyPropertyRead(); 3385 return (this.linkedParentProperty_ ? this.linkedParentProperty_.get() : undefined); 3386 } 3387 setObject(newValue) { 3388 if (this.linkedParentProperty_) { 3389 this.linkedParentProperty_.set(newValue); 3390 } 3391 } 3392 // this object is subscriber to ObservedObject 3393 // will call this cb function when property has changed 3394 hasChanged(newValue) { 3395 if (!this.changeNotificationIsOngoing_) { 3396 3397 this.notifyHasChanged(this.getObject()); 3398 } 3399 } 3400 // get 'read through` from the ObservedProperty 3401 get() { 3402 3403 return this.getObject(); 3404 } 3405 // set 'writes through` to the ObservedProperty 3406 set(newValue) { 3407 if (this.getObject() === newValue) { 3408 3409 return; 3410 } 3411 3412 ObservedObject.removeOwningProperty(this.getObject(), this); 3413 // the purpose of the changeNotificationIsOngoing_ is to avoid 3414 // circular notifications @Link -> source @State -> other but alos same @Link 3415 this.changeNotificationIsOngoing_ = true; 3416 this.setObject(newValue); 3417 ObservedObject.addOwningProperty(this.getObject(), this); 3418 this.notifyHasChanged(newValue); 3419 this.changeNotificationIsOngoing_ = false; 3420 } 3421 /** 3422 * These functions are meant for use in connection with the App Stoage and 3423 * business logic implementation. 3424 * the created Link and Prop will update when 'this' property value 3425 * changes. 3426 */ 3427 createLink(subscribeOwner, linkPropName) { 3428 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 3429 } 3430 createProp(subscribeOwner, linkPropName) { 3431 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 3432 } 3433} 3434/* 3435 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3436 * Licensed under the Apache License, Version 2.0 (the "License"); 3437 * you may not use this file except in compliance with the License. 3438 * You may obtain a copy of the License at 3439 * 3440 * http://www.apache.org/licenses/LICENSE-2.0 3441 * 3442 * Unless required by applicable law or agreed to in writing, software 3443 * distributed under the License is distributed on an "AS IS" BASIS, 3444 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3445 * See the License for the specific language governing permissions and 3446 * limitations under the License. 3447 */ 3448/** 3449 * SynchedPropertySimpleOneWay 3450 * 3451 * all definitions in this file are framework internal 3452 */ 3453class SynchedPropertySimpleOneWay extends ObservedPropertySimpleAbstract { 3454 constructor(value, subscribeMe, info) { 3455 super(subscribeMe, info); 3456 // add a test here that T is a simple type 3457 this.wrappedValue_ = value; 3458 } 3459 /* 3460 like a destructor, need to call this before deleting 3461 the property. 3462 */ 3463 aboutToBeDeleted() { 3464 super.aboutToBeDeleted(); 3465 } 3466 // get 'read through` from the ObservedProperty 3467 get() { 3468 3469 this.notifyPropertyRead(); 3470 return this.wrappedValue_; 3471 } 3472 set(newValue) { 3473 if (this.wrappedValue_ == newValue) { 3474 3475 return; 3476 } 3477 3478 this.wrappedValue_ = newValue; 3479 this.notifyHasChanged(newValue); 3480 } 3481 /** 3482 * These functions are meant for use in connection with the App Stoage and 3483 * business logic implementation. 3484 * the created Link and Prop will update when 'this' property value 3485 * changes. 3486 */ 3487 createLink(subscribeOwner, linkPropName) { 3488 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 3489 } 3490 createProp(subscribeOwner, linkPropName) { 3491 throw new Error('Method not supported, create a SynchedPropertySimpleOneWaySubscribing from, where to create a Prop.'); 3492 } 3493} 3494/* 3495 This exrension of SynchedPropertySimpleOneWay needs to be used for AppStorage 3496 because it needs to be notified about the source property changing 3497 ( there is no re-render process as in Views to update the wrappedValue ) 3498*/ 3499class SynchedPropertySimpleOneWaySubscribing extends SynchedPropertySimpleOneWay { 3500 constructor(linkedProperty, subscribeMe, info) { 3501 super(linkedProperty.get(), subscribeMe, info); 3502 this.linkedParentProperty_ = linkedProperty; 3503 this.linkedParentProperty_.subscribeMe(this); 3504 } 3505 aboutToBeDeleted() { 3506 // unregister from parent of this prop 3507 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 3508 super.aboutToBeDeleted(); 3509 } 3510 hasChanged(newValue) { 3511 3512 this.set(newValue); 3513 } 3514 /** 3515 * These functions are meant for use in connection with the App Stoage and 3516 * business logic implementation. 3517 * the created Link and Prop will update when 'this' property value 3518 * changes. 3519 */ 3520 createLink(subscribeOwner, linkPropName) { 3521 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 3522 } 3523 createProp(subscribeOwner, propPropName) { 3524 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 3525 } 3526} 3527/* 3528 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3529 * Licensed under the Apache License, Version 2.0 (the "License"); 3530 * you may not use this file except in compliance with the License. 3531 * You may obtain a copy of the License at 3532 * 3533 * http://www.apache.org/licenses/LICENSE-2.0 3534 * 3535 * Unless required by applicable law or agreed to in writing, software 3536 * distributed under the License is distributed on an "AS IS" BASIS, 3537 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3538 * See the License for the specific language governing permissions and 3539 * limitations under the License. 3540 */ 3541/** 3542 * SynchedPropertySimpleTwoWay 3543 * 3544 * all definitions in this file are framework internal 3545 */ 3546class SynchedPropertySimpleTwoWay extends ObservedPropertySimpleAbstract { 3547 constructor(source, owningView, owningViewPropNme) { 3548 super(owningView, owningViewPropNme); 3549 this.changeNotificationIsOngoing_ = false; 3550 this.source_ = source; 3551 this.source_.subscribeMe(this); 3552 } 3553 /* 3554 like a destructor, need to call this before deleting 3555 the property. 3556 */ 3557 aboutToBeDeleted() { 3558 if (this.source_) { 3559 this.source_.unlinkSuscriber(this.id__()); 3560 this.source_ = undefined; 3561 } 3562 super.aboutToBeDeleted(); 3563 } 3564 // this object is subscriber to SynchedPropertySimpleTwoWay 3565 // will call this cb function when property has changed 3566 // a set (newValue) is not done because get reads through for the source_ 3567 hasChanged(newValue) { 3568 if (!this.changeNotificationIsOngoing_) { 3569 3570 this.notifyHasChanged(newValue); 3571 } 3572 } 3573 // get 'read through` from the ObservedProperty 3574 get() { 3575 3576 if (!this.source_) { 3577 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || 'unknown'}'] source_ is undefined: get value is undefined.`); 3578 return undefined; 3579 } 3580 this.notifyPropertyRead(); 3581 return this.source_.get(); 3582 } 3583 // set 'writes through` to the ObservedProperty 3584 set(newValue) { 3585 if (!this.source_) { 3586 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || 'unknown'}'] source_ is undefined: set '${newValue}' ignoring.`); 3587 return; 3588 } 3589 if (this.source_.get() === newValue) { 3590 3591 return; 3592 } 3593 3594 // the source_ ObservedProeprty will call: this.hasChanged(newValue); 3595 // the purpose of the changeNotificationIsOngoing_ is to avoid 3596 // circular notifications @Link -> source @State -> other but alos same @Link 3597 this.changeNotificationIsOngoing_ = true; 3598 this.source_.set(newValue); 3599 this.notifyHasChanged(newValue); 3600 this.changeNotificationIsOngoing_ = false; 3601 } 3602 /** 3603 * These functions are meant for use in connection with the App Stoage and 3604 * business logic implementation. 3605 * the created Link and Prop will update when 'this' property value 3606 * changes. 3607 */ 3608 createLink(subscribeOwner, linkPropName) { 3609 return new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 3610 } 3611 createProp(subscribeOwner, propPropName) { 3612 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 3613 } 3614} 3615/* 3616 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3617 * Licensed under the Apache License, Version 2.0 (the "License"); 3618 * you may not use this file except in compliance with the License. 3619 * You may obtain a copy of the License at 3620 * 3621 * http://www.apache.org/licenses/LICENSE-2.0 3622 * 3623 * Unless required by applicable law or agreed to in writing, software 3624 * distributed under the License is distributed on an "AS IS" BASIS, 3625 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3626 * See the License for the specific language governing permissions and 3627 * limitations under the License. 3628 */ 3629/** 3630 * SynchedPropertyNesedObject 3631 * 3632 * all definitions in this file are framework internal 3633 */ 3634class SynchedPropertyNesedObject extends ObservedPropertyObjectAbstract { 3635 /** 3636 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 3637 * example 3638 * this.b.$a with b of type PC and a of type C, or 3639 * this.$b[5] with this.b of type PC and array item b[5] of type C; 3640 * 3641 * @param subscribeMe 3642 * @param propName 3643 */ 3644 constructor(obsObject, owningChildView, propertyName) { 3645 super(owningChildView, propertyName); 3646 this.obsObject_ = obsObject; 3647 // register to the ObservedObject 3648 ObservedObject.addOwningProperty(this.obsObject_, this); 3649 } 3650 /* 3651 like a destructor, need to call this before deleting 3652 the property. 3653 */ 3654 aboutToBeDeleted() { 3655 // unregister from the ObservedObject 3656 ObservedObject.removeOwningProperty(this.obsObject_, this); 3657 super.aboutToBeDeleted(); 3658 } 3659 // this object is subscriber to ObservedObject 3660 // will call this cb function when property has changed 3661 hasChanged(newValue) { 3662 3663 this.notifyHasChanged(this.obsObject_); 3664 } 3665 // get 'read through` from the ObservedProperty 3666 get() { 3667 3668 this.notifyPropertyRead(); 3669 return this.obsObject_; 3670 } 3671 // set 'writes through` to the ObservedProperty 3672 set(newValue) { 3673 if (this.obsObject_ === newValue) { 3674 3675 return; 3676 } 3677 3678 // unsubscribe from the old value ObservedObject 3679 ObservedObject.removeOwningProperty(this.obsObject_, this); 3680 this.obsObject_ = newValue; 3681 // subscribe to the new value ObservedObject 3682 ObservedObject.addOwningProperty(this.obsObject_, this); 3683 // notify value change to subscribing View 3684 this.notifyHasChanged(this.obsObject_); 3685 } 3686 /** 3687 * These functions are meant for use in connection with the App Stoage and 3688 * business logic implementation. 3689 * the created Link and Prop will update when 'this' property value 3690 * changes. 3691 */ 3692 createLink(subscribeOwner, linkPropName) { 3693 throw new Error('Method not supported for property linking to a nested objects.'); 3694 } 3695 createProp(subscribeOwner, linkPropName) { 3696 throw new Error("Creating a 'Prop' proerty is unsuppoeted for Object type prperty value."); 3697 } 3698} 3699/* 3700 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3701 * Licensed under the Apache License, Version 2.0 (the "License"); 3702 * you may not use this file except in compliance with the License. 3703 * You may obtain a copy of the License at 3704 * 3705 * http://www.apache.org/licenses/LICENSE-2.0 3706 * 3707 * Unless required by applicable law or agreed to in writing, software 3708 * distributed under the License is distributed on an "AS IS" BASIS, 3709 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3710 * See the License for the specific language governing permissions and 3711 * limitations under the License. 3712 */ 3713// Nativeview 3714// implemented in C++ for release 3715// and in utest/view_native_mock.ts for testing 3716class View extends NativeViewFullUpdate { 3717 /** 3718 * Create a View 3719 * 3720 * 1. option: top level View, specify 3721 * - compilerAssignedUniqueChildId must specify 3722 * - parent=undefined 3723 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 3724 * in this View or descendant Views. 3725 * 3726 * 2. option: not a top level View 3727 * - compilerAssignedUniqueChildId must specify 3728 * - parent must specify 3729 * - localStorage do not specify, will inherit from parent View. 3730 * 3731 * @param compilerAssignedUniqueChildId Tw 3732 * @param parent 3733 * @param localStorage 3734 */ 3735 constructor(compilerAssignedUniqueChildId, parent, localStorage) { 3736 super(compilerAssignedUniqueChildId, parent); 3737 this.propsUsedForRender = new Set(); 3738 this.isRenderingInProgress = false; 3739 this.watchedProps = new Map(); 3740 // my LocalStorge instance, shared with ancestor Views. 3741 // create a default instance on demand if none is initialized 3742 this.localStoragebackStore_ = undefined; 3743 this.id_ = SubscriberManager.MakeId(); 3744 this.providedVars_ = parent ? new Map(parent.providedVars_) 3745 : new Map(); 3746 this.localStoragebackStore_ = undefined; 3747 if (parent) { 3748 // this View is not a top-level View 3749 3750 this.setCardId(parent.getCardId()); 3751 this.localStorage_ = parent.localStorage_; 3752 } 3753 else if (localStorage) { 3754 this.localStorage_ = localStorage; 3755 3756 } 3757 SubscriberManager.Add(this); 3758 3759 } 3760 get localStorage_() { 3761 if (!this.localStoragebackStore_) { 3762 3763 this.localStoragebackStore_ = new LocalStorage({ /* emty */}); 3764 } 3765 return this.localStoragebackStore_; 3766 } 3767 set localStorage_(instance) { 3768 if (!instance) { 3769 // setting to undefined not allowed 3770 return; 3771 } 3772 if (this.localStoragebackStore_) { 3773 stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); 3774 } 3775 this.localStoragebackStore_ = instance; 3776 } 3777 // globally unique id, this is different from compilerAssignedUniqueChildId! 3778 id__() { 3779 return this.id_; 3780 } 3781 // temporary function, do not use, it will be removed soon! 3782 // prupsoe is to allow eDSL transpiler to fix a bug that 3783 // relies on this method 3784 id() { 3785 return this.id__(); 3786 } 3787 propertyHasChanged(info) { 3788 if (info) { 3789 // need to sync container instanceId to switch instanceId in C++ side. 3790 this.syncInstanceId(); 3791 if (this.propsUsedForRender.has(info)) { 3792 3793 this.markNeedUpdate(); 3794 } 3795 else { 3796 3797 } 3798 let cb = this.watchedProps.get(info); 3799 if (cb) { 3800 3801 cb.call(this, info); 3802 } 3803 this.restoreInstanceId(); 3804 } // if info avail. 3805 } 3806 propertyRead(info) { 3807 3808 if (info && (info !== 'unknown') && this.isRenderingInProgress) { 3809 this.propsUsedForRender.add(info); 3810 } 3811 } 3812 // for test purposes 3813 propertiesNeededToRender() { 3814 return this.propsUsedForRender; 3815 } 3816 aboutToRender() { 3817 3818 // reset 3819 this.propsUsedForRender = new Set(); 3820 this.isRenderingInProgress = true; 3821 } 3822 aboutToContinueRender() { 3823 // do not reset 3824 this.isRenderingInProgress = true; 3825 } 3826 onRenderDone() { 3827 this.isRenderingInProgress = false; 3828 3829 } 3830 /** 3831 * Function to be called from the constructor of the sub component 3832 * to register a @Watch varibale 3833 * @param propStr name of the variable. Note from @Provide and @Consume this is 3834 * the variable name and not the alias! 3835 * @param callback application defined member function of sub-class 3836 */ 3837 declareWatch(propStr, callback) { 3838 this.watchedProps.set(propStr, callback); 3839 } 3840 /** 3841 * This View @Provide's a variable under given name 3842 * Call this function from the constructor of the sub class 3843 * @param providedPropName either the variable name or the alias defined as 3844 * decorator param 3845 * @param store the backing store object for this variable (not the get/set variable!) 3846 */ 3847 addProvidedVar(providedPropName, store) { 3848 if (this.providedVars_.has(providedPropName)) { 3849 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. 3850 Property with this name is provided by one of the ancestor Views already.`); 3851 } 3852 this.providedVars_.set(providedPropName, store); 3853 } 3854 /** 3855 * Method for the sub-class to call from its constructor for resolving 3856 * a @Consume variable and initializing its backing store 3857 * with the yncedPropertyTwoWay<T> object created from the 3858 * @Provide variable's backing store. 3859 * @param providedPropName the name of the @Provide'd variable. 3860 * This is either the @Consume decortor parameter, or variable name. 3861 * @param consumeVarName the @Consume variable name (not the 3862 * @Consume decortor parameter) 3863 * @returns initiaizing value of the @Consume backing store 3864 */ 3865 initializeConsume(providedPropName, consumeVarName) { 3866 let providedVarStore = this.providedVars_.get(providedPropName); 3867 if (providedVarStore === undefined) { 3868 throw new ReferenceError(`${this.constructor.name}: missing @Provide property with name ${providedPropName}. 3869 Fail to resolve @Consume(${providedPropName}).`); 3870 } 3871 return providedVarStore.createLink(this, consumeVarName); 3872 } 3873} 3874/* 3875 * Copyright (c) 2024 Huawei Device Co., Ltd. 3876 * Licensed under the Apache License, Version 2.0 (the "License"); 3877 * you may not use this file except in compliance with the License. 3878 * You may obtain a copy of the License at 3879 * 3880 * http://www.apache.org/licenses/LICENSE-2.0 3881 * 3882 * Unless required by applicable law or agreed to in writing, software 3883 * distributed under the License is distributed on an "AS IS" BASIS, 3884 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3885 * See the License for the specific language governing permissions and 3886 * limitations under the License. 3887 */ 3888/* 3889 * Copyright (c) 2022 Huawei Device Co., Ltd. 3890 * Licensed under the Apache License, Version 2.0 (the "License"); 3891 * you may not use this file except in compliance with the License. 3892 * You may obtain a copy of the License at 3893 * 3894 * http://www.apache.org/licenses/LICENSE-2.0 3895 * 3896 * Unless required by applicable law or agreed to in writing, software 3897 * distributed under the License is distributed on an "AS IS" BASIS, 3898 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3899 * See the License for the specific language governing permissions and 3900 * limitations under the License. 3901 */ 3902// UpdateFuncRecord: misc framework-internal info related to updating of a UINode C++ object 3903// that TS side needs to know. 3904// updateFunc_ lambda function to update the UINode 3905// JS interface class reference (it only has static functions) 3906class UpdateFuncRecord { 3907 constructor(params) { 3908 this.updateFunc_ = params.updateFunc; 3909 this.classObject_ = params.classObject; 3910 this.node_ = params.node; 3911 } 3912 getUpdateFunc() { 3913 return this.updateFunc_; 3914 } 3915 getComponentClass() { 3916 return this.classObject_; 3917 } 3918 getComponentName() { 3919 return (this.classObject_ && ('name' in this.classObject_)) ? Reflect.get(this.classObject_, 'name') : 'unspecified UINode'; 3920 } 3921 getPopFunc() { 3922 return (this.classObject_ && 'pop' in this.classObject_) ? this.classObject_.pop : () => { }; 3923 } 3924 getNode() { 3925 return this.node_; 3926 } 3927 setNode(node) { 3928 this.node_ = node; 3929 } 3930} // UpdateFuncRecord 3931class UpdateFuncsByElmtId { 3932 constructor() { 3933 this.map_ = new Map(); 3934 } 3935 delete(elmtId) { 3936 return this.map_.delete(elmtId); 3937 } 3938 set(elmtId, params) { 3939 (typeof params === 'object') ? 3940 this.map_.set(elmtId, new UpdateFuncRecord(params)) : 3941 this.map_.set(elmtId, new UpdateFuncRecord({ updateFunc: params })); 3942 } 3943 get(elmtId) { 3944 return this.map_.get(elmtId); 3945 } 3946 has(elmtId) { 3947 return this.map_.has(elmtId); 3948 } 3949 keys() { 3950 return this.map_.keys(); 3951 } 3952 clear() { 3953 return this.map_.clear(); 3954 } 3955 get size() { 3956 return this.map_.size; 3957 } 3958 forEach(callbackfn) { 3959 this.map_.forEach(callbackfn); 3960 } 3961 // dump info about known elmtIds to a string 3962 // use function only for debug output and DFX. 3963 debugInfoRegisteredElmtIds() { 3964 let result = ''; 3965 let sepa = ''; 3966 this.map_.forEach((value, elmtId) => { 3967 result += `${sepa}${value.getComponentName()}[${elmtId}]`; 3968 sepa = ', '; 3969 }); 3970 return result; 3971 } 3972 debugInfoElmtId(elmtId) { 3973 const updateFuncEntry = this.map_.get(elmtId); 3974 return updateFuncEntry ? `${updateFuncEntry.getComponentName()}[${elmtId}]` : `'unknown component type'[${elmtId}]`; 3975 } 3976} // class UpdateFuncByElmtId 3977/* 3978 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 3979 * Licensed under the Apache License, Version 2.0 (the "License"); 3980 * you may not use this file except in compliance with the License. 3981 * You may obtain a copy of the License at 3982 * 3983 * http://www.apache.org/licenses/LICENSE-2.0 3984 * 3985 * Unless required by applicable law or agreed to in writing, software 3986 * distributed under the License is distributed on an "AS IS" BASIS, 3987 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3988 * See the License for the specific language governing permissions and 3989 * limitations under the License. 3990 * 3991*/ 3992// NativeView 3993// implemented in C++ for release 3994class PUV2ViewBase extends NativeViewPartialUpdate { 3995 constructor(parent, elmtId = UINodeRegisterProxy.notRecordingDependencies, extraInfo = undefined) { 3996 super(); 3997 // indicates the currently rendered or rendered UINode's elmtIds 3998 // or UINodeRegisterProxy.notRecordingDependencies if none is currently rendering 3999 // isRenderInProgress == true always when currentlyRenderedElmtIdStack_ length >= 0 4000 this.currentlyRenderedElmtIdStack_ = new Array(); 4001 // Set of elmtIds that need re-render 4002 this.dirtDescendantElementIds_ = new Set(); 4003 // Map elmtId -> Repeat instance in this ViewPU 4004 this.elmtId2Repeat_ = new Map(); 4005 this.parent_ = undefined; 4006 this.childrenWeakrefMap_ = new Map(); 4007 // flag if active of inActive 4008 // inActive means updates are delayed 4009 this.isActive_ = true; 4010 // flag if {aboutToBeDeletedInternal} is called and the instance of ViewPU/V2 has not been GC. 4011 this.isDeleting_ = false; 4012 this.isCompFreezeAllowed_ = false; 4013 // registry of update functions 4014 // the key is the elementId of the Component/Element that's the result of this function 4015 this.updateFuncByElmtId = new UpdateFuncsByElmtId(); 4016 this.extraInfo_ = undefined; 4017 // if set use the elmtId also as the ViewPU/V2 object's subscribable id. 4018 // these matching is requirement for updateChildViewById(elmtId) being able to 4019 // find the child ViewPU/V2 object by given elmtId 4020 this.id_ = elmtId === UINodeRegisterProxy.notRecordingDependencies ? SubscriberManager.MakeId() : elmtId; 4021 4022 if (extraInfo) { 4023 this.extraInfo_ = extraInfo; 4024 } 4025 if (parent) { 4026 // this View is not a top-level View 4027 this.setCardId(parent.getCardId()); 4028 // Call below will set this parent_ to parent as well 4029 parent.addChild(this); // FIXME 4030 } 4031 this.isCompFreezeAllowed_ = this.isCompFreezeAllowed_ || (this.parent_ && this.parent_.isCompFreezeAllowed()); 4032 4033 } 4034 // globally unique id, this is different from compilerAssignedUniqueChildId! 4035 id__() { 4036 return this.id_; 4037 } 4038 updateId(elmtId) { 4039 this.id_ = elmtId; 4040 } 4041 setParent(parent) { 4042 if (this.parent_ && parent) { 4043 stateMgmtConsole.warn(`${this.debugInfo__()}: setChild: changing parent to '${parent === null || parent === void 0 ? void 0 : parent.debugInfo__()} (unsafe operation)`); 4044 } 4045 this.parent_ = parent; 4046 } 4047 getParent() { 4048 return this.parent_; 4049 } 4050 /** 4051 * add given child and set 'this' as its parent 4052 * @param child child to add 4053 * @returns returns false if child with given child's id already exists 4054 * 4055 * framework internal function 4056 * Note: Use of WeakRef ensures child and parent do not generate a cycle dependency. 4057 * The add. Set<ids> is required to reliably tell what children still exist. 4058 */ 4059 addChild(child) { 4060 if (this.childrenWeakrefMap_.has(child.id__())) { 4061 stateMgmtConsole.warn(`${this.debugInfo__()}: addChild '${child === null || child === void 0 ? void 0 : child.debugInfo__()}' elmtId already exists ${child.id__()}. Internal error!`); 4062 return false; 4063 } 4064 this.childrenWeakrefMap_.set(child.id__(), new WeakRef(child)); 4065 child.setParent(this); // FIXME 4066 return true; 4067 } 4068 /** 4069 * remove given child and remove 'this' as its parent 4070 * @param child child to add 4071 * @returns returns false if child with given child's id does not exist 4072 */ 4073 removeChild(child) { 4074 const hasBeenDeleted = this.childrenWeakrefMap_.delete(child.id__()); 4075 if (!hasBeenDeleted) { 4076 stateMgmtConsole.warn(`${this.debugInfo__()}: removeChild '${child === null || child === void 0 ? void 0 : child.debugInfo__()}', child id ${child.id__()} not known. Internal error!`); 4077 } 4078 else { 4079 child.setParent(undefined); 4080 } 4081 return hasBeenDeleted; 4082 } 4083 /** 4084 * Retrieve child by given id 4085 * @param id 4086 * @returns child if in map and weak ref resolves to IView object 4087 */ 4088 getChildById(id) { 4089 const childWeakRef = this.childrenWeakrefMap_.get(id); 4090 return childWeakRef ? childWeakRef.deref() : undefined; 4091 } 4092 aboutToReuse(_) { } 4093 aboutToRecycle() { } 4094 isDeleting() { 4095 return this.isDeleting_; 4096 } 4097 setDeleting() { 4098 4099 this.isDeleting_ = true; 4100 } 4101 setDeleteStatusRecursively() { 4102 if (!this.childrenWeakrefMap_.size) { 4103 return; 4104 } 4105 4106 this.childrenWeakrefMap_.forEach((value) => { 4107 let child = value.deref(); 4108 if (child) { 4109 child.setDeleting(); 4110 child.setDeleteStatusRecursively(); 4111 } 4112 }); 4113 } 4114 isCompFreezeAllowed() { 4115 return this.isCompFreezeAllowed_; 4116 } 4117 getChildViewV2ForElmtId(elmtId) { 4118 const optComp = this.childrenWeakrefMap_.get(elmtId); 4119 return (optComp === null || optComp === void 0 ? void 0 : optComp.deref()) && (optComp.deref() instanceof ViewV2) ? 4120 optComp === null || optComp === void 0 ? void 0 : optComp.deref() : undefined; 4121 } 4122 purgeVariableDependenciesOnElmtIdOwnFunc(elmtId) { 4123 // ViewPU overrides to unregister ViewPU from variables, 4124 // not in use in ViewV2 4125 } 4126 // overwritten by sub classes 4127 debugInfo__() { 4128 return `@Component '${this.constructor.name}'[${this.id__()}]`; 4129 } 4130 debugInfoRegisteredElmtIds() { 4131 return this.updateFuncByElmtId.debugInfoRegisteredElmtIds(); 4132 } 4133 // for given elmtIds look up their component name/type and format a string out of this info 4134 // use function only for debug output and DFX. 4135 debugInfoElmtIds(elmtIds) { 4136 let result = ''; 4137 let sepa = ''; 4138 elmtIds.forEach((elmtId) => { 4139 result += `${sepa}${this.debugInfoElmtId(elmtId)}`; 4140 sepa = ', '; 4141 }); 4142 return result; 4143 } 4144 debugInfoElmtId(elmtId, isProfiler = false) { 4145 return isProfiler ? { 4146 elementId: elmtId, 4147 elementTag: this.updateFuncByElmtId.get(elmtId).getComponentName(), 4148 isCustomNode: this.childrenWeakrefMap_.has(elmtId) 4149 } : this.updateFuncByElmtId.debugInfoElmtId(elmtId); 4150 } 4151 dumpStateVars() { 4152 4153 } 4154 isViewActive() { 4155 return this.isActive_; 4156 } 4157 dumpReport() { 4158 stateMgmtConsole.warn(`Printing profiler information`); 4159 stateMgmtProfiler.report(); 4160 } 4161 updateStateVarsOfChildByElmtId(elmtId, params) { 4162 4163 4164 if (elmtId < 0) { 4165 stateMgmtConsole.warn(`${this.debugInfo__()}: updateChildViewById(${elmtId}) - invalid elmtId - internal error!`); 4166 4167 return; 4168 } 4169 let iChild = this.getChildById(elmtId); 4170 if (!iChild || !((iChild instanceof ViewPU) || (iChild instanceof ViewV2))) { 4171 stateMgmtConsole.warn(`${this.debugInfo__()}: updateChildViewById(${elmtId}) - no child with this elmtId - internal error!`); 4172 4173 return; 4174 } 4175 const child = iChild; 4176 if ('updateStateVars' in child) { 4177 child.updateStateVars(params); 4178 } 4179 4180 4181 } 4182 // request list of all (global) elmtIds of deleted UINodes and unregister from the all ViewPUs/ViewV2 4183 // this function equals purgeDeletedElmtIdsRecursively because it does un-registration for all ViewPU/V2's 4184 purgeDeletedElmtIds() { 4185 4186 // request list of all (global) elmtIds of deleted UINodes that need to be unregistered 4187 UINodeRegisterProxy.obtainDeletedElmtIds(); 4188 // unregister the removed elmtIds requested from the cpp side for all ViewPUs/ViewV2, it will make the first ViewPUs/ViewV2 slower 4189 // than before, but the rest ViewPUs/ViewV2 will be faster 4190 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 4191 4192 } 4193 /** 4194 * force a complete rerender / update by executing all update functions 4195 * exec a regular rerender first 4196 * 4197 * @param deep recurse all children as well 4198 * 4199 * framework internal functions, apps must not call 4200 */ 4201 forceCompleteRerender(deep = false) { 4202 4203 4204 // see which elmtIds are managed by this View 4205 // and clean up all book keeping for them 4206 this.purgeDeletedElmtIds(); 4207 Array.from(this.updateFuncByElmtId.keys()).sort(ViewPU.compareNumber).forEach(elmtId => this.UpdateElement(elmtId)); 4208 if (!deep) { 4209 4210 4211 return; 4212 } 4213 for (const child of this.childrenWeakrefMap_.values()) { 4214 const childView = child.deref(); 4215 if (!childView) { 4216 continue; 4217 } 4218 if (child instanceof ViewPU) { 4219 if (!child.isRecycled()) { 4220 child.forceCompleteRerender(true); 4221 } 4222 else { 4223 child.delayCompleteRerender(deep); 4224 } 4225 } 4226 else { 4227 childView.forceCompleteRerender(true); 4228 } 4229 } 4230 4231 4232 } 4233 /** 4234 * force a complete rerender / update on specific node by executing update function. 4235 * 4236 * @param elmtId which node needs to update. 4237 * 4238 * framework internal functions, apps must not call 4239 */ 4240 forceRerenderNode(elmtId) { 4241 4242 // see which elmtIds are managed by this View 4243 // and clean up all book keeping for them 4244 this.purgeDeletedElmtIds(); 4245 this.UpdateElement(elmtId); 4246 // remove elemtId from dirtDescendantElementIds. 4247 this.dirtDescendantElementIds_.delete(elmtId); 4248 4249 } 4250 /** 4251 * for C++ to judge whether a CustomNode has updateFunc with specified nodeId. 4252 * use same judgement with UpdateElement, to make sure it can rerender if return true. 4253 * 4254 * @param elmtId query ID 4255 * 4256 * framework internal function 4257 */ 4258 hasNodeUpdateFunc(elmtId) { 4259 const entry = this.updateFuncByElmtId.get(elmtId); 4260 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 4261 // if this component does not have updateFunc for elmtId, return false. 4262 return typeof updateFunc === 'function'; 4263 } 4264 static pauseRendering() { 4265 PUV2ViewBase.renderingPaused = true; 4266 } 4267 static restoreRendering() { 4268 PUV2ViewBase.renderingPaused = false; 4269 } 4270 // performs the update on a branch within if() { branch } else if (..) { branch } else { branch } 4271 ifElseBranchUpdateFunction(branchId, branchfunc) { 4272 var _a, _b; 4273 const oldBranchid = If.getBranchId(); 4274 if (branchId === oldBranchid) { 4275 4276 return; 4277 } 4278 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onIfElseBranchUpdateEnter(); 4279 // branchid identifies uniquely the if .. <1> .. else if .<2>. else .<3>.branch 4280 // ifElseNode stores the most recent branch, so we can compare 4281 // removedChildElmtIds will be filled with the elmtIds of all children and their children will be deleted in response to if .. else change 4282 let removedChildElmtIds = new Array(); 4283 If.branchId(branchId, removedChildElmtIds); 4284 //un-registers the removed child elementIDs using proxy 4285 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 4286 // purging these elmtIds from state mgmt will make sure no more update function on any deleted child wi;ll be executed 4287 4288 this.purgeDeletedElmtIds(); 4289 branchfunc(); 4290 (_b = PUV2ViewBase.arkThemeScopeManager) === null || _b === void 0 ? void 0 : _b.onIfElseBranchUpdateExit(removedChildElmtIds); 4291 } 4292 /** 4293 Partial updates for ForEach. 4294 * @param elmtId ID of element. 4295 * @param itemArray Array of items for use of itemGenFunc. 4296 * @param itemGenFunc Item generation function to generate new elements. If index parameter is 4297 * given set itemGenFuncUsesIndex to true. 4298 * @param idGenFunc ID generation function to generate unique ID for each element. If index parameter is 4299 * given set idGenFuncUsesIndex to true. 4300 * @param itemGenFuncUsesIndex itemGenFunc optional index parameter is given or not. 4301 * @param idGenFuncUsesIndex idGenFunc optional index parameter is given or not. 4302 */ 4303 forEachUpdateFunction(elmtId, itemArray, itemGenFunc, idGenFunc, itemGenFuncUsesIndex = false, idGenFuncUsesIndex = false) { 4304 4305 4306 if (itemArray === null || itemArray === undefined) { 4307 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): input array is null or undefined error. Application error!`); 4308 4309 return; 4310 } 4311 if (typeof itemGenFunc !== 'function') { 4312 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): Item generation function missing. Application error!`); 4313 4314 return; 4315 } 4316 if (idGenFunc !== undefined && typeof idGenFunc !== 'function') { 4317 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): id generator is not a function. Application error!`); 4318 4319 return; 4320 } 4321 if (idGenFunc === undefined) { 4322 4323 idGenFuncUsesIndex = true; 4324 // catch possible error caused by Stringify and re-throw an Error with a meaningful (!) error message 4325 idGenFunc = (item, index) => { 4326 try { 4327 return `${index}__${JSON.stringify(item)}`; 4328 } 4329 catch (e) { 4330 throw new Error(`${this.debugInfo__()}: ForEach id ${elmtId}: use of default id generator function not possible on provided data structure. Need to specify id generator function (ForEach 3rd parameter). Application Error!`); 4331 } 4332 }; 4333 } 4334 let diffIndexArray = []; // New indexes compared to old one. 4335 let newIdArray = []; 4336 let idDuplicates = []; 4337 const arr = itemArray; // just to trigger a 'get' onto the array 4338 // ID gen is with index. 4339 if (idGenFuncUsesIndex || idGenFunc.length > 1) { 4340 // Create array of new ids. 4341 arr.forEach((item, indx) => { 4342 newIdArray.push(idGenFunc(item, indx)); 4343 }); 4344 } 4345 else { 4346 // Create array of new ids. 4347 arr.forEach((item, index) => { 4348 newIdArray.push(`${itemGenFuncUsesIndex ? index + '_' : ''}` + idGenFunc(item)); 4349 }); 4350 } 4351 // removedChildElmtIds will be filled with the elmtIds of all children and their children will be deleted in response to foreach change 4352 let removedChildElmtIds = []; 4353 // Set new array on C++ side. 4354 // C++ returns array of indexes of newly added array items. 4355 // these are indexes in new child list. 4356 ForEach.setIdArray(elmtId, newIdArray, diffIndexArray, idDuplicates, removedChildElmtIds); 4357 // Its error if there are duplicate IDs. 4358 if (idDuplicates.length > 0) { 4359 idDuplicates.forEach((indx) => { 4360 stateMgmtConsole.error(`Error: ${newIdArray[indx]} generated for ${indx}${indx < 4 ? indx === 2 ? 'nd' : 'rd' : 'th'} array item ${arr[indx]}.`); 4361 }); 4362 stateMgmtConsole.applicationError(`${this.debugInfo__()}: Ids generated by the ForEach id gen function must be unique. Application error!`); 4363 } 4364 4365 // Item gen is with index. 4366 4367 // Create new elements if any. 4368 4369 diffIndexArray.forEach((indx) => { 4370 ForEach.createNewChildStart(newIdArray[indx], this); 4371 if (itemGenFuncUsesIndex) { 4372 itemGenFunc(arr[indx], indx); 4373 } 4374 else { 4375 itemGenFunc(arr[indx]); 4376 } 4377 ForEach.createNewChildFinish(newIdArray[indx], this); 4378 }); 4379 // un-registers the removed child elementIDs using proxy 4380 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 4381 // purging these elmtIds from state mgmt will make sure no more update function on any deleted child will be executed 4382 4383 this.purgeDeletedElmtIds(); 4384 4385 4386 4387 } 4388 createOrGetNode(elmtId, builder) { 4389 const entry = this.updateFuncByElmtId.get(elmtId); 4390 if (entry === undefined) { 4391 throw new Error(`${this.debugInfo__()} fail to create node, elmtId is illegal`); 4392 } 4393 let nodeInfo = entry.getNode(); 4394 if (nodeInfo === undefined) { 4395 nodeInfo = builder(); 4396 entry.setNode(nodeInfo); 4397 } 4398 return nodeInfo; 4399 } 4400 /** 4401 * getNodeById is used to get ArkComponent stored updateFuncByElmtId 4402 * @param elmtId - the id of the component 4403 * @returns ArkComponent | undefined 4404 */ 4405 getNodeById(elmtId) { 4406 const entry = this.updateFuncByElmtId.get(elmtId); 4407 return entry ? entry.getNode() : undefined; 4408 } 4409 /** 4410 * return its elmtId if currently rendering or re-rendering an UINode 4411 * otherwise return UINodeRegisterProxy.notRecordingDependencies 4412 * set in observeComponentCreation(2) 4413 */ 4414 getCurrentlyRenderedElmtId() { 4415 return PUV2ViewBase.renderingPaused || this.currentlyRenderedElmtIdStack_.length === 0 4416 ? UINodeRegisterProxy.notRecordingDependencies 4417 : this.currentlyRenderedElmtIdStack_[this.currentlyRenderedElmtIdStack_.length - 1]; 4418 } 4419 debugInfoViewHierarchy(recursive = false) { 4420 return this.debugInfoViewHierarchyInternal(0, recursive); 4421 } 4422 debugInfoViewHierarchyInternal(depth = 0, recursive = false) { 4423 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]`; 4424 if (this.isCompFreezeAllowed()) { 4425 retVaL += ` {freezeWhenInactive : ${this.isCompFreezeAllowed()}}`; 4426 } 4427 if (depth < 1 || recursive) { 4428 this.childrenWeakrefMap_.forEach((weakChild) => { 4429 var _a; 4430 retVaL += (_a = weakChild.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoViewHierarchyInternal(depth + 1, recursive); 4431 }); 4432 } 4433 return retVaL; 4434 } 4435 debugInfoUpdateFuncByElmtId(recursive = false) { 4436 return this.debugInfoUpdateFuncByElmtIdInternal({ total: 0 }, 0, recursive); 4437 } 4438 debugInfoUpdateFuncByElmtIdInternal(counter, depth = 0, recursive = false) { 4439 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 4440 this.updateFuncByElmtId.forEach((value, key, map) => { 4441 retVaL += `\n${' '.repeat(depth + 2)}${value.getComponentName()}[${key}]`; 4442 }); 4443 counter.total += this.updateFuncByElmtId.size; 4444 retVaL += `\n${' '.repeat(depth + 1)}}[${this.updateFuncByElmtId.size}]`; 4445 if (recursive) { 4446 this.childrenWeakrefMap_.forEach((value, key, map) => { 4447 var _a; 4448 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoUpdateFuncByElmtIdInternal(counter, depth + 1, recursive); 4449 }); 4450 } 4451 if (recursive && depth === 0) { 4452 retVaL += `\nTotal: ${counter.total}`; 4453 } 4454 return retVaL; 4455 } 4456 debugInfoInactiveComponents() { 4457 return Array.from(PUV2ViewBase.inactiveComponents_) 4458 .map((component) => `- ${component}`).join('\n'); 4459 } 4460 onGlobalThemeChanged() { 4461 } 4462 static setArkThemeScopeManager(mgr) { 4463 PUV2ViewBase.arkThemeScopeManager = mgr; 4464 } 4465} // class PUV2ViewBase 4466// List of inactive components used for Dfx 4467PUV2ViewBase.inactiveComponents_ = new Set(); 4468// Array.sort() converts array items to string to compare them! 4469PUV2ViewBase.compareNumber = (a, b) => { 4470 return (a < b) ? -1 : (a > b) ? 1 : 0; 4471}; 4472// static flag for paused rendering 4473// when paused, getCurrentlyRenderedElmtId() will return UINodeRegisterProxy.notRecordingDependencies 4474PUV2ViewBase.renderingPaused = false; 4475PUV2ViewBase.arkThemeScopeManager = undefined; 4476/* 4477 * Copyright (c) 2024 Huawei Device Co., Ltd. 4478 * Licensed under the Apache License, Version 2.0 (the "License"); 4479 * you may not use this file except in compliance with the License. 4480 * You may obtain a copy of the License at 4481 * 4482 * http://www.apache.org/licenses/LICENSE-2.0 4483 * 4484 * Unless required by applicable law or agreed to in writing, software 4485 * distributed under the License is distributed on an "AS IS" BASIS, 4486 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4487 * See the License for the specific language governing permissions and 4488 * limitations under the License. 4489 */ 4490; 4491; 4492; 4493class SendableType { 4494 static isArray(o) { 4495 return o instanceof SendableArray; 4496 } 4497 static isSet(o) { 4498 return o instanceof SendableSet; 4499 } 4500 static isMap(o) { 4501 return o instanceof SendableMap; 4502 } 4503 static isContainer(o) { 4504 return o instanceof SendableMap || o instanceof SendableSet || o instanceof SendableArray; 4505 } 4506} 4507/* 4508 * Copyright (c) 2022 Huawei Device Co., Ltd. 4509 * Licensed under the Apache License, Version 2.0 (the "License"); 4510 * you may not use this file except in compliance with the License. 4511 * You may obtain a copy of the License at 4512 * 4513 * http://www.apache.org/licenses/LICENSE-2.0 4514 * 4515 * Unless required by applicable law or agreed to in writing, software 4516 * distributed under the License is distributed on an "AS IS" BASIS, 4517 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4518 * See the License for the specific language governing permissions and 4519 * limitations under the License. 4520 */ 4521/* 4522 * Copyright (c) 2023 Huawei Device Co., Ltd. 4523 * Licensed under the Apache License, Version 2.0 (the "License"); 4524 * you may not use this file except in compliance with the License. 4525 * You may obtain a copy of the License at 4526 * 4527 * http://www.apache.org/licenses/LICENSE-2.0 4528 * 4529 * Unless required by applicable law or agreed to in writing, software 4530 * distributed under the License is distributed on an "AS IS" BASIS, 4531 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4532 * See the License for the specific language governing permissions and 4533 * limitations under the License. 4534 */ 4535// @Track class property decorator 4536// indicates to framework to track individual object property value changes 4537function Track(target, property) { 4538 var _a; 4539 ConfigureStateMgmt.instance.usingPUObservedTrack(`@Track`, property); 4540 Reflect.set(target, `${TrackedObject.___TRACKED_PREFIX}${property}`, true); 4541 Reflect.set(target, TrackedObject.___IS_TRACKED_OPTIMISED, true); 4542 4543} 4544class TrackedObject { 4545 static isCompatibilityMode(obj) { 4546 return !obj || (typeof obj !== 'object') || !Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED); 4547 } 4548 static needsPropertyReadCb(obj) { 4549 return obj && (typeof obj === 'object') && Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED); 4550 } 4551 /** 4552 * @Track new object assignment optimization 4553 * can apply if old and new value are object, instance of same class, do not use compat mode. 4554 * in this case function returns true and calls supplied notifyTrackedPropertyChanged cb function 4555 * for each @Tracked'ed property whose value actually changed. 4556 * if optimisation can not be applied calls notifyPropertyChanged and returns false 4557 */ 4558 static notifyObjectValueAssignment(obj1, obj2, notifyPropertyChanged, // notify as assignment (none-optimised) 4559 notifyTrackedPropertyChange, obSelf) { 4560 if (!obj1 || !obj2 || (typeof obj1 !== 'object') || (typeof obj2 !== 'object') || 4561 (obj1.constructor !== obj2.constructor) || 4562 TrackedObject.isCompatibilityMode(obj1)) { 4563 4564 notifyPropertyChanged.call(obSelf); 4565 return false; 4566 } 4567 4568 const obj1Raw = ObservedObject.GetRawObject(obj1); 4569 const obj2Raw = ObservedObject.GetRawObject(obj2); 4570 let shouldFakePropPropertyBeNotified = false; 4571 Object.keys(obj2Raw) 4572 .forEach(propName => { 4573 // Collect only @Track'ed changed properties 4574 if (Reflect.has(obj1Raw, `${TrackedObject.___TRACKED_PREFIX}${propName}`) && 4575 (Reflect.get(obj1Raw, propName) !== Reflect.get(obj2Raw, propName))) { 4576 4577 notifyTrackedPropertyChange.call(obSelf, propName); 4578 shouldFakePropPropertyBeNotified = true; 4579 } 4580 else { 4581 4582 } 4583 }); 4584 // notify this non-existing object property has changed only if some of the tracked properties changed. 4585 // SynchedPropertyOneWay reset() report a 'read' on this property, thereby creating a dependency 4586 // reporting the property as changed causes @Prop sync from source 4587 if (shouldFakePropPropertyBeNotified) { 4588 4589 notifyTrackedPropertyChange.call(obSelf, TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY); 4590 } 4591 // always notify this non-existing object property has changed for SynchedPropertyNestedObject as 4592 // the object has changed in assigment. 4593 // SynchedPropertyNestedObject set() reports a 'read' on this property, thereby creating a dependency 4594 // reporting the property as changed causes @ObjectLink sync from source 4595 4596 notifyTrackedPropertyChange.call(obSelf, TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY); 4597 return true; 4598 } 4599} 4600TrackedObject.___IS_TRACKED_OPTIMISED = `___IS_TRACKED_OPTIMISED`; 4601TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_PROP_PROPERTY`; 4602TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_OBJLINK_PROPERTY`; 4603TrackedObject.___TRACKED_PREFIX = `___TRACKED_`; 4604TrackedObject.___TRACKED_PREFIX_LEN = TrackedObject.___TRACKED_PREFIX.length; 4605/* 4606 * Copyright (c) 2022 Huawei Device Co., Ltd. 4607 * Licensed under the Apache License, Version 2.0 (the "License"); 4608 * you may not use this file except in compliance with the License. 4609 * You may obtain a copy of the License at 4610 * 4611 * http://www.apache.org/licenses/LICENSE-2.0 4612 * 4613 * Unless required by applicable law or agreed to in writing, software 4614 * distributed under the License is distributed on an "AS IS" BASIS, 4615 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4616 * See the License for the specific language governing permissions and 4617 * limitations under the License. 4618 */ 4619var _a; 4620/** 4621 * ObservedPropertyAbstractPU aka ObservedPropertyAbstract for partial update 4622 * 4623 * all definitions in this file are framework internal 4624 */ 4625class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { 4626 constructor(subscriber, viewName) { 4627 super(subscriber, viewName); 4628 this.changeNotificationIsOngoing_ = false; 4629 // when owning ViewPU is inActive, delay notifying changes 4630 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 4631 // install when current value is ObservedObject and the value type is not using compatibility mode 4632 // note value may change for union type variables when switching an object from one class to another. 4633 this.shouldInstallTrackedObjectReadCb = false; 4634 this.dependentElmtIdsByProperty_ = new PropertyDependencies(); 4635 Object.defineProperty(this, 'owningView_', { writable: true, enumerable: false, value: undefined }); 4636 Object.defineProperty(this, 'subscriberRefs_', { writable: true, enumerable: false, value: new Set() }); 4637 if (subscriber) { 4638 if (subscriber instanceof ViewPU) { 4639 this.owningView_ = subscriber; 4640 } 4641 else { 4642 this.subscriberRefs_.add(subscriber); 4643 } 4644 } 4645 } 4646 aboutToBeDeleted() { 4647 super.aboutToBeDeleted(); 4648 this.subscriberRefs_.clear(); 4649 this.owningView_ = undefined; 4650 } 4651 setDecoratorInfo(decorate) { 4652 this.decoratorInfo_ = decorate; 4653 } 4654 // dump info about variable decorator to string 4655 // e.g. @State, @Link, etc. 4656 debugInfoDecorator() { 4657 return this.decoratorInfo_; 4658 } 4659 // dump basic info about this variable to a string, non-recursive, no subscriber info 4660 debugInfo() { 4661 const propSource = this.isPropSourceObservedPropertyFakeName(); 4662 return (propSource) 4663 ? `internal source (ObservedPropertyPU) of @Prop ${propSource} [${this.id__()}]` 4664 : `${this.debugInfoDecorator()} '${this.info()}'[${this.id__()}] <${this.debugInfoOwningView()}>`; 4665 } 4666 debugInfoOwningView() { 4667 return `${this.owningView_ ? this.owningView_.debugInfo__() : 'owning @Component UNKNOWN'}`; 4668 } 4669 // dump info about owning view and subscribers (PU ones only) 4670 // use function only for debug output and DFX. 4671 debugInfoSubscribers() { 4672 return (this.owningView_) 4673 ? `|--Owned by ${this.debugInfoOwningView()} ` 4674 : `|--Owned by: owning view not known`; 4675 } 4676 debugInfoSyncPeers() { 4677 if (!this.subscriberRefs_.size) { 4678 return '|--Sync peers: none'; 4679 } 4680 let result = `|--Sync peers: {`; 4681 let sepa = ''; 4682 this.subscriberRefs_.forEach((subscriber) => { 4683 if ('debugInfo' in subscriber) { 4684 result += `\n ${sepa}${subscriber.debugInfo()}`; 4685 sepa = ', '; 4686 } 4687 }); 4688 result += '\n }'; 4689 return result; 4690 } 4691 debugInfoDependentElmtIds(dumpDependantElements = false) { 4692 return this.dependentElmtIdsByProperty_.dumpInfoDependencies(this.owningView_, dumpDependantElements); 4693 } 4694 dumpDependentElmtIdsObj(isTrackedMode, isProfiler) { 4695 return this.dependentElmtIdsByProperty_.dumpInfoDependenciesObj(this.owningView_, isTrackedMode, isProfiler); 4696 } 4697 debugInfoElmtId(elmtId) { 4698 if (this.owningView_) { 4699 return this.owningView_.debugInfoElmtId(elmtId); 4700 } 4701 return '<unknown element id ' + elmtId + ', missing owning view>'; 4702 } 4703 debugInfoDependentComponents() { 4704 let result = `|--Dependent elements: `; 4705 let sepa = '; '; 4706 let sepaDiff = ''; 4707 const dumpDependantElements = true; 4708 let queue = [this]; 4709 let seen = new Set(); 4710 while (queue.length) { 4711 let item = queue.shift(); 4712 seen.add(item); 4713 if (item !== this) { 4714 result += `${sepa}${item.debugInfoOwningView()}`; 4715 } 4716 result += `${sepaDiff}${item.debugInfoDependentElmtIds(dumpDependantElements)}`; // new dependent elements 4717 sepaDiff = ', '; 4718 item.subscriberRefs_.forEach((subscriber) => { 4719 if ((subscriber instanceof ObservedPropertyAbstractPU)) { 4720 if (!seen.has(subscriber)) { 4721 queue.push(subscriber); 4722 } 4723 } 4724 }); 4725 } 4726 return result; 4727 } 4728 /**/ 4729 hasDependencies() { 4730 return this.dependentElmtIdsByProperty_.hasDependencies(); 4731 } 4732 /* for @Prop value from source we need to generate a @State 4733 that observes when this value changes. This ObservedPropertyPU 4734 sits inside SynchedPropertyOneWayPU. 4735 below methods invent a fake variable name for it 4736 */ 4737 getPropSourceObservedPropertyFakeName() { 4738 return `${this.info()}_prop_fake_state_source___`; 4739 } 4740 isPropSourceObservedPropertyFakeName() { 4741 return this.info() && this.info().endsWith('_prop_fake_state_source___') 4742 ? this.info().substring(0, this.info().length - '_prop_fake_state_source___'.length) 4743 : false; 4744 } 4745 getOwningView() { 4746 var _a, _b; 4747 return { componentName: (_a = this.owningView_) === null || _a === void 0 ? void 0 : _a.constructor.name, id: (_b = this.owningView_) === null || _b === void 0 ? void 0 : _b.id__() }; 4748 } 4749 dumpSyncPeers(isProfiler, changedTrackPropertyName) { 4750 let res = []; 4751 this.subscriberRefs_.forEach((subscriber) => { 4752 if ('debugInfo' in subscriber) { 4753 const observedProp = subscriber; 4754 res.push(stateMgmtDFX.getObservedPropertyInfo(observedProp, isProfiler, changedTrackPropertyName)); 4755 } 4756 }); 4757 return res; 4758 } 4759 onDumpProfiler(changedTrackPropertyName) { 4760 var _a, _b; 4761 let res = new DumpInfo(); 4762 res.viewInfo = { componentName: (_a = this.owningView_) === null || _a === void 0 ? void 0 : _a.constructor.name, id: (_b = this.owningView_) === null || _b === void 0 ? void 0 : _b.id__() }; 4763 res.observedPropertiesInfo.push(stateMgmtDFX.getObservedPropertyInfo(this, true, changedTrackPropertyName)); 4764 if (this.owningView_) { 4765 try { 4766 this.owningView_.sendStateInfo(JSON.stringify(res)); 4767 } 4768 catch (error) { 4769 stateMgmtConsole.applicationError(`${this.debugInfo()} has error in sendStateInfo: ${error.message}`); 4770 } 4771 } 4772 } 4773 /* 4774 Virtualized version of the subscription mechanism - add subscriber 4775 Overrides implementation in ObservedPropertyAbstract<T> 4776 */ 4777 addSubscriber(subscriber) { 4778 if (subscriber) { 4779 // ObservedPropertyAbstract will also add subscriber to 4780 // SubscriberManager map and to its own Set of subscribers as well 4781 // Something to improve in the future for PU path. 4782 // subscribeMe should accept IPropertySubscriber interface 4783 super.subscribeMe(subscriber); 4784 this.subscriberRefs_.add(subscriber); 4785 } 4786 } 4787 /* 4788 Virtualized version of the subscription mechanism - remove subscriber 4789 Overrides implementation in ObservedPropertyAbstract<T> 4790 */ 4791 removeSubscriber(subscriber, id) { 4792 if (subscriber) { 4793 this.subscriberRefs_.delete(subscriber); 4794 if (!id) { 4795 id = subscriber.id__(); 4796 } 4797 } 4798 super.unlinkSuscriber(id); 4799 } 4800 /** 4801 * put the property to delayed notification mode 4802 * feature is only used for @StorageLink/Prop, @LocalStorageLink/Prop 4803 */ 4804 enableDelayedNotification() { 4805 if (this.delayedNotification_ !== ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) { 4806 4807 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_none_pending; 4808 } 4809 } 4810 /* 4811 when moving from inActive to active state the owning ViewPU calls this function 4812 This solution is faster than ViewPU polling each variable to send back a viewPropertyHasChanged event 4813 with the elmtIds 4814 4815 returns undefined if variable has _not_ changed 4816 returns dependentElementIds_ Set if changed. This Set is empty if variable is not used to construct the UI 4817 */ 4818 moveElmtIdsForDelayedUpdate(isReused = false) { 4819 const result = (this.delayedNotification_ === ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) ? 4820 this.dependentElmtIdsByProperty_.getAllPropertyDependencies() : 4821 undefined; 4822 4823 if (isReused && !this.owningView_.isViewActive()) { 4824 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_none_pending; 4825 } 4826 else { 4827 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 4828 } 4829 return result; 4830 } 4831 notifyPropertyRead() { 4832 stateMgmtConsole.error(`${this.debugInfo()}: notifyPropertyRead, DO NOT USE with PU. Use notifyReadCb mechanism.`); 4833 } 4834 // notify owning ViewPU and peers of a variable assignment 4835 // also property/item changes to ObservedObjects of class object type, which use compat mode 4836 // Date and Array are notified as if there had been an assignment. 4837 notifyPropertyHasChangedPU() { 4838 4839 4840 if (this.owningView_) { 4841 if (this.delayedNotification_ === ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) { 4842 // send viewPropertyHasChanged right away 4843 this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getAllPropertyDependencies()); 4844 // send changed observed property to profiler 4845 // only will be true when enable profiler 4846 if (stateMgmtDFX.enableProfiler) { 4847 4848 this.onDumpProfiler(); 4849 } 4850 } 4851 else { 4852 // mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending 4853 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending; 4854 } 4855 } 4856 this.subscriberRefs_.forEach((subscriber) => { 4857 if (subscriber && typeof subscriber === 'object' && 'syncPeerHasChanged' in subscriber) { 4858 subscriber.syncPeerHasChanged(this); 4859 } 4860 else { 4861 stateMgmtConsole.warn(`${this.debugInfo()}: notifyPropertyHasChangedPU: unknown subscriber ID 'subscribedId' error!`); 4862 } 4863 }); 4864 4865 } 4866 // notify owning ViewPU and peers of a ObservedObject @Track property's assignment 4867 notifyTrackedObjectPropertyHasChanged(changedPropertyName) { 4868 4869 4870 if (this.owningView_) { 4871 if (this.delayedNotification_ == ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) { 4872 // send viewPropertyHasChanged right away 4873 this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getTrackedObjectPropertyDependencies(changedPropertyName, 'notifyTrackedObjectPropertyHasChanged')); 4874 // send changed observed property to profiler 4875 // only will be true when enable profiler 4876 if (stateMgmtDFX.enableProfiler) { 4877 4878 this.onDumpProfiler(changedPropertyName); 4879 } 4880 } 4881 else { 4882 // mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending 4883 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending; 4884 } 4885 } 4886 this.subscriberRefs_.forEach((subscriber) => { 4887 if (subscriber) { 4888 if ('syncPeerTrackedPropertyHasChanged' in subscriber) { 4889 subscriber.syncPeerTrackedPropertyHasChanged(this, changedPropertyName); 4890 } 4891 else { 4892 stateMgmtConsole.warn(`${this.debugInfo()}: notifyTrackedObjectPropertyHasChanged: unknown subscriber ID 'subscribedId' error!`); 4893 } 4894 } 4895 }); 4896 4897 } 4898 markDependentElementsDirty(view) { 4899 // TODO ace-ets2bundle, framework, complicated apps need to update together 4900 // this function will be removed after a short transition period. 4901 stateMgmtConsole.warn(`${this.debugInfo()}: markDependentElementsDirty no longer supported. App will work ok, but 4902 please update your ace-ets2bundle and recompile your application!`); 4903 } 4904 numberOfSubscrbers() { 4905 return this.subscriberRefs_.size + (this.owningView_ ? 1 : 0); 4906 } 4907 /* 4908 type checking for any supported type, as required for union type support 4909 see 1st parameter for explanation what is allowed 4910 4911 FIXME this expects the Map, Set patch to go in 4912 */ 4913 checkIsSupportedValue(value) { 4914 let res = ((typeof value === 'object' && typeof value !== 'function' && 4915 !ObserveV2.IsObservedObjectV2(value) && 4916 !ObserveV2.IsMakeObserved(value)) || 4917 typeof value === 'number' || 4918 typeof value === 'string' || 4919 typeof value === 'boolean' || 4920 value === undefined || 4921 value === null); 4922 if (!res) { 4923 errorReport.varValueCheckFailed({ 4924 customComponent: this.debugInfoOwningView(), 4925 variableDeco: this.debugInfoDecorator(), 4926 variableName: this.info(), 4927 expectedType: `undefined, null, number, boolean, string, or Object but not function, not V2 @ObservedV2 / @Trace class, and makeObserved return value either`, 4928 value: value 4929 }); 4930 } 4931 return res; 4932 } 4933 /* 4934 type checking for allowed Object type value 4935 see 1st parameter for explanation what is allowed 4936 4937 FIXME this expects the Map, Set patch to go in 4938 */ 4939 checkIsObject(value) { 4940 let res = ((typeof value === 'object' && typeof value !== 'function' && !ObserveV2.IsObservedObjectV2(value)) || 4941 value === undefined || value === null); 4942 if (!res) { 4943 errorReport.varValueCheckFailed({ 4944 customComponent: this.debugInfoOwningView(), 4945 variableDeco: this.debugInfoDecorator(), 4946 variableName: this.info(), 4947 expectedType: `undefined, null, Object including Array and instance of SubscribableAbstract, excluding function and V2 @Observed/@Trace object`, 4948 value: value 4949 }); 4950 } 4951 return res; 4952 } 4953 /* 4954 type checking for allowed simple types value 4955 see 1st parameter for explanation what is allowed 4956 */ 4957 checkIsSimple(value) { 4958 let res = (value === undefined || typeof value === 'number' || typeof value === 'string' || typeof value === 'boolean'); 4959 if (!res) { 4960 errorReport.varValueCheckFailed({ 4961 customComponent: this.debugInfoOwningView(), 4962 variableDeco: this.debugInfoDecorator(), 4963 variableName: this.info(), 4964 expectedType: `undefined, number, boolean, string`, 4965 value: value 4966 }); 4967 } 4968 return res; 4969 } 4970 checkNewValue(isAllowedComment, newValue, validator) { 4971 if (validator(newValue)) { 4972 return true; 4973 } 4974 // report error 4975 // current implementation throws an Exception 4976 errorReport.varValueCheckFailed({ 4977 customComponent: this.debugInfoOwningView(), 4978 variableDeco: this.debugInfoDecorator(), 4979 variableName: this.info(), 4980 expectedType: isAllowedComment, 4981 value: newValue 4982 }); 4983 // never gets here if errorReport.varValueCheckFailed throws an exception 4984 // but should not depend on its implementation 4985 return false; 4986 } 4987 /** 4988 * factory function for concrete 'object' or 'simple' ObservedProperty object 4989 * depending if value is Class object 4990 * or simple type (boolean | number | string) 4991 * @param value 4992 * @param owningView 4993 * @param thisPropertyName 4994 * @returns either 4995 */ 4996 static CreateObservedObject(value, owningView, thisPropertyName) { 4997 return (typeof value === 'object') ? 4998 new ObservedPropertyObject(value, owningView, thisPropertyName) 4999 : new ObservedPropertySimple(value, owningView, thisPropertyName); 5000 } 5001 /** 5002 * If owning viewPU is currently rendering or re-rendering a UINode, return its elmtId 5003 * return notRecordingDependencies (-1) otherwise 5004 * ViewPU caches the info, it does not request the info from C++ side (by calling 5005 * ViewStackProcessor.GetElmtIdToAccountFor(); as done in earlier implementation 5006 */ 5007 getRenderingElmtId() { 5008 return (this.owningView_) ? this.owningView_.getCurrentlyRenderedElmtId() : UINodeRegisterProxy.notRecordingDependencies; 5009 } 5010 /** 5011 * during 'get' access recording take note of the created component and its elmtId 5012 * and add this component to the list of components who are dependent on this property 5013 */ 5014 recordPropertyDependentUpdate() { 5015 const elmtId = this.getRenderingElmtId(); 5016 if (elmtId === UINodeRegisterProxy.notRecordingDependencies) { 5017 // not access recording 5018 return; 5019 } 5020 if (elmtId === UINodeRegisterProxy.monitorIllegalV1V2StateAccess) { 5021 const error = `${this.debugInfo()}: recordPropertyDependentUpdate trying to use V1 state to init/update child V2 @Component. Application error`; 5022 stateMgmtConsole.applicationError(error); 5023 throw new TypeError(error); 5024 } 5025 5026 this.dependentElmtIdsByProperty_.addPropertyDependency(elmtId); 5027 } 5028 /** record dependency ObservedObject + propertyName -> elmtId 5029 * caller ensures renderingElmtId >= 0 5030 */ 5031 recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readTrackedPropertyName) { 5032 5033 this.dependentElmtIdsByProperty_.addTrackedObjectPropertyDependency(readTrackedPropertyName, renderingElmtId); 5034 } 5035 purgeDependencyOnElmtId(rmElmtId) { 5036 var _a; 5037 (_a = this.dependentElmtIdsByProperty_) === null || _a === void 0 ? void 0 : _a.purgeDependenciesForElmtId(rmElmtId); 5038 } 5039 SetPropertyUnchanged() { 5040 // function to be removed 5041 // keep it here until transpiler is updated. 5042 } 5043 // unified Appstorage, what classes to use, and the API 5044 createLink(subscribeOwner, linkPropName) { 5045 throw new Error(`${this.debugInfo()}: createLink: Can not create a AppStorage 'Link' from this property.`); 5046 } 5047 createProp(subscribeOwner, linkPropName) { 5048 throw new Error(`${this.debugInfo()}: createProp: Can not create a AppStorage 'Prop' from a @State property. `); 5049 } 5050 /* 5051 Below empty functions required to keep as long as this class derives from FU version 5052 ObservedPropertyAbstract. Need to overwrite these functions to do nothing for PU 5053 */ 5054 notifyHasChanged(_) { 5055 stateMgmtConsole.error(`${this.debugInfo()}: notifyHasChanged, DO NOT USE with PU. Use syncPeerHasChanged() \ 5056 or onTrackedObjectProperty(CompatMode)HasChangedPU()`); 5057 } 5058 /** 5059 * event emitted by wrapped ObservedObject, when one of its property values changes 5060 * for class objects when in compatibility mode 5061 * for Array, Date instances always 5062 * @param souceObject 5063 * @param changedPropertyName 5064 */ 5065 onTrackedObjectPropertyHasChangedPU(sourceObject, changedPropertyName) { 5066 5067 this.notifyTrackedObjectPropertyHasChanged(changedPropertyName); 5068 } 5069 /** 5070 * event emitted by wrapped ObservedObject, when one of its property values changes 5071 * for class objects when in compatibility mode 5072 * for Array, Date instances always 5073 * @param souceObject 5074 * @param changedPropertyName 5075 */ 5076 onTrackedObjectPropertyCompatModeHasChangedPU(sourceObject, changedPropertyName) { 5077 5078 this.notifyPropertyHasChangedPU(); 5079 } 5080 hasChanged(_) { 5081 // unused for PU 5082 // need to overwrite impl of base class with empty function. 5083 } 5084 propertyHasChanged(_) { 5085 // unused for PU 5086 // need to overwrite impl of base class with empty function. 5087 } 5088 propertyRead(_) { 5089 // unused for PU 5090 // need to overwrite impl of base class with empty function. 5091 } 5092} 5093ObservedPropertyAbstractPU.DelayedNotifyChangesEnum = (_a = class { 5094 }, 5095 _a.do_not_delay = 0, 5096 _a.delay_none_pending = 1, 5097 _a.delay_notification_pending = 2, 5098 _a); 5099class PropertyDependencies { 5100 constructor() { 5101 // dependencies for property -> elmtId 5102 // variable read during render adds elmtId 5103 // variable assignment causes elmtId to need re-render. 5104 // UINode with elmtId deletion needs elmtId to be removed from all records, see purgeDependenciesForElmtId 5105 this.propertyDependencies_ = new Set(); 5106 // dependencies on individual object properties 5107 this.trackedObjectPropertyDependencies_ = new Map(); 5108 } 5109 getAllPropertyDependencies() { 5110 5111 return this.propertyDependencies_; 5112 } 5113 addPropertyDependency(elmtId) { 5114 this.propertyDependencies_.add(elmtId); 5115 5116 } 5117 purgeDependenciesForElmtId(rmElmtId) { 5118 5119 this.propertyDependencies_.delete(rmElmtId); 5120 5121 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 5122 propertyElmtId.delete(rmElmtId); 5123 5124 }); 5125 } 5126 addTrackedObjectPropertyDependency(readProperty, elmtId) { 5127 let dependentElmtIds = this.trackedObjectPropertyDependencies_.get(readProperty); 5128 if (!dependentElmtIds) { 5129 dependentElmtIds = new Set(); 5130 this.trackedObjectPropertyDependencies_.set(readProperty, dependentElmtIds); 5131 } 5132 dependentElmtIds.add(elmtId); 5133 5134 } 5135 getTrackedObjectPropertyDependencies(changedObjectProperty, debugInfo) { 5136 const dependentElmtIds = this.trackedObjectPropertyDependencies_.get(changedObjectProperty) || new Set(); 5137 5138 return dependentElmtIds; 5139 } 5140 dumpInfoDependencies(owningView = undefined, dumpDependantElements) { 5141 const formatElmtId = owningView ? (elmtId => owningView.debugInfoElmtId(elmtId)) : (elmtId => elmtId); 5142 let result = ''; 5143 const arr = Array.from(this.propertyDependencies_).map(formatElmtId); 5144 if (dumpDependantElements) { 5145 return (arr.length > 1 ? arr.join(', ') : arr[0]); 5146 } 5147 if (!this.trackedObjectPropertyDependencies_.size) { 5148 result += `dependencies: variable assignment affects elmtIds: ${Array.from(this.propertyDependencies_).map(formatElmtId).join(', ')}`; 5149 return result; 5150 } 5151 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 5152 result += `dependencies: property '@Track ${propertyName}' change affects elmtIds: ${Array.from(propertyElmtId).map(formatElmtId).join(', ')}`; 5153 }); 5154 return result; 5155 } 5156 dumpInfoDependenciesObj(owningView = undefined, isTrackedMode, isProfiler) { 5157 const formatElmtId = owningView ? (elmtId => owningView.debugInfoElmtId(elmtId, isProfiler)) : (elmtId => elmtId); 5158 let trackedObjectPropertyDependenciesDumpInfo = new Map(); 5159 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 5160 trackedObjectPropertyDependenciesDumpInfo.set(propertyName, Array.from(propertyElmtId).map(formatElmtId)); 5161 }); 5162 let PropertyDependenciesInfo = { 5163 mode: isTrackedMode ? 'Track Mode' : 'Compatible Mode', 5164 trackPropertiesDependencies: MapInfo.toObject(trackedObjectPropertyDependenciesDumpInfo).keyToValue, 5165 propertyDependencies: Array.from(this.propertyDependencies_).map(formatElmtId), 5166 }; 5167 return PropertyDependenciesInfo; 5168 } 5169 hasDependencies() { 5170 return this.propertyDependencies_.size > 0 || this.trackedObjectPropertyDependencies_.size > 0; 5171 } 5172} 5173/* 5174 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 5175 * Licensed under the Apache License, Version 2.0 (the "License"); 5176 * you may not use this file except in compliance with the License. 5177 * You may obtain a copy of the License at 5178 * 5179 * http://www.apache.org/licenses/LICENSE-2.0 5180 * 5181 * Unless required by applicable law or agreed to in writing, software 5182 * distributed under the License is distributed on an "AS IS" BASIS, 5183 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5184 * See the License for the specific language governing permissions and 5185 * limitations under the License. 5186 */ 5187/** 5188 * ObservedPropertyObjectPU 5189 * implementation of @State and @Provide decorated variables of type class object 5190 * 5191 * all definitions in this file are framework internal 5192 * 5193 * class that holds an actual property value of type T 5194 * uses its base class to manage subscribers to this 5195 * property. 5196*/ 5197class ObservedPropertyPU extends ObservedPropertyAbstractPU { 5198 constructor(localInitValue, owningView, propertyName) { 5199 super(owningView, propertyName); 5200 this.setValueInternal(localInitValue); 5201 this.setDecoratorInfo('@State'); 5202 } 5203 aboutToBeDeleted(unsubscribeMe) { 5204 this.unsubscribeWrappedObject(); 5205 this.removeSubscriber(unsubscribeMe); 5206 super.aboutToBeDeleted(); 5207 } 5208 /** 5209 * Called by a SynchedPropertyObjectTwoWayPU (@Link, @Consume) that uses this as sync peer when it has changed 5210 * @param eventSource 5211 */ 5212 syncPeerHasChanged(eventSource) { 5213 5214 this.notifyPropertyHasChangedPU(); 5215 } 5216 syncPeerTrackedPropertyHasChanged(eventSource, changedTrackedObjectPropertyName) { 5217 5218 this.notifyTrackedObjectPropertyHasChanged(changedTrackedObjectPropertyName); 5219 } 5220 /** 5221 * Wrapped ObservedObjectPU has changed 5222 * @param souceObject 5223 * @param changedPropertyName 5224 */ 5225 objectPropertyHasChangedPU(souceObject, changedPropertyName) { 5226 5227 this.notifyPropertyHasChangedPU(); 5228 } 5229 unsubscribeWrappedObject() { 5230 if (this.wrappedValue_) { 5231 if (this.wrappedValue_ instanceof SubscribableAbstract) { 5232 this.wrappedValue_.removeOwningProperty(this); 5233 } 5234 else { 5235 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 5236 // make sure the ObservedObject no longer has a read callback function 5237 // assigned to it 5238 ObservedObject.unregisterPropertyReadCb(this.wrappedValue_); 5239 } 5240 } 5241 } 5242 /* 5243 actually update this.wrappedValue_ 5244 called needs to do value change check 5245 and also notify with this.aboutToChange(); 5246 */ 5247 setValueInternal(newValue) { 5248 5249 if (newValue === this.wrappedValue_) { 5250 5251 5252 return false; 5253 } 5254 if (!this.checkIsSupportedValue(newValue)) { 5255 5256 return false; 5257 } 5258 this.unsubscribeWrappedObject(); 5259 if (!newValue || typeof newValue !== 'object') { 5260 // undefined, null, simple type: 5261 // nothing to subscribe to in case of new value undefined || null || simple type 5262 this.wrappedValue_ = newValue; 5263 } 5264 else if (newValue instanceof SubscribableAbstract) { 5265 5266 this.wrappedValue_ = newValue; 5267 this.wrappedValue_.addOwningProperty(this); 5268 } 5269 else if (ObservedObject.IsObservedObject(newValue)) { 5270 5271 ObservedObject.addOwningProperty(newValue, this); 5272 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(newValue); 5273 this.wrappedValue_ = newValue; 5274 } 5275 else { 5276 5277 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 5278 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.wrappedValue_); 5279 } 5280 5281 return true; 5282 } 5283 get() { 5284 5285 5286 this.recordPropertyDependentUpdate(); 5287 if (this.shouldInstallTrackedObjectReadCb) { 5288 5289 ObservedObject.registerPropertyReadCb(this.wrappedValue_, this.onOptimisedObjectPropertyRead, this); 5290 } 5291 else { 5292 5293 } 5294 5295 return this.wrappedValue_; 5296 } 5297 getUnmonitored() { 5298 5299 // unmonitored get access , no call to notifyPropertyRead ! 5300 return this.wrappedValue_; 5301 } 5302 set(newValue) { 5303 if (this.wrappedValue_ === newValue) { 5304 5305 return; 5306 } 5307 5308 const oldValue = this.wrappedValue_; 5309 if (this.setValueInternal(newValue)) { 5310 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.wrappedValue_, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5311 } 5312 } 5313 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 5314 5315 const renderingElmtId = this.getRenderingElmtId(); 5316 if (renderingElmtId >= 0) { 5317 if (!isTracked) { 5318 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 5319 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 5320 } 5321 else { 5322 5323 // only record dependency when 5324 // 1 - currently rendering or re-rendering 5325 // TODO room for further optimization: if not an expression in updateFunc, only first time render needs to record 5326 // because there can be change to depended variables unless one of the bindings is a JS expression 5327 // 2 - the changed ObservedObject is the wrapped object. The situation where it can be different is after a value assignment. 5328 if (this.getUnmonitored() === readObservedObject) { 5329 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 5330 } 5331 } 5332 } 5333 5334 } 5335} 5336// class definitions for backward compatibility 5337class ObservedPropertyObjectPU extends ObservedPropertyPU { 5338} 5339class ObservedPropertySimplePU extends ObservedPropertyPU { 5340} 5341/* 5342 * Copyright (c) 2022 Huawei Device Co., Ltd. 5343 * Licensed under the Apache License, Version 2.0 (the "License"); 5344 * you may not use this file except in compliance with the License. 5345 * You may obtain a copy of the License at 5346 * 5347 * http://www.apache.org/licenses/LICENSE-2.0 5348 * 5349 * Unless required by applicable law or agreed to in writing, software 5350 * distributed under the License is distributed on an "AS IS" BASIS, 5351 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5352 * See the License for the specific language governing permissions and 5353 * limitations under the License. 5354 */ 5355/** 5356 * SynchedPropertyObjectOneWayPU 5357 * implementation of @Prop decorated variables of type class object 5358 * 5359 * all definitions in this file are framework internal 5360 * 5361 */ 5362/** 5363 * Initialisation scenarios: 5364 * ------------------------- 5365 * 5366 * 1 - no local initialization, source provided (its ObservedObject value) 5367 * wrap the ObservedObject into an ObservedPropertyObjectPU 5368 * deep copy the ObservedObject into localCopyObservedObject_ 5369 * 5370 * 2 - local initialization, no source provided 5371 * app transpiled code calls set 5372 * leave source_ undefined 5373 * no deep copy needed, but provided local init might need wrapping inside an ObservedObject to set to 5374 * localCopyObservedObject_ 5375 * 5376 * 3 local initialization, source provided (its ObservedObject value) 5377 * current app transpiled code is not optional 5378 * sets source in constructor, as in case 1 5379 * calls set() to set the source value, but this will not deepcopy 5380 * 5381 * Update scenarios: 5382 * ----------------- 5383 * 5384 * 1- assignment of a new Object value: this.aProp = new ClassA() 5385 * rhs can be ObservedObject because of @Observed decoration or now 5386 * notifyPropertyHasChangedPU 5387 * 5388 * 2- local ObservedObject member property change 5389 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored in localCopyObservedObject_ 5390 * no need to copy, notifyPropertyHasChangedPU 5391 * 5392 * 3- Rerender of the custom component triggered from the parent 5393 * reset() is called (code generated by the transpiler), set the value of source_ , if that causes a change will call syncPeerHasChanged 5394 * syncPeerHasChanged need to deep copy the ObservedObject from source to localCopyObservedObject_ 5395 * notifyPropertyHasChangedPU 5396 * 5397 * 4- source_ ObservedObject member property change 5398 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored source_.getUnmonitored 5399 * notifyPropertyHasChangedPU 5400 */ 5401class SynchedPropertyOneWayPU extends ObservedPropertyAbstractPU { 5402 constructor(source, owningChildView, thisPropertyName) { 5403 super(owningChildView, thisPropertyName); 5404 this.setDecoratorInfo("@Prop"); 5405 if (source && (typeof (source) === 'object') && ('subscribeMe' in source)) { 5406 // code path for @(Local)StorageProp, the source is a ObservedPropertyObject<C> in a LocalStorage) 5407 this.source_ = source; 5408 this.sourceIsOwnObject = false; 5409 // subscribe to receive value change updates from LocalStorage source property 5410 this.source_.addSubscriber(this); 5411 } 5412 else { 5413 const sourceValue = source; 5414 if (this.checkIsSupportedValue(sourceValue)) { 5415 // code path for 5416 // 1- source is of same type C in parent, source is its value, not the backing store ObservedPropertyObject 5417 // 2- nested Object/Array inside observed another object/array in parent, source is its value 5418 5419 this.createSourceDependency(sourceValue); 5420 this.source_ = new ObservedPropertyObjectPU(sourceValue, this, this.getPropSourceObservedPropertyFakeName()); 5421 this.sourceIsOwnObject = true; 5422 } 5423 } 5424 if (this.source_ !== undefined) { 5425 this.resetLocalValue(this.source_.get(), /* needCopyObject */ true); 5426 } 5427 5428 } 5429 /* 5430 like a destructor, need to call this before deleting 5431 the property. 5432 */ 5433 aboutToBeDeleted() { 5434 if (this.source_) { 5435 this.source_.removeSubscriber(this); 5436 if (this.sourceIsOwnObject === true && this.source_.numberOfSubscrbers() === 0) { 5437 5438 this.source_.aboutToBeDeleted(); 5439 } 5440 this.source_ = undefined; 5441 } 5442 super.aboutToBeDeleted(); 5443 } 5444 // sync peer can be 5445 // 1. the embedded ObservedPropertyPU, followed by a reset when the owning ViewPU received a local update in parent 5446 // 2. a @Link or @Consume that uses this @Prop as a source. FIXME is this possible? - see the if (eventSource && this.source_ == eventSource) { 5447 syncPeerHasChanged(eventSource) { 5448 5449 if (this.source_ === undefined) { 5450 stateMgmtConsole.error(`${this.debugInfo()}: syncPeerHasChanged from peer ${eventSource && eventSource.debugInfo && eventSource.debugInfo()}. source_ undefined. Internal error.`); 5451 5452 return; 5453 } 5454 if (eventSource && this.source_ === eventSource) { 5455 // defensive programming: should always be the case! 5456 const newValue = this.source_.getUnmonitored(); 5457 if (this.checkIsSupportedValue(newValue)) { 5458 5459 if (this.resetLocalValue(newValue, /* needCopyObject */ true)) { 5460 this.notifyPropertyHasChangedPU(); 5461 } 5462 } 5463 } 5464 else { 5465 stateMgmtConsole.warn(`${this.debugInfo()}: syncPeerHasChanged: from peer '${eventSource === null || eventSource === void 0 ? void 0 : eventSource.debugInfo()}', Unexpected situation. syncPeerHasChanged from different sender than source_. Ignoring event.`); 5466 } 5467 5468 } 5469 syncPeerTrackedPropertyHasChanged(eventSource, changedPropertyName) { 5470 5471 if (this.source_ == undefined) { 5472 stateMgmtConsole.error(`${this.debugInfo()}: syncPeerTrackedPropertyHasChanged from peer ${eventSource && eventSource.debugInfo && eventSource.debugInfo()}. source_ undefined. Internal error.`); 5473 5474 return; 5475 } 5476 if (eventSource && this.source_ == eventSource) { 5477 // defensive programming: should always be the case! 5478 const newValue = this.source_.getUnmonitored(); 5479 if (this.checkIsSupportedValue(newValue)) { 5480 5481 if (this.resetLocalValue(newValue, /* needCopyObject */ true)) { 5482 this.notifyTrackedObjectPropertyHasChanged(changedPropertyName); 5483 } 5484 } 5485 } 5486 else { 5487 stateMgmtConsole.warn(`${this.debugInfo()}: syncPeerTrackedPropertyHasChanged: from peer '${eventSource === null || eventSource === void 0 ? void 0 : eventSource.debugInfo()}', Unexpected situation. syncPeerHasChanged from different sender than source_. Ignoring event.`); 5488 } 5489 5490 } 5491 getUnmonitored() { 5492 5493 // unmonitored get access , no call to notifyPropertyRead ! 5494 return this.localCopyObservedObject_; 5495 } 5496 get() { 5497 5498 5499 this.recordPropertyDependentUpdate(); 5500 if (this.shouldInstallTrackedObjectReadCb) { 5501 5502 ObservedObject.registerPropertyReadCb(this.localCopyObservedObject_, this.onOptimisedObjectPropertyRead, this); 5503 } 5504 else { 5505 5506 } 5507 5508 return this.localCopyObservedObject_; 5509 } 5510 // assignment to local variable in the form of this.aProp = <object value> 5511 set(newValue) { 5512 if (this.localCopyObservedObject_ === newValue) { 5513 5514 return; 5515 } 5516 5517 const oldValue = this.localCopyObservedObject_; 5518 if (this.resetLocalValue(newValue, /* needCopyObject */ false)) { 5519 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.localCopyObservedObject_, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5520 } 5521 } 5522 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 5523 5524 const renderingElmtId = this.getRenderingElmtId(); 5525 if (renderingElmtId >= 0) { 5526 if (!isTracked) { 5527 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 5528 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 5529 } 5530 else { 5531 5532 if (this.getUnmonitored() === readObservedObject) { 5533 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 5534 } 5535 } 5536 } 5537 5538 } 5539 // called when updated from parent 5540 // during parent ViewPU rerender, calls update lambda of child ViewPU with @Prop variable 5541 // this lambda generated code calls ViewPU.updateStateVarsOfChildByElmtId, 5542 // calls inside app class updateStateVars() 5543 // calls reset() for each @Prop 5544 reset(sourceChangedValue) { 5545 5546 if (this.source_ !== undefined && this.checkIsSupportedValue(sourceChangedValue)) { 5547 // if this.source_.set causes an actual change, then, ObservedPropertyObject source_ will call syncPeerHasChanged method 5548 this.createSourceDependency(sourceChangedValue); 5549 this.source_.set(sourceChangedValue); 5550 } 5551 } 5552 createSourceDependency(sourceObject) { 5553 if (ObservedObject.IsObservedObject(sourceObject)) { 5554 5555 const fake = sourceObject[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY]; 5556 } 5557 } 5558 /* 5559 unsubscribe from previous wrapped ObjectObject 5560 take a shallow or (TODO) deep copy 5561 copied Object might already be an ObservedObject (e.g. becurse of @Observed decorator) or might be raw 5562 Therefore, conditionally wrap the object, then subscribe 5563 return value true only if localCopyObservedObject_ has been changed 5564 */ 5565 resetLocalValue(newObservedObjectValue, needCopyObject) { 5566 // note: We can not test for newObservedObjectValue == this.localCopyObservedObject_ 5567 // here because the object might still be the same, but some property of it has changed 5568 // this is added for stability test: Target of target is not Object/is not callable/ 5569 // InstanceOf error when target is not Callable/Can not get Prototype on non ECMA Object 5570 try { 5571 if (!this.checkIsSupportedValue(newObservedObjectValue)) { 5572 return false; 5573 } 5574 // unsubscribe from old local copy 5575 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 5576 this.localCopyObservedObject_.removeOwningProperty(this); 5577 } 5578 else { 5579 ObservedObject.removeOwningProperty(this.localCopyObservedObject_, this); 5580 // make sure the ObservedObject no longer has a read callback function 5581 // assigned to it 5582 ObservedObject.unregisterPropertyReadCb(this.localCopyObservedObject_); 5583 } 5584 } 5585 catch (error) { 5586 stateMgmtConsole.error(`${this.debugInfo()}, an error occurred in resetLocalValue: ${error.message}`); 5587 ArkTools.print("resetLocalValue SubscribableAbstract", SubscribableAbstract); 5588 ArkTools.print("resetLocalValue ObservedObject", ObservedObject); 5589 ArkTools.print("resetLocalValue this", this); 5590 let a = Reflect.getPrototypeOf(this); 5591 ArkTools.print("resetLocalVale getPrototypeOf", a); 5592 throw error; 5593 } 5594 // shallow/deep copy value 5595 // needed whenever newObservedObjectValue comes from source 5596 // not needed on a local set (aka when called from set() method) 5597 if (needCopyObject) { 5598 ViewPU.pauseRendering(); 5599 this.localCopyObservedObject_ = this.copyObject(newObservedObjectValue, this.info_); 5600 ViewPU.restoreRendering(); 5601 } 5602 else { 5603 this.localCopyObservedObject_ = newObservedObjectValue; 5604 } 5605 if (typeof this.localCopyObservedObject_ === 'object') { 5606 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 5607 // deep copy will copy Set of subscribers as well. But local copy only has its own subscribers 5608 // not those of its parent value. 5609 this.localCopyObservedObject_.clearOwningProperties(); 5610 this.localCopyObservedObject_.addOwningProperty(this); 5611 } 5612 else if (ObservedObject.IsObservedObject(this.localCopyObservedObject_)) { 5613 // case: new ObservedObject 5614 ObservedObject.addOwningProperty(this.localCopyObservedObject_, this); 5615 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.localCopyObservedObject_); 5616 } 5617 else { 5618 // wrap newObservedObjectValue raw object as ObservedObject and subscribe to it 5619 5620 this.localCopyObservedObject_ = ObservedObject.createNew(this.localCopyObservedObject_, this); 5621 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.localCopyObservedObject_); 5622 } 5623 5624 } 5625 return true; 5626 } 5627 copyObject(value, propName) { 5628 // ViewStackProcessor.getApiVersion function is not present in API9 5629 // therefore shallowCopyObject will always be used in API version 9 and before 5630 // but the code in this file is the same regardless of API version 5631 5632 return ((typeof ViewStackProcessor['getApiVersion'] == 'function') && 5633 (ViewStackProcessor['getApiVersion']() >= 10)) 5634 ? this.deepCopyObject(value, propName) 5635 : this.shallowCopyObject(value, propName); 5636 } 5637 // API 9 code path 5638 shallowCopyObject(value, propName) { 5639 let rawValue = ObservedObject.GetRawObject(value); 5640 let copy; 5641 if (!rawValue || typeof rawValue !== 'object') { 5642 copy = rawValue; 5643 } 5644 else if (typeof rawValue != 'object') { 5645 // FIXME would it be better to throw Exception here? 5646 stateMgmtConsole.error(`${this.debugInfo()}: shallowCopyObject: request to copy non-object value, actual type is '${typeof rawValue}'. Internal error! Setting copy:=original value.`); 5647 copy = rawValue; 5648 } 5649 else if (rawValue instanceof Array) { 5650 // case Array inside ObservedObject 5651 copy = ObservedObject.createNew([...rawValue], this); 5652 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 5653 } 5654 else if (rawValue instanceof Date) { 5655 // case Date inside ObservedObject 5656 let d = new Date(); 5657 d.setTime(rawValue.getTime()); 5658 // subscribe, also Date gets wrapped / proxied by ObservedObject 5659 copy = ObservedObject.createNew(d, this); 5660 } 5661 else if (rawValue instanceof SubscribableAbstract) { 5662 // case SubscribableAbstract, no wrapping inside ObservedObject 5663 copy = Object.assign({}, rawValue); 5664 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 5665 if (copy instanceof SubscribableAbstract) { 5666 // subscribe 5667 copy.addOwningProperty(this); 5668 } 5669 } 5670 else if (typeof rawValue === 'object') { 5671 // case Object that is not Array, not Date, not SubscribableAbstract 5672 copy = ObservedObject.createNew(Object.assign({}, rawValue), this); 5673 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 5674 } 5675 else { 5676 // TODO in PR "F": change to exception throwing: 5677 stateMgmtConsole.error(`${this.debugInfo()}: shallow failed. Attempt to copy unsupported value of type '${typeof rawValue}' .`); 5678 copy = rawValue; 5679 } 5680 return copy; 5681 } 5682 // API 10 code path 5683 deepCopyObject(obj, variable) { 5684 let copy = SynchedPropertyObjectOneWayPU.deepCopyObjectInternal(obj, variable); 5685 // this subscribe to the top level object/array of the copy 5686 // same as shallowCopy does 5687 if ((obj instanceof SubscribableAbstract) && 5688 (copy instanceof SubscribableAbstract)) { 5689 copy.addOwningProperty(this); 5690 } 5691 else if (ObservedObject.IsObservedObject(obj) && ObservedObject.IsObservedObject(copy)) { 5692 ObservedObject.addOwningProperty(copy, this); 5693 } 5694 return copy; 5695 ; 5696 } 5697 // do not use this function from outside unless it is for testing purposes. 5698 static deepCopyObjectInternal(obj, variable) { 5699 if (!obj || typeof obj !== 'object') { 5700 return obj; 5701 } 5702 let copiedObjects = new Map(); 5703 return getDeepCopyOfObjectRecursive(obj); 5704 function getDeepCopyOfObjectRecursive(obj) { 5705 if (!obj || typeof obj !== 'object') { 5706 return obj; 5707 } 5708 const alreadyCopiedObject = copiedObjects.get(obj); 5709 if (alreadyCopiedObject) { 5710 5711 return alreadyCopiedObject; 5712 } 5713 let copy; 5714 if (obj instanceof Set) { 5715 copy = new Set(); 5716 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5717 copiedObjects.set(obj, copy); 5718 obj.forEach((setKey) => { 5719 copy.add(getDeepCopyOfObjectRecursive(setKey)); 5720 }); 5721 } 5722 else if (obj instanceof Map) { 5723 copy = new Map(); 5724 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5725 copiedObjects.set(obj, copy); 5726 obj.forEach((mapValue, mapKey) => { 5727 copy.set(mapKey, getDeepCopyOfObjectRecursive(mapValue)); 5728 }); 5729 } 5730 else if (obj instanceof Date) { 5731 copy = new Date(); 5732 copy.setTime(obj.getTime()); 5733 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5734 copiedObjects.set(obj, copy); 5735 } 5736 else if (obj instanceof Object) { 5737 copy = Array.isArray(obj) ? [] : {}; 5738 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5739 copiedObjects.set(obj, copy); 5740 } 5741 else { 5742 /** 5743 * As we define a variable called 'copy' with no initial value before this if/else branch, 5744 * so it will crash at Reflect.set when obj is not instance of Set/Map/Date/Object/Array. 5745 * This branch is for those known special cases: 5746 * 1、obj is a NativePointer 5747 * 2、obj is a @Sendable decorated class 5748 * In case the application crash directly, use shallow copy instead. 5749 * Will use new API when ark engine team is ready which will be a more elegant way. 5750 * If we difine the copy like 'let copy = {};', 5751 * it will not crash but copy will be a normal JSObject, not a @Sendable object. 5752 * To keep the functionality of @Sendable, still not define copy with initial value. 5753 */ 5754 stateMgmtConsole.warn('DeepCopy target obj is not instance of Set/Date/Map/Object/Array, will use shallow copy instead.'); 5755 return obj; 5756 } 5757 Object.keys(obj).forEach((objKey) => { 5758 copy[objKey] = getDeepCopyOfObjectRecursive(obj[objKey]); 5759 }); 5760 return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, undefined) : copy; 5761 } 5762 } 5763} 5764// class definitions for backward compatibility 5765class SynchedPropertySimpleOneWayPU extends SynchedPropertyOneWayPU { 5766} 5767class SynchedPropertyObjectOneWayPU extends SynchedPropertyOneWayPU { 5768} 5769/* 5770 * Copyright (c) 2022 Huawei Device Co., Ltd. 5771 * Licensed under the Apache License, Version 2.0 (the "License"); 5772 * you may not use this file except in compliance with the License. 5773 * You may obtain a copy of the License at 5774 * 5775 * http://www.apache.org/licenses/LICENSE-2.0 5776 * 5777 * Unless required by applicable law or agreed to in writing, software 5778 * distributed under the License is distributed on an "AS IS" BASIS, 5779 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5780 * See the License for the specific language governing permissions and 5781 * limitations under the License. 5782 */ 5783/** 5784 * SynchedPropertyObjectTwoWayPU 5785 * implementation of @Link and @Consume decorated variables of type class object 5786 * 5787 * all definitions in this file are framework internal 5788*/ 5789class SynchedPropertyTwoWayPU extends ObservedPropertyAbstractPU { 5790 constructor(source, owningChildView, thisPropertyName) { 5791 super(owningChildView, thisPropertyName); 5792 this.source_ = source; 5793 if (this.source_) { 5794 // register to the parent property 5795 this.source_.addSubscriber(this); 5796 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.source_.getUnmonitored()); 5797 } 5798 else { 5799 throw new SyntaxError(`${this.debugInfo()}: constructor: source variable in parent/ancestor @Component must be defined. Application error!`); 5800 } 5801 this.setDecoratorInfo("@Link"); 5802 } 5803 /* 5804 like a destructor, need to call this before deleting 5805 the property. 5806 */ 5807 aboutToBeDeleted() { 5808 // unregister from parent of this link 5809 if (this.source_) { 5810 this.source_.removeSubscriber(this); 5811 // unregister from the ObservedObject 5812 ObservedObject.removeOwningProperty(this.source_.getUnmonitored(), this); 5813 } 5814 super.aboutToBeDeleted(); 5815 } 5816 isStorageLinkProp() { 5817 return (this.source_ && this.source_ instanceof ObservedPropertyAbstract && (!(this.source_ instanceof ObservedPropertyAbstractPU))); 5818 } 5819 setObject(newValue) { 5820 if (!this.source_) { 5821 throw new SyntaxError(`${this.debugInfo()}: setObject (assign a new value), no source variable in parent/ancestor \ 5822 @Component. Application error.`); 5823 } 5824 if (this.getUnmonitored() === newValue) { 5825 5826 return; 5827 } 5828 5829 if (this.checkIsSupportedValue(newValue)) { 5830 // the source_ ObservedProperty will call: this.syncPeerHasChanged(newValue); 5831 this.source_.set(newValue); 5832 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(newValue); 5833 } 5834 } 5835 /** 5836 * Called when sync peer ObservedPropertyObject or SynchedPropertyObjectTwoWay has changed value 5837 * that peer can be in either parent or child component if 'this' is used for a @Link 5838 * that peer can be in either ancestor or descendant component if 'this' is used for a @Consume 5839 * @param eventSource 5840 */ 5841 syncPeerHasChanged(eventSource) { 5842 5843 if (!this.changeNotificationIsOngoing_) { 5844 5845 this.notifyPropertyHasChangedPU(); 5846 } 5847 5848 } 5849 syncPeerTrackedPropertyHasChanged(eventSource, changedTrackedObjectPropertyName) { 5850 5851 if (!this.changeNotificationIsOngoing_) { 5852 5853 this.notifyTrackedObjectPropertyHasChanged(changedTrackedObjectPropertyName); 5854 } 5855 5856 } 5857 getUnmonitored() { 5858 5859 return (this.source_ ? this.source_.getUnmonitored() : undefined); 5860 } 5861 // get 'read through` from the ObservedProperty 5862 get() { 5863 5864 5865 this.recordPropertyDependentUpdate(); 5866 const result = this.getUnmonitored(); 5867 if (this.shouldInstallTrackedObjectReadCb) { 5868 5869 ObservedObject.registerPropertyReadCb(result, this.onOptimisedObjectPropertyRead, this); 5870 } 5871 else { 5872 5873 } 5874 5875 return result; 5876 } 5877 // set 'writes through` to the ObservedProperty 5878 set(newValue) { 5879 5880 if (this.getUnmonitored() === newValue) { 5881 5882 5883 return; 5884 } 5885 5886 // avoid circular notifications @Link -> source @State -> other but also back to same @Link 5887 this.changeNotificationIsOngoing_ = true; 5888 let oldValue = this.getUnmonitored(); 5889 this.setObject(newValue); 5890 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ newValue, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5891 this.changeNotificationIsOngoing_ = false; 5892 5893 } 5894 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 5895 5896 const renderingElmtId = this.getRenderingElmtId(); 5897 if (renderingElmtId >= 0) { 5898 if (!isTracked) { 5899 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 5900 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 5901 } 5902 else { 5903 5904 if (this.getUnmonitored() === readObservedObject) { 5905 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 5906 } 5907 } 5908 } 5909 5910 } 5911} 5912// class definitions for backward compatibility 5913class SynchedPropertyObjectTwoWayPU extends SynchedPropertyTwoWayPU { 5914} 5915class SynchedPropertySimpleTwoWayPU extends SynchedPropertyTwoWayPU { 5916} 5917/* 5918 * Copyright (c) 2022 Huawei Device Co., Ltd. 5919 * Licensed under the Apache License, Version 2.0 (the "License"); 5920 * you may not use this file except in compliance with the License. 5921 * You may obtain a copy of the License at 5922 * 5923 * http://www.apache.org/licenses/LICENSE-2.0 5924 * 5925 * Unless required by applicable law or agreed to in writing, software 5926 * distributed under the License is distributed on an "AS IS" BASIS, 5927 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5928 * See the License for the specific language governing permissions and 5929 * limitations under the License. 5930 */ 5931/** 5932 * SynchedPropertyNestedObjectPU 5933 * implementation of @ObjectLink decorated variables 5934 * 5935 * all definitions in this file are framework internal 5936 * 5937 */ 5938class SynchedPropertyNestedObjectPU extends ObservedPropertyAbstractPU { 5939 /** 5940 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 5941 * example 5942 * this.b.$a with b of type PC and a of type C, or 5943 * this.$b[5] with this.b of type PC and array item b[5] of type C; 5944 * 5945 * @param subscribeMe 5946 * @param propName 5947 */ 5948 constructor(obsObject, owningChildView, propertyName) { 5949 super(owningChildView, propertyName); 5950 this.obsObject_ = undefined; 5951 this.createSourceDependency(obsObject); 5952 this.setValueInternal(obsObject); 5953 this.setDecoratorInfo("@ObjectLink"); 5954 } 5955 /* 5956 like a destructor, need to call this before deleting 5957 the property. 5958 */ 5959 aboutToBeDeleted() { 5960 // unregister from the ObservedObject 5961 ObservedObject.removeOwningProperty(this.obsObject_, this); 5962 super.aboutToBeDeleted(); 5963 } 5964 getUnmonitored() { 5965 5966 // unmonitored get access , no call to notifyPropertyRead ! 5967 return this.obsObject_; 5968 } 5969 // get 'read through` from the ObservedProperty 5970 get() { 5971 5972 5973 this.recordPropertyDependentUpdate(); 5974 if (this.shouldInstallTrackedObjectReadCb) { 5975 5976 ObservedObject.registerPropertyReadCb(this.obsObject_, this.onOptimisedObjectPropertyRead, this); 5977 } 5978 else { 5979 5980 } 5981 5982 return this.obsObject_; 5983 } 5984 // parent ViewPU rerender, runs update lambda with child ViewPU that contains a @ObjectLink 5985 // calls ViewPU.updateStateVarsByElmtId, calls updateStateVars in application class, calls this 'set' function 5986 set(newValue) { 5987 if (this.obsObject_ === newValue) { 5988 5989 return; 5990 } 5991 5992 const oldValue = this.obsObject_; 5993 if (this.setValueInternal(newValue)) { 5994 this.createSourceDependency(newValue); 5995 // notify value change to subscribing View 5996 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.obsObject_, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5997 } 5998 } 5999 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 6000 6001 const renderingElmtId = this.getRenderingElmtId(); 6002 if (renderingElmtId >= 0) { 6003 if (!isTracked) { 6004 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 6005 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 6006 } 6007 else { 6008 6009 if (this.getUnmonitored() === readObservedObject) { 6010 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 6011 } 6012 } 6013 } 6014 6015 } 6016 createSourceDependency(sourceObject) { 6017 if (ObservedObject.IsObservedObject(sourceObject)) { 6018 6019 const fake = sourceObject[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY]; 6020 } 6021 } 6022 setValueInternal(newValue) { 6023 if (!this.checkIsObject(newValue)) { 6024 return false; 6025 } 6026 if (this.obsObject_ != undefined) { 6027 if (this.obsObject_ instanceof SubscribableAbstract) { 6028 // unregister from SubscribableAbstract object 6029 this.obsObject_.removeOwningProperty(this); 6030 } 6031 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 6032 // unregister from the ObservedObject 6033 ObservedObject.removeOwningProperty(this.obsObject_, this); 6034 // make sure the ObservedObject no longer has a read callback function 6035 // assigned to it 6036 ObservedObject.unregisterPropertyReadCb(this.obsObject_); 6037 } 6038 } 6039 this.obsObject_ = newValue; 6040 if (this.obsObject_ != undefined) { 6041 if (this.obsObject_ instanceof SubscribableAbstract) { 6042 // register to SubscribableAbstract object 6043 this.obsObject_.addOwningProperty(this); 6044 } 6045 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 6046 // register to the ObservedObject 6047 ObservedObject.addOwningProperty(this.obsObject_, this); 6048 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.obsObject_); 6049 } 6050 else { 6051 stateMgmtConsole.applicationWarn(`${this.debugInfo()}: set/init (method setValueInternal): assigned value is not 6052 be decorated by @Observed. Value changes will not be observed and UI will not update.`); 6053 } 6054 } 6055 return true; 6056 } 6057} 6058/** backward compatibility after typo in classname fix */ 6059class SynchedPropertyNesedObjectPU extends SynchedPropertyNestedObjectPU { 6060} 6061/* 6062 * Copyright (c) 2023 Huawei Device Co., Ltd. 6063 * Licensed under the Apache License, Version 2.0 (the "License"); 6064 * you may not use this file except in compliance with the License. 6065 * You may obtain a copy of the License at 6066 * 6067 * http://www.apache.org/licenses/LICENSE-2.0 6068 * 6069 * Unless required by applicable law or agreed to in writing, software 6070 * distributed under the License is distributed on an "AS IS" BASIS, 6071 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6072 * See the License for the specific language governing permissions and 6073 * limitations under the License. 6074 */ 6075// defined a globle function to clean up the removeItems when idle 6076function uiNodeCleanUpIdleTask() { 6077 6078 UINodeRegisterProxy.obtainDeletedElmtIds(); 6079 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 6080 UINodeRegisterProxy.cleanUpDeadReferences(); 6081} 6082class UINodeRegisterProxy { 6083 constructor() { 6084 this.removeElementsInfo_ = new Array(); 6085 } 6086 static obtainDeletedElmtIds() { 6087 6088 if ((!UINodeRegisterProxy.instance_.obtainDeletedElmtIds) || typeof UINodeRegisterProxy.instance_.obtainDeletedElmtIds !== 'function') { 6089 stateMgmtConsole.error(`UINodeRegisterProxy obtainDeletedElmtIds is not a function: ${UINodeRegisterProxy.instance_.obtainDeletedElmtIds}.`); 6090 } 6091 else { 6092 UINodeRegisterProxy.instance_.obtainDeletedElmtIds(); 6093 } 6094 } 6095 // FIXME unregisterElmtIdsFromIViews needs adaptation 6096 static unregisterElmtIdsFromIViews() { 6097 6098 UINodeRegisterProxy.instance_.unregisterElmtIdsFromIViews(); 6099 } 6100 // unregisters all the received removedElements in func parameter 6101 static unregisterRemovedElmtsFromViewPUs(removedElements) { 6102 6103 UINodeRegisterProxy.instance_.populateRemoveElementInfo(removedElements); 6104 UINodeRegisterProxy.instance_.unregisterElmtIdsFromIViews(); 6105 } 6106 static registerModifierElmtDeleteCallback(callback) { 6107 if (UINodeRegisterProxy.modifierElmtDeleteCallback_) { 6108 return; 6109 } 6110 UINodeRegisterProxy.modifierElmtDeleteCallback_ = callback; 6111 } 6112 populateRemoveElementInfo(removedElements) { 6113 for (const elmtId of removedElements) { 6114 this.removeElementsInfo_.push(elmtId); 6115 } 6116 } 6117 /* just get the remove items from the native side 6118 */ 6119 obtainDeletedElmtIds() { 6120 6121 let removedElementsInfo = new Array(); 6122 ViewStackProcessor.moveDeletedElmtIds(removedElementsInfo); 6123 6124 this.removeElementsInfo_ = removedElementsInfo; 6125 } 6126 unregisterElmtIdsFromIViews() { 6127 6128 if (this.removeElementsInfo_.length === 0) { 6129 6130 return; 6131 } 6132 let owningView; 6133 this.removeElementsInfo_.forEach((elmtId) => { 6134 const owningViewPUWeak = UINodeRegisterProxy.ElementIdToOwningViewPU_.get(elmtId); 6135 if (owningViewPUWeak !== undefined) { 6136 owningView = owningViewPUWeak.deref(); 6137 if (owningView) { 6138 owningView.purgeDeleteElmtId(elmtId); 6139 } 6140 else { 6141 6142 } 6143 if (UINodeRegisterProxy.modifierElmtDeleteCallback_) { 6144 UINodeRegisterProxy.modifierElmtDeleteCallback_(elmtId); 6145 } 6146 } 6147 else { 6148 6149 } 6150 ObserveV2.getObserve().clearBinding(elmtId); 6151 }); 6152 this.removeElementsInfo_.length = 0; 6153 } 6154 static cleanUpDeadReferences() { 6155 6156 ObserveV2.getObserve().cleanUpDeadReferences(); 6157 } 6158} 6159UINodeRegisterProxy.notRecordingDependencies = -1; 6160UINodeRegisterProxy.monitorIllegalV1V2StateAccess = -2; 6161UINodeRegisterProxy.instance_ = new UINodeRegisterProxy(); 6162UINodeRegisterProxy.ElementIdToOwningViewPU_ = new Map(); 6163/* 6164 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 6165 * Licensed under the Apache License, Version 2.0 (the "License"); 6166 * you may not use this file except in compliance with the License. 6167 * You may obtain a copy of the License at 6168 * 6169 * http://www.apache.org/licenses/LICENSE-2.0 6170 * 6171 * Unless required by applicable law or agreed to in writing, software 6172 * distributed under the License is distributed on an "AS IS" BASIS, 6173 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6174 * See the License for the specific language governing permissions and 6175 * limitations under the License. 6176 * 6177 * * ViewPU - View for Partial Update 6178 * 6179* all definitions in this file are framework internal 6180*/ 6181class ViewPU extends PUV2ViewBase { 6182 /** 6183 * Create a View 6184 * 6185 * 1. option: top level View, specify 6186 * - compilerAssignedUniqueChildId must specify 6187 * - parent=undefined 6188 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 6189 * in this View or descendant Views. 6190 * 6191 * 2. option: not a top level View 6192 * - compilerAssignedUniqueChildId must specify 6193 * - parent must specify 6194 * - localStorage do not specify, will inherit from parent View. 6195 * 6196 */ 6197 constructor(parent, localStorage, elmtId = UINodeRegisterProxy.notRecordingDependencies, extraInfo = undefined) { 6198 var _a; 6199 super(parent, elmtId, extraInfo); 6200 // flag for initial rendering or re-render on-going. 6201 this.isRenderInProgress = false; 6202 // flag for initial rendering being done 6203 this.isInitialRenderDone = false; 6204 this.runReuse_ = false; 6205 this.watchedProps = new Map(); 6206 this.recycleManager_ = undefined; 6207 this.hasBeenRecycled_ = false; 6208 this.preventRecursiveRecycle_ = false; 6209 this.delayRecycleNodeRerender = false; 6210 this.delayRecycleNodeRerenderDeep = false; 6211 // @Provide'd variables by this class and its ancestors 6212 this.providedVars_ = new Map(); 6213 // my LocalStorage instance, shared with ancestor Views. 6214 // create a default instance on demand if none is initialized 6215 this.localStoragebackStore_ = undefined; 6216 /** 6217 * on first render create a new Instance of Repeat 6218 * on re-render connect to existing instance 6219 * @param arr 6220 * @returns 6221 */ 6222 this.__mkRepeatAPI = (arr) => { 6223 // factory is for future extensions, currently always return the same 6224 const elmtId = this.getCurrentlyRenderedElmtId(); 6225 let repeat = this.elmtId2Repeat_.get(elmtId); 6226 if (!repeat) { 6227 repeat = new __Repeat(this, arr); 6228 this.elmtId2Repeat_.set(elmtId, repeat); 6229 } 6230 else { 6231 repeat.updateArr(arr); 6232 } 6233 return repeat; 6234 }; 6235 // if set use the elmtId also as the ViewPU object's subscribable id. 6236 // these matching is requirement for updateChildViewById(elmtId) being able to 6237 // find the child ViewPU object by given elmtId 6238 //this.id_ = elmtId == UINodeRegisterProxy.notRecordingDependencies ? SubscriberManager.MakeId() : elmtId; 6239 this.localStoragebackStore_ = undefined; 6240 6241 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onViewPUCreate(this); 6242 if (localStorage) { 6243 this.localStorage_ = localStorage; 6244 6245 } 6246 SubscriberManager.Add(this); 6247 6248 } 6249 get ownObservedPropertiesStore_() { 6250 if (!this.ownObservedPropertiesStore__) { 6251 // lazy init 6252 this.ownObservedPropertiesStore__ = new Set(); 6253 this.obtainOwnObservedProperties(); 6254 } 6255 return this.ownObservedPropertiesStore__; 6256 } 6257 obtainOwnObservedProperties() { 6258 let usesStateMgmtVersion = 0; 6259 Object.getOwnPropertyNames(this) 6260 .filter((propName) => { 6261 // do not include backing store, and ObserveV2/MonitorV2/ComputedV2 meta data objects 6262 return (propName.startsWith('__') && 6263 !propName.startsWith(ObserveV2.OB_PREFIX) && 6264 !propName.startsWith(MonitorV2.WATCH_PREFIX) && 6265 !propName.startsWith(ComputedV2.COMPUTED_PREFIX)); 6266 }) 6267 .forEach((propName) => { 6268 const stateVar = Reflect.get(this, propName); 6269 if (stateVar && typeof stateVar === 'object' && 'notifyPropertyHasChangedPU' in stateVar) { 6270 6271 this.ownObservedPropertiesStore_.add(stateVar); 6272 usesStateMgmtVersion = 2; 6273 } 6274 else { 6275 6276 } 6277 }); 6278 if (this.isViewV2 === true) { 6279 if (usesStateMgmtVersion === 2) { 6280 const error = `${this.debugInfo__()}: mixed use of stateMgmt V1 and V2 variable decorators. Application error!`; 6281 stateMgmtConsole.applicationError(error); 6282 throw new Error(error); 6283 } 6284 } 6285 6286 } 6287 get localStorage_() { 6288 if (!this.localStoragebackStore_ && this.getParent()) { 6289 6290 this.localStoragebackStore_ = this.getParent().localStorage_; 6291 } 6292 if (!this.localStoragebackStore_) { 6293 6294 this.localStoragebackStore_ = new LocalStorage({ /* empty */}); 6295 } 6296 return this.localStoragebackStore_; 6297 } 6298 set localStorage_(instance) { 6299 if (!instance) { 6300 // setting to undefined not allowed 6301 return; 6302 } 6303 if (this.localStoragebackStore_) { 6304 stateMgmtConsole.applicationError(`${this.debugInfo__()}: constructor: is setting LocalStorage instance twice. Application error.`); 6305 } 6306 this.localStoragebackStore_ = instance; 6307 } 6308 // FIXME 6309 // indicate if this is V1 or a V2 component 6310 // V1 by default, changed to V2 by the first V2 decorated variable 6311 // when splitting ViewPU and ViewV2 6312 // use instanceOf. Until then, this is a workaround. 6313 // @Local, @Param, @Trace, etc V2 decorator functions modify isViewV2 to return true 6314 // (decorator can modify functions in prototype) 6315 // FIXME 6316 get isViewV2() { 6317 return false; 6318 } 6319 onGlobalThemeChanged() { 6320 this.onWillApplyThemeInternally(); 6321 this.forceCompleteRerender(false); 6322 this.childrenWeakrefMap_.forEach((weakRefChild) => { 6323 const child = weakRefChild.deref(); 6324 if (child) { 6325 child.onGlobalThemeChanged(); 6326 } 6327 }); 6328 } 6329 aboutToReuse(params) { } 6330 aboutToRecycle() { } 6331 onWillApplyThemeInternally() { 6332 var _a; 6333 const theme = (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.getFinalTheme(this.id__()); 6334 if (theme) { 6335 this.onWillApplyTheme(theme); 6336 } 6337 } 6338 onWillApplyTheme(theme) { } 6339 // super class will call this function from 6340 // its aboutToBeDeleted implementation 6341 aboutToBeDeletedInternal() { 6342 var _a; 6343 6344 // if this isDeleting_ is true already, it may be set delete status recursively by its parent, so it is not necessary 6345 // to set and recursively set its children any more 6346 if (!this.isDeleting_) { 6347 this.isDeleting_ = true; 6348 this.setDeleteStatusRecursively(); 6349 } 6350 // tell UINodeRegisterProxy that all elmtIds under 6351 // this ViewPU should be treated as already unregistered 6352 6353 // purge the elmtIds owned by this viewPU from the updateFuncByElmtId and also the state variable dependent elmtIds 6354 Array.from(this.updateFuncByElmtId.keys()).forEach((elmtId) => { 6355 this.purgeDeleteElmtId(elmtId); 6356 }); 6357 if (this.hasRecycleManager()) { 6358 this.getRecycleManager().purgeAllCachedRecycleNode(); 6359 } 6360 // un-registration of ElementIDs 6361 6362 // it will unregister removed elmtIds from all ViewPu, equals purgeDeletedElmtIdsRecursively 6363 this.purgeDeletedElmtIds(); 6364 // un-registers its own id once all its children are unregistered 6365 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs([this.id__()]); 6366 6367 // in case this ViewPU is currently frozen 6368 PUV2ViewBase.inactiveComponents_.delete(`${this.constructor.name}[${this.id__()}]`); 6369 // FIXME needed ? 6370 MonitorV2.clearWatchesFromTarget(this); 6371 this.updateFuncByElmtId.clear(); 6372 this.watchedProps.clear(); 6373 this.providedVars_.clear(); 6374 if (this.ownObservedPropertiesStore__) { 6375 this.ownObservedPropertiesStore__.clear(); 6376 } 6377 if (this.getParent()) { 6378 this.getParent().removeChild(this); 6379 } 6380 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onViewPUDelete(this); 6381 this.localStoragebackStore_ = undefined; 6382 } 6383 purgeDeleteElmtId(rmElmtId) { 6384 6385 const result = this.updateFuncByElmtId.delete(rmElmtId); 6386 if (result) { 6387 this.purgeVariableDependenciesOnElmtIdOwnFunc(rmElmtId); 6388 // it means rmElmtId has finished all the unregistration from the js side, ElementIdToOwningViewPU_ does not need to keep it 6389 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(rmElmtId); 6390 } 6391 return result; 6392 } 6393 purgeVariableDependenciesOnElmtIdOwnFunc(elmtId) { 6394 this.ownObservedPropertiesStore_.forEach((stateVar) => { 6395 stateVar.purgeDependencyOnElmtId(elmtId); 6396 }); 6397 } 6398 debugInfoStateVars() { 6399 let result = `|--${this.constructor.name}[${this.id__()}]`; 6400 Object.getOwnPropertyNames(this) 6401 .filter((varName) => varName.startsWith('__') && !varName.startsWith(ObserveV2.OB_PREFIX)) 6402 .forEach((varName) => { 6403 const prop = Reflect.get(this, varName); 6404 if ('debugInfoDecorator' in prop) { 6405 const observedProp = prop; 6406 result += `\n ${observedProp.debugInfoDecorator()} '${observedProp.info()}'[${observedProp.id__()}]`; 6407 result += `\n ${observedProp.debugInfoSubscribers()}`; 6408 result += `\n ${observedProp.debugInfoSyncPeers()}`; 6409 result += `\n ${observedProp.debugInfoDependentElmtIds()}`; 6410 result += `\n ${observedProp.debugInfoDependentComponents()}`; 6411 } 6412 }); 6413 return result; 6414 } 6415 /** 6416 * Indicate if this @Component is allowed to freeze by calling with freezeState=true 6417 * Called with value of the @Component decorator 'freezeWhenInactive' parameter 6418 * or depending how UI compiler works also with 'undefined' 6419 * @param freezeState only value 'true' will be used, otherwise inherits from parent 6420 * if not parent, set to false. 6421 */ 6422 initAllowComponentFreeze(freezeState) { 6423 // set to true if freeze parameter set for this @Component to true 6424 // otherwise inherit from parent @Component (if it exists). 6425 this.isCompFreezeAllowed_ = freezeState || this.isCompFreezeAllowed_; 6426 6427 } 6428 /** 6429 * ArkUI engine will call this function when the corresponding CustomNode's active status change. 6430 * ArkUI engine will not recurse children nodes to inform the stateMgmt for the performance reason. 6431 * So the stateMgmt needs to recurse the children although the isCompFreezeAllowed is false because the children nodes 6432 * may enable the freezeWhenInActive. 6433 * @param active true for active, false for inactive 6434 */ 6435 setActiveInternal(active) { 6436 6437 if (this.isCompFreezeAllowed()) { 6438 this.isActive_ = active; 6439 if (this.isActive_) { 6440 this.onActiveInternal(); 6441 } 6442 else { 6443 this.onInactiveInternal(); 6444 } 6445 } 6446 for (const child of this.childrenWeakrefMap_.values()) { 6447 const childView = child.deref(); 6448 if (childView) { 6449 childView.setActiveInternal(active); 6450 } 6451 } 6452 6453 } 6454 onActiveInternal() { 6455 if (!this.isActive_) { 6456 return; 6457 } 6458 6459 this.performDelayedUpdate(); 6460 // Remove the active component from the Map for Dfx 6461 ViewPU.inactiveComponents_.delete(`${this.constructor.name}[${this.id__()}]`); 6462 } 6463 onInactiveInternal() { 6464 if (this.isActive_) { 6465 return; 6466 } 6467 6468 for (const stateLinkProp of this.ownObservedPropertiesStore_) { 6469 stateLinkProp.enableDelayedNotification(); 6470 } 6471 // Add the inactive Components to Map for Dfx listing 6472 ViewPU.inactiveComponents_.add(`${this.constructor.name}[${this.id__()}]`); 6473 } 6474 initialRenderView() { 6475 6476 this.onWillApplyThemeInternally(); 6477 this.obtainOwnObservedProperties(); 6478 this.isRenderInProgress = true; 6479 this.initialRender(); 6480 this.isRenderInProgress = false; 6481 this.isInitialRenderDone = true; 6482 6483 } 6484 UpdateElement(elmtId) { 6485 6486 if (elmtId === this.id__()) { 6487 // do not attempt to update itself. 6488 // a @Prop can add a dependency of the ViewPU onto itself. Ignore it. 6489 6490 return; 6491 } 6492 // do not process an Element that has been marked to be deleted 6493 const entry = this.updateFuncByElmtId.get(elmtId); 6494 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 6495 if (typeof updateFunc !== 'function') { 6496 6497 } 6498 else { 6499 6500 this.isRenderInProgress = true; 6501 6502 updateFunc(elmtId, /* isFirstRender */ false); 6503 6504 6505 this.finishUpdateFunc(elmtId); 6506 6507 this.isRenderInProgress = false; 6508 6509 } 6510 6511 } 6512 delayCompleteRerender(deep = false) { 6513 this.delayRecycleNodeRerender = true; 6514 this.delayRecycleNodeRerenderDeep = deep; 6515 } 6516 flushDelayCompleteRerender() { 6517 this.forceCompleteRerender(this.delayRecycleNodeRerenderDeep); 6518 this.delayRecycleNodeRerender = false; 6519 } 6520 /** 6521 * force a complete rerender / update on specific node by executing update function. 6522 * 6523 * @param elmtId which node needs to update. 6524 * 6525 * framework internal functions, apps must not call 6526 */ 6527 forceRerenderNode(elmtId) { 6528 6529 // see which elmtIds are managed by this View 6530 // and clean up all book keeping for them 6531 this.purgeDeletedElmtIds(); 6532 this.UpdateElement(elmtId); 6533 // remove elemtId from dirtDescendantElementIds. 6534 this.dirtDescendantElementIds_.delete(elmtId); 6535 6536 } 6537 // implements IMultiPropertiesChangeSubscriber 6538 viewPropertyHasChanged(varName, dependentElmtIds) { 6539 6540 aceTrace.begin('ViewPU.viewPropertyHasChanged', this.constructor.name, varName, dependentElmtIds.size); 6541 if (this.isRenderInProgress) { 6542 stateMgmtConsole.applicationError(`${this.debugInfo__()}: 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!`); 6543 } 6544 this.syncInstanceId(); 6545 if (dependentElmtIds.size && !this.isFirstRender()) { 6546 if (!this.dirtDescendantElementIds_.size && !this.runReuse_) { 6547 // mark ComposedElement dirty when first elmtIds are added 6548 // do not need to do this every time 6549 this.markNeedUpdate(); 6550 } 6551 6552 for (const elmtId of dependentElmtIds) { 6553 if (this.hasRecycleManager()) { 6554 this.dirtDescendantElementIds_.add(this.recycleManager_.proxyNodeId(elmtId)); 6555 } 6556 else { 6557 this.dirtDescendantElementIds_.add(elmtId); 6558 } 6559 } 6560 6561 } 6562 else { 6563 6564 6565 } 6566 let cb = this.watchedProps.get(varName); 6567 if (cb && typeof cb === 'function') { 6568 6569 cb.call(this, varName); 6570 } 6571 this.restoreInstanceId(); 6572 aceTrace.end(); 6573 6574 } 6575 /** 6576 * inform that UINode with given elmtId needs rerender 6577 * does NOT exec @Watch function. 6578 * only used on V2 code path from ObserveV2.fireChange. 6579 * 6580 * FIXME will still use in the future? 6581 */ 6582 uiNodeNeedUpdateV2(elmtId) { 6583 if (this.isFirstRender()) { 6584 return; 6585 } 6586 6587 if (!this.dirtDescendantElementIds_.size) { // && !this runReuse_) { 6588 // mark ComposedElement dirty when first elmtIds are added 6589 // do not need to do this every time 6590 this.syncInstanceId(); 6591 this.markNeedUpdate(); 6592 this.restoreInstanceId(); 6593 } 6594 if (this.hasRecycleManager()) { 6595 this.dirtDescendantElementIds_.add(this.recycleManager_.proxyNodeId(elmtId)); 6596 } 6597 else { 6598 this.dirtDescendantElementIds_.add(elmtId); 6599 } 6600 6601 6602 } 6603 performDelayedUpdate() { 6604 if (!this.ownObservedPropertiesStore_.size) { 6605 return; 6606 } 6607 6608 aceTrace.begin('ViewPU.performDelayedUpdate', this.constructor.name); 6609 6610 this.syncInstanceId(); 6611 for (const stateLinkPropVar of this.ownObservedPropertiesStore_) { 6612 const changedElmtIds = stateLinkPropVar.moveElmtIdsForDelayedUpdate(); 6613 if (changedElmtIds) { 6614 const varName = stateLinkPropVar.info(); 6615 if (changedElmtIds.size && !this.isFirstRender()) { 6616 for (const elmtId of changedElmtIds) { 6617 this.dirtDescendantElementIds_.add(elmtId); 6618 } 6619 } 6620 6621 const cb = this.watchedProps.get(varName); 6622 if (cb) { 6623 6624 cb.call(this, varName); 6625 } 6626 } 6627 } // for all ownStateLinkProps_ 6628 this.restoreInstanceId(); 6629 if (this.dirtDescendantElementIds_.size) { 6630 this.markNeedUpdate(); 6631 } 6632 aceTrace.end(); 6633 6634 } 6635 /** 6636 * Function to be called from the constructor of the sub component 6637 * to register a @Watch variable 6638 * @param propStr name of the variable. Note from @Provide and @Consume this is 6639 * the variable name and not the alias! 6640 * @param callback application defined member function of sub-class 6641 */ 6642 declareWatch(propStr, callback) { 6643 this.watchedProps.set(propStr, callback); 6644 } 6645 /** 6646 * This View @Provide's a variable under given name 6647 * Call this function from the constructor of the sub class 6648 * @param providedPropName either the variable name or the alias defined as 6649 * decorator param 6650 * @param store the backing store object for this variable (not the get/set variable!) 6651 */ 6652 addProvidedVar(providedPropName, store, allowOverride = false) { 6653 if (!allowOverride && this.findProvidePU(providedPropName)) { 6654 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. Property with this name is provided by one of the ancestor Views already. @Provide override not allowed.`); 6655 } 6656 store.setDecoratorInfo('@Provide'); 6657 this.providedVars_.set(providedPropName, store); 6658 } 6659 /* 6660 findProvidePU finds @Provided property recursively by traversing ViewPU's towards that of the UI tree root @Component: 6661 if 'this' ViewPU has a @Provide('providedPropName') return it, otherwise ask from its parent ViewPU. 6662 */ 6663 findProvidePU(providedPropName) { 6664 return this.providedVars_.get(providedPropName) || (this.parent_ && this.parent_.findProvidePU(providedPropName)); 6665 } 6666 /** 6667 * Method for the sub-class to call from its constructor for resolving 6668 * a @Consume variable and initializing its backing store 6669 * with the SyncedPropertyTwoWay<T> object created from the 6670 * @Provide variable's backing store. 6671 * @param providedPropName the name of the @Provide'd variable. 6672 * This is either the @Consume decorator parameter, or variable name. 6673 * @param consumeVarName the @Consume variable name (not the 6674 * @Consume decorator parameter) 6675 * @returns initializing value of the @Consume backing store 6676 */ 6677 initializeConsume(providedPropName, consumeVarName) { 6678 let providedVarStore = this.findProvidePU(providedPropName); 6679 if (providedVarStore === undefined) { 6680 throw new ReferenceError(`${this.debugInfo__()} missing @Provide property with name ${providedPropName}. 6681 Fail to resolve @Consume(${providedPropName}).`); 6682 } 6683 const factory = (source) => { 6684 const result = new SynchedPropertyTwoWayPU(source, this, consumeVarName); 6685 result.setDecoratorInfo('@Consume'); 6686 6687 return result; 6688 }; 6689 return providedVarStore.createSync(factory); 6690 } 6691 /** 6692 * given the elmtId of a child or child of child within this custom component 6693 * remember this component needs a partial update 6694 * @param elmtId 6695 */ 6696 markElemenDirtyById(elmtId) { 6697 // TODO ace-ets2bundle, framework, compiled apps need to update together 6698 // this function will be removed after a short transition period 6699 stateMgmtConsole.applicationError(`${this.debugInfo__()}: markElemenDirtyById no longer supported. 6700 Please update your ace-ets2bundle and recompile your application. Application error!`); 6701 } 6702 /** 6703 * For each recorded dirty Element in this custom component 6704 * run its update function 6705 * 6706 */ 6707 updateDirtyElements() { 6708 6709 do { 6710 6711 // see which elmtIds are managed by this View 6712 // and clean up all book keeping for them 6713 this.purgeDeletedElmtIds(); 6714 // process all elmtIds marked as needing update in ascending order. 6715 // ascending order ensures parent nodes will be updated before their children 6716 // prior cleanup ensure no already deleted Elements have their update func executed 6717 const dirtElmtIdsFromRootNode = Array.from(this.dirtDescendantElementIds_).sort(ViewPU.compareNumber); 6718 // if state changed during exec update lambda inside UpdateElement, then the dirty elmtIds will be added 6719 // to newly created this.dirtDescendantElementIds_ Set 6720 dirtElmtIdsFromRootNode.forEach(elmtId => { 6721 if (this.hasRecycleManager()) { 6722 this.UpdateElement(this.recycleManager_.proxyNodeId(elmtId)); 6723 } 6724 else { 6725 this.UpdateElement(elmtId); 6726 } 6727 this.dirtDescendantElementIds_.delete(elmtId); 6728 }); 6729 if (this.dirtDescendantElementIds_.size) { 6730 stateMgmtConsole.applicationError(`${this.debugInfo__()}: New UINode objects added to update queue while re-render! - Likely caused by @Component state change during build phase, not allowed. Application error!`); 6731 } 6732 } while (this.dirtDescendantElementIds_.size); 6733 6734 6735 } 6736 // executed on first render only 6737 // kept for backward compatibility with old ace-ets2bundle 6738 observeComponentCreation(compilerAssignedUpdateFunc) { 6739 if (this.isDeleting_) { 6740 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation `); 6741 return; 6742 } 6743 const updateFunc = (elmtId, isFirstRender) => { 6744 6745 this.currentlyRenderedElmtIdStack_.push(elmtId); 6746 compilerAssignedUpdateFunc(elmtId, isFirstRender); 6747 this.currentlyRenderedElmtIdStack_.pop(); 6748 6749 }; 6750 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 6751 // in observeComponentCreation function we do not get info about the component name, in 6752 // observeComponentCreation2 we do. 6753 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc }); 6754 // add element id -> owning ViewPU 6755 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 6756 try { 6757 updateFunc(elmtId, /* is first render */ true); 6758 } 6759 catch (error) { 6760 // avoid the incompatible change that move set function before updateFunc. 6761 this.updateFuncByElmtId.delete(elmtId); 6762 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 6763 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 6764 throw error; 6765 } 6766 } 6767 observeComponentCreation2(compilerAssignedUpdateFunc, classObject) { 6768 if (this.isDeleting_) { 6769 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation2 `); 6770 return; 6771 } 6772 const _componentName = (classObject && ('name' in classObject)) ? Reflect.get(classObject, 'name') : 'unspecified UINode'; 6773 if (_componentName === '__Recycle__') { 6774 return; 6775 } 6776 const _popFunc = (classObject && 'pop' in classObject) ? classObject.pop : () => { }; 6777 const updateFunc = (elmtId, isFirstRender) => { 6778 var _a, _b; 6779 this.syncInstanceId(); 6780 6781 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onComponentCreateEnter(_componentName, elmtId, isFirstRender, this); 6782 ViewStackProcessor.StartGetAccessRecordingFor(elmtId); 6783 if (!this.isViewV2) { 6784 // Enable PU state tracking only in PU @Components 6785 this.currentlyRenderedElmtIdStack_.push(elmtId); 6786 stateMgmtDFX.inRenderingElementId.push(elmtId); 6787 } 6788 // if V2 @Observed/@Track used anywhere in the app (there is no more fine grained criteria), 6789 // enable V2 object deep observation 6790 // FIXME: A @Component should only use PU or V2 state, but ReactNative dynamic viewer uses both. 6791 if (this.isViewV2 || ConfigureStateMgmt.instance.needsV2Observe()) { 6792 // FIXME: like in V2 setting bindId_ in ObserveV2 does not work with 'stacked' 6793 // update + initial render calls, like in if and ForEach case, convert to stack as well 6794 ObserveV2.getObserve().startRecordDependencies(this, elmtId); 6795 } 6796 compilerAssignedUpdateFunc(elmtId, isFirstRender); 6797 if (!isFirstRender) { 6798 _popFunc(); 6799 } 6800 let node = this.getNodeById(elmtId); 6801 if (node !== undefined) { 6802 node.cleanStageValue(); 6803 } 6804 if (this.isViewV2 || ConfigureStateMgmt.instance.needsV2Observe()) { 6805 ObserveV2.getObserve().stopRecordDependencies(); 6806 } 6807 if (!this.isViewV2) { 6808 this.currentlyRenderedElmtIdStack_.pop(); 6809 stateMgmtDFX.inRenderingElementId.pop(); 6810 } 6811 ViewStackProcessor.StopGetAccessRecording(); 6812 (_b = PUV2ViewBase.arkThemeScopeManager) === null || _b === void 0 ? void 0 : _b.onComponentCreateExit(elmtId); 6813 6814 this.restoreInstanceId(); 6815 }; 6816 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 6817 // needs to move set before updateFunc. 6818 // make sure the key and object value exist since it will add node in attributeModifier during updateFunc. 6819 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject }); 6820 // add element id -> owning ViewPU 6821 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 6822 try { 6823 updateFunc(elmtId, /* is first render */ true); 6824 } 6825 catch (error) { 6826 // avoid the incompatible change that move set function before updateFunc. 6827 this.updateFuncByElmtId.delete(elmtId); 6828 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 6829 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 6830 throw error; 6831 } 6832 6833 } 6834 getOrCreateRecycleManager() { 6835 if (!this.recycleManager_) { 6836 this.recycleManager_ = new RecycleManager; 6837 } 6838 return this.recycleManager_; 6839 } 6840 getRecycleManager() { 6841 return this.recycleManager_; 6842 } 6843 hasRecycleManager() { 6844 return !(this.recycleManager_ === undefined); 6845 } 6846 initRecycleManager() { 6847 if (this.recycleManager_) { 6848 stateMgmtConsole.error(`${this.debugInfo__()}: init recycleManager multiple times. Internal error.`); 6849 return; 6850 } 6851 this.recycleManager_ = new RecycleManager; 6852 } 6853 rebuildUpdateFunc(elmtId, compilerAssignedUpdateFunc) { 6854 const updateFunc = (elmtId, isFirstRender) => { 6855 this.currentlyRenderedElmtIdStack_.push(elmtId); 6856 compilerAssignedUpdateFunc(elmtId, isFirstRender); 6857 this.currentlyRenderedElmtIdStack_.pop(); 6858 }; 6859 if (this.updateFuncByElmtId.has(elmtId)) { 6860 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc }); 6861 } 6862 } 6863 /** 6864 * @function observeRecycleComponentCreation 6865 * @description custom node recycle creation 6866 * @param name custom node name 6867 * @param recycleUpdateFunc custom node recycle update which can be converted to a normal update function 6868 * @return void 6869 */ 6870 observeRecycleComponentCreation(name, recycleUpdateFunc) { 6871 // convert recycle update func to update func 6872 const compilerAssignedUpdateFunc = (element, isFirstRender) => { 6873 recycleUpdateFunc(element, isFirstRender, undefined); 6874 }; 6875 let node; 6876 // if there is no suitable recycle node, run a normal creation function. 6877 if (!this.hasRecycleManager() || !(node = this.getRecycleManager().popRecycleNode(name))) { 6878 6879 this.observeComponentCreation(compilerAssignedUpdateFunc); 6880 return; 6881 } 6882 // if there is a suitable recycle node, run a recycle update function. 6883 const newElmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 6884 const oldElmtId = node.id__(); 6885 this.recycleManager_.updateNodeId(oldElmtId, newElmtId); 6886 node.hasBeenRecycled_ = false; 6887 this.rebuildUpdateFunc(oldElmtId, compilerAssignedUpdateFunc); 6888 recycleUpdateFunc(oldElmtId, /* is first render */ true, node); 6889 } 6890 // param is used by BuilderNode 6891 aboutToReuseInternal(param) { 6892 this.runReuse_ = true; 6893 stateMgmtTrace.scopedTrace(() => { 6894 if (this.paramsGenerator_ && typeof this.paramsGenerator_ === 'function') { 6895 const params = param ? param : this.paramsGenerator_(); 6896 this.updateStateVars(params); 6897 this.aboutToReuse(params); 6898 } 6899 }, 'aboutToReuse', this.constructor.name); 6900 for (const stateLinkPropVar of this.ownObservedPropertiesStore_) { 6901 const changedElmtIds = stateLinkPropVar.moveElmtIdsForDelayedUpdate(true); 6902 if (changedElmtIds) { 6903 if (changedElmtIds.size && !this.isFirstRender()) { 6904 for (const elmtId of changedElmtIds) { 6905 this.dirtDescendantElementIds_.add(elmtId); 6906 } 6907 } 6908 } 6909 } 6910 if (!this.delayRecycleNodeRerender) { 6911 this.updateDirtyElements(); 6912 } 6913 else { 6914 this.flushDelayCompleteRerender(); 6915 } 6916 this.childrenWeakrefMap_.forEach((weakRefChild) => { 6917 const child = weakRefChild.deref(); 6918 if (child) { 6919 if (child instanceof ViewPU) { 6920 if (!child.hasBeenRecycled_) { 6921 child.aboutToReuseInternal(); 6922 } 6923 } 6924 else { 6925 // FIXME fix for mixed V1 - V2 Hierarchies 6926 throw new Error('aboutToReuseInternal: Recycle not implemented for ViewV2, yet'); 6927 } 6928 } // if child 6929 }); 6930 this.runReuse_ = false; 6931 } 6932 stopRecursiveRecycle() { 6933 this.preventRecursiveRecycle_ = true; 6934 } 6935 aboutToRecycleInternal() { 6936 this.runReuse_ = true; 6937 stateMgmtTrace.scopedTrace(() => { 6938 this.aboutToRecycle(); 6939 }, 'aboutToRecycle', this.constructor.name); 6940 if (this.preventRecursiveRecycle_) { 6941 this.preventRecursiveRecycle_ = false; 6942 return; 6943 } 6944 this.childrenWeakrefMap_.forEach((weakRefChild) => { 6945 const child = weakRefChild.deref(); 6946 if (child) { 6947 if (child instanceof ViewPU) { 6948 if (!child.hasBeenRecycled_) { 6949 child.aboutToRecycleInternal(); 6950 } 6951 } 6952 else { 6953 // FIXME fix for mixed V1 - V2 Hierarchies 6954 throw new Error('aboutToRecycleInternal: Recycle not yet implemented for ViewV2'); 6955 } 6956 } // if child 6957 }); 6958 this.runReuse_ = false; 6959 } 6960 // add current JS object to it's parent recycle manager 6961 recycleSelf(name) { 6962 if (this.getParent() && this.getParent() instanceof ViewPU && !this.getParent().isDeleting_) { 6963 const parentPU = this.getParent(); 6964 parentPU.getOrCreateRecycleManager().pushRecycleNode(name, this); 6965 this.hasBeenRecycled_ = true; 6966 } 6967 else { 6968 this.resetRecycleCustomNode(); 6969 } 6970 } 6971 isRecycled() { 6972 return this.hasBeenRecycled_; 6973 } 6974 UpdateLazyForEachElements(elmtIds) { 6975 if (!Array.isArray(elmtIds)) { 6976 return; 6977 } 6978 Array.from(elmtIds).sort(ViewPU.compareNumber).forEach((elmtId) => { 6979 const entry = this.updateFuncByElmtId.get(elmtId); 6980 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 6981 if (typeof updateFunc !== 'function') { 6982 6983 } 6984 else { 6985 this.isRenderInProgress = true; 6986 updateFunc(elmtId, false); 6987 this.finishUpdateFunc(elmtId); 6988 this.isRenderInProgress = false; 6989 } 6990 }); 6991 } 6992 /** 6993 * CreateStorageLink and CreateStorageLinkPU are used by the implementation of @StorageLink and 6994 * @LocalStotrageLink in full update and partial update solution respectively. 6995 * These are not part of the public AppStorage API , apps should not use. 6996 * @param storagePropName - key in LocalStorage 6997 * @param defaultValue - value to use when creating a new prop in the LocalStotage 6998 * @param owningView - the View/ViewPU owning the @StorageLink/@LocalStorageLink variable 6999 * @param viewVariableName - @StorageLink/@LocalStorageLink variable name 7000 * @returns SynchedPropertySimple/ObjectTwoWay/PU 7001 */ 7002 createStorageLink(storagePropName, defaultValue, viewVariableName) { 7003 const appStorageLink = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7004 ? undefined 7005 : new SynchedPropertyTwoWayPU(source, this, viewVariableName)); 7006 appStorageLink === null || appStorageLink === void 0 ? void 0 : appStorageLink.setDecoratorInfo('@StorageLink'); 7007 return appStorageLink; 7008 } 7009 createStorageProp(storagePropName, defaultValue, viewVariableName) { 7010 const appStorageProp = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7011 ? undefined 7012 : new SynchedPropertyOneWayPU(source, this, viewVariableName)); 7013 appStorageProp === null || appStorageProp === void 0 ? void 0 : appStorageProp.setDecoratorInfo('@StorageProp'); 7014 return appStorageProp; 7015 } 7016 createLocalStorageLink(storagePropName, defaultValue, viewVariableName) { 7017 const localStorageLink = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7018 ? undefined 7019 : new SynchedPropertyTwoWayPU(source, this, viewVariableName)); 7020 localStorageLink === null || localStorageLink === void 0 ? void 0 : localStorageLink.setDecoratorInfo('@LocalStorageLink'); 7021 return localStorageLink; 7022 } 7023 createLocalStorageProp(storagePropName, defaultValue, viewVariableName) { 7024 const localStorageProp = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7025 ? undefined 7026 : new SynchedPropertyObjectOneWayPU(source, this, viewVariableName)); 7027 localStorageProp === null || localStorageProp === void 0 ? void 0 : localStorageProp.setDecoratorInfo('@LocalStorageProp'); 7028 return localStorageProp; 7029 } 7030 /** 7031 * onDumpInfo is used to process commands delivered by the hidumper process 7032 * @param commands - list of commands provided in the shell 7033 * @returns void 7034 */ 7035 onDumpInfo(commands) { 7036 let dfxCommands = this.processOnDumpCommands(commands); 7037 dfxCommands.forEach((command) => { 7038 let view = undefined; 7039 if (command.viewId) { 7040 view = this.findViewPUInHierarchy(command.viewId); 7041 if (!view) { 7042 DumpLog.print(0, `\nTarget view: ${command.viewId} not found for command: ${command.what}\n`); 7043 return; 7044 } 7045 } 7046 else { 7047 view = this; 7048 command.viewId = view.id__(); 7049 } 7050 switch (command.what) { 7051 case '-dumpAll': 7052 view.printDFXHeader('ViewPU Info', command); 7053 DumpLog.print(0, view.debugInfoView(command.isRecursive)); 7054 break; 7055 case '-viewHierarchy': 7056 view.printDFXHeader('ViewPU Hierarchy', command); 7057 DumpLog.print(0, view.debugInfoViewHierarchy(command.isRecursive)); 7058 break; 7059 case '-stateVariables': 7060 view.printDFXHeader('ViewPU State Variables', command); 7061 DumpLog.print(0, view.debugInfoStateVars()); 7062 break; 7063 case '-registeredElementIds': 7064 view.printDFXHeader('ViewPU Registered Element IDs', command); 7065 DumpLog.print(0, view.debugInfoUpdateFuncByElmtId(command.isRecursive)); 7066 break; 7067 case '-dirtyElementIds': 7068 view.printDFXHeader('ViewPU Dirty Registered Element IDs', command); 7069 DumpLog.print(0, view.debugInfoDirtDescendantElementIds(command.isRecursive)); 7070 break; 7071 case '-inactiveComponents': 7072 view.printDFXHeader('List of Inactive Components', command); 7073 DumpLog.print(0, view.debugInfoInactiveComponents()); 7074 break; 7075 case '-profiler': 7076 view.printDFXHeader('Profiler Info', command); 7077 view.dumpReport(); 7078 this.sendStateInfo('{}'); 7079 break; 7080 default: 7081 DumpLog.print(0, `\nUnsupported JS DFX dump command: [${command.what}, viewId=${command.viewId}, isRecursive=${command.isRecursive}]\n`); 7082 } 7083 }); 7084 } 7085 printDFXHeader(header, command) { 7086 let length = 50; 7087 let remainder = length - header.length < 0 ? 0 : length - header.length; 7088 DumpLog.print(0, `\n${'-'.repeat(remainder / 2)}${header}${'-'.repeat(remainder / 2)}`); 7089 DumpLog.print(0, `[${command.what}, viewId=${command.viewId}, isRecursive=${command.isRecursive}]\n`); 7090 } 7091 processOnDumpCommands(commands) { 7092 let isFlag = (param) => { 7093 return '-r'.match(param) != null || param.startsWith('-viewId='); 7094 }; 7095 let dfxCommands = []; 7096 for (var i = 0; i < commands.length; i++) { 7097 let command = commands[i]; 7098 if (isFlag(command)) { 7099 if (command.startsWith('-viewId=')) { 7100 let dfxCommand = dfxCommands[dfxCommands.length - 1]; 7101 if (dfxCommand) { 7102 let input = command.split('='); 7103 if (input[1]) { 7104 let viewId = Number.parseInt(input[1]); 7105 dfxCommand.viewId = Number.isNaN(viewId) ? UINodeRegisterProxy.notRecordingDependencies : viewId; 7106 } 7107 } 7108 } 7109 else if (command.match('-r')) { 7110 let dfxCommand = dfxCommands[dfxCommands.length - 1]; 7111 if (dfxCommand) { 7112 dfxCommand.isRecursive = true; 7113 } 7114 } 7115 } 7116 else { 7117 dfxCommands.push({ 7118 what: command, 7119 viewId: undefined, 7120 isRecursive: false, 7121 }); 7122 } 7123 } 7124 return dfxCommands; 7125 } 7126 findViewPUInHierarchy(id) { 7127 let weakChild = this.childrenWeakrefMap_.get(id); 7128 if (weakChild) { 7129 const child = weakChild.deref(); 7130 // found child with id, is it a ViewPU? 7131 return (child instanceof ViewPU) ? child : undefined; 7132 } 7133 // did not find, continue searching 7134 let retVal = undefined; 7135 for (const [key, value] of this.childrenWeakrefMap_.entries()) { 7136 retVal = value.deref().findViewPUInHierarchy(id); 7137 if (retVal) { 7138 break; 7139 } 7140 } 7141 return retVal; 7142 } 7143 debugInfoView(recursive = false) { 7144 return this.debugInfoViewInternal(recursive); 7145 } 7146 debugInfoViewInternal(recursive = false) { 7147 let retVal = `@Component\n${this.constructor.name}[${this.id__()}]`; 7148 retVal += `\n\nView Hierarchy:\n${this.debugInfoViewHierarchy(recursive)}`; 7149 retVal += `\n\nState variables:\n${this.debugInfoStateVars()}`; 7150 retVal += `\n\nRegistered Element IDs:\n${this.debugInfoUpdateFuncByElmtId(recursive)}`; 7151 retVal += `\n\nDirty Registered Element IDs:\n${this.debugInfoDirtDescendantElementIds(recursive)}`; 7152 return retVal; 7153 } 7154 debugInfoDirtDescendantElementIds(recursive = false) { 7155 return this.debugInfoDirtDescendantElementIdsInternal(0, recursive, { total: 0 }); 7156 } 7157 debugInfoDirtDescendantElementIdsInternal(depth = 0, recursive = false, counter) { 7158 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 7159 this.dirtDescendantElementIds_.forEach((value) => { 7160 retVaL += `${value}, `; 7161 }); 7162 counter.total += this.dirtDescendantElementIds_.size; 7163 retVaL += `\n${' '.repeat(depth + 1)}}[${this.dirtDescendantElementIds_.size}]`; 7164 if (recursive) { 7165 this.childrenWeakrefMap_.forEach((value, key, map) => { 7166 var _a; 7167 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoDirtDescendantElementIdsInternal(depth + 1, recursive, counter); 7168 }); 7169 } 7170 if (recursive && depth == 0) { 7171 retVaL += `\nTotal: ${counter.total}`; 7172 } 7173 return retVaL; 7174 } 7175 /** 7176 * onDumpInspector is invoked by native side to create Inspector tree including state variables 7177 * @returns dump info 7178 */ 7179 onDumpInspector() { 7180 let res = new DumpInfo(); 7181 res.viewInfo = { componentName: this.constructor.name, id: this.id__() }; 7182 Object.getOwnPropertyNames(this) 7183 .filter((varName) => varName.startsWith('__') && !varName.startsWith(ObserveV2.OB_PREFIX)) 7184 .forEach((varName) => { 7185 const prop = Reflect.get(this, varName); 7186 if (typeof prop === 'object' && 'debugInfoDecorator' in prop) { 7187 const observedProp = prop; 7188 res.observedPropertiesInfo.push(stateMgmtDFX.getObservedPropertyInfo(observedProp, false)); 7189 } 7190 }); 7191 let resInfo = ''; 7192 try { 7193 resInfo = JSON.stringify(res); 7194 } 7195 catch (error) { 7196 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in getInspector: ${error.message}`); 7197 } 7198 return resInfo; 7199 } 7200} // class ViewPU 7201/* 7202 * Copyright (c) 2023 Huawei Device Co., Ltd. 7203 * Licensed under the Apache License, Version 2.0 (the "License"); 7204 * you may not use this file except in compliance with the License. 7205 * You may obtain a copy of the License at 7206 * 7207 * http://www.apache.org/licenses/LICENSE-2.0 7208 * 7209 * Unless required by applicable law or agreed to in writing, software 7210 * distributed under the License is distributed on an "AS IS" BASIS, 7211 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7212 * See the License for the specific language governing permissions and 7213 * limitations under the License. 7214 * 7215 * * RecycleManager - Recycle cache manager 7216 * 7217* all definitions in this file are framework internal 7218*/ 7219/** 7220 * @class RecycleManager 7221 * @description manage the JS object cached of current node 7222 */ 7223class RecycleManager { 7224 constructor() { 7225 // key: recycle node name 7226 // value: recycle node JS object 7227 this.cachedRecycleNodes_ = undefined; 7228 this.biMap_ = undefined; 7229 this.cachedRecycleNodes_ = new Map(); 7230 this.biMap_ = new BidirectionalMap(); 7231 } 7232 updateNodeId(oldElmtId, newElmtId) { 7233 this.biMap_.delete(oldElmtId); 7234 this.biMap_.add([oldElmtId, newElmtId]); 7235 } 7236 proxyNodeId(oldElmtId) { 7237 const proxy = this.biMap_.get(oldElmtId); 7238 if (!proxy) { 7239 return oldElmtId; 7240 } 7241 return proxy; 7242 } 7243 pushRecycleNode(name, node) { 7244 var _a; 7245 if (!this.cachedRecycleNodes_.get(name)) { 7246 this.cachedRecycleNodes_.set(name, new Array()); 7247 } 7248 (_a = this.cachedRecycleNodes_.get(name)) === null || _a === void 0 ? void 0 : _a.push(node); 7249 } 7250 popRecycleNode(name) { 7251 var _a; 7252 return (_a = this.cachedRecycleNodes_.get(name)) === null || _a === void 0 ? void 0 : _a.pop(); 7253 } 7254 // When parent JS View is deleted, release all cached nodes 7255 purgeAllCachedRecycleNode() { 7256 this.cachedRecycleNodes_.forEach((nodes, _) => { 7257 nodes.forEach((node) => { 7258 node.resetRecycleCustomNode(); 7259 }); 7260 }); 7261 this.cachedRecycleNodes_.clear(); 7262 } 7263 // Set active status for all cached nodes 7264 setActive(active) { 7265 this.cachedRecycleNodes_.forEach((nodes, _) => { 7266 nodes.forEach((node) => { 7267 node.setActiveInternal(active); 7268 }); 7269 }); 7270 } 7271} 7272class BidirectionalMap { 7273 constructor() { 7274 this.fwdMap_ = undefined; 7275 this.revMap_ = undefined; 7276 this.fwdMap_ = new Map(); 7277 this.revMap_ = new Map(); 7278 } 7279 delete(key) { 7280 if (!this.fwdMap_[key]) { 7281 return; 7282 } 7283 const rev = this.fwdMap_[key]; 7284 this.fwdMap_.delete(key); 7285 this.revMap_.delete(rev); 7286 } 7287 get(key) { 7288 return this.fwdMap_[key] || this.revMap_[key]; 7289 } 7290 add(pair) { 7291 this.fwdMap_[pair[0]] = pair[1]; 7292 this.revMap_[pair[1]] = pair[0]; 7293 } 7294} 7295/* 7296 * Copyright (c) 2022 Huawei Device Co., Ltd. 7297 * Licensed under the Apache License, Version 2.0 (the "License"); 7298 * you may not use this file except in compliance with the License. 7299 * You may obtain a copy of the License at 7300 * 7301 * http://www.apache.org/licenses/LICENSE-2.0 7302 * 7303 * Unless required by applicable law or agreed to in writing, software 7304 * distributed under the License is distributed on an "AS IS" BASIS, 7305 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7306 * See the License for the specific language governing permissions and 7307 * limitations under the License. 7308 * 7309 * * ViewPU - View for Partial Update 7310 * 7311* all definitions in this file are framework internal 7312*/ 7313/** 7314 given parameters for calling a @Builder function 7315 this function wraps the Object of type T inside a ES6 Proxy. 7316 Each param, i.e. Object property is either a function or a value. 7317 If it is a function the function can either return a value of expected 7318 parameter type or an ObservedPropertyabstract<T> where T is the exected 7319 parameter type. The latter is the case when passing a state variable by 7320 reference. 7321 7322 Two purposes: 7323 1 - @Builder function boxy accesses params a '$$.paramA' 7324 However paramA can be a function, so to obtain the value the 7325 access would need to be '$$.param()' The proxy executes 7326 the function and return s the result 7327 2 - said function returns to ObservedPropertyAbstract backing store of 7328 a calling @Component state variable (whenever the state var is 7329 provided to the @Builder function). For this case the proxy can provide 7330 - the value by executing paramA() to return the ObservedPropertyAbstract 7331 and further (monitored!) get() to read its value 7332 - when requested to return '__param1' it returns the ObservedPropertyAbstract 7333 object. The scenario is to use to init a @Link source. 7334 */ 7335function makeBuilderParameterProxy(builderName, source) { 7336 return new Proxy(source, { 7337 set(target, prop, val) { 7338 throw Error(`@Builder '${builderName}': Invalid attempt to set(write to) parameter '${prop.toString()}' error!`); 7339 }, 7340 get(target, prop) { 7341 const prop1 = prop.toString().trim().startsWith('__') 7342 ? prop.toString().trim().substring(2) 7343 : prop.toString().trim(); 7344 7345 if (!(typeof target === 'object') && (prop1 in target)) { 7346 throw Error(`@Builder '${builderName}': '${prop1}' used but not a function parameter error!`); 7347 } 7348 const value = target[prop1]; 7349 if (typeof value !== 'function') { 7350 7351 return value; 7352 } 7353 const funcRet = value(); 7354 if ((typeof funcRet === 'object') && ('get' in funcRet)) { 7355 if (prop1 !== prop) { 7356 7357 return funcRet; 7358 } 7359 else { 7360 7361 const result = funcRet.get(); 7362 7363 return result; 7364 } 7365 } 7366 7367 return funcRet; 7368 } // get 7369 }); // new Proxy 7370} 7371/* 7372 * Copyright (c) 2024 Huawei Device Co., Ltd. 7373 * Licensed under the Apache License, Version 2.0 (the "License"); 7374 * you may not use this file except in compliance with the License. 7375 * You may obtain a copy of the License at 7376 * 7377 * http://www.apache.org/licenses/LICENSE-2.0 7378 * 7379 * Unless required by applicable law or agreed to in writing, software 7380 * distributed under the License is distributed on an "AS IS" BASIS, 7381 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7382 * See the License for the specific language governing permissions and 7383 * limitations under the License. 7384 */ 7385/** 7386 * Common Proxy handler for objects and dates for both decorators and makeObserved 7387 */ 7388class ObjectProxyHandler { 7389 constructor(isMakeObserved = false) { 7390 this.isMakeObserved_ = isMakeObserved; 7391 } 7392 // decorators work on object that holds the dependencies directly 7393 // makeObserved can't modify the object itself, so it creates a 7394 // wrapper object around it and that will hold the references 7395 // 7396 // this function is used to get the correct object that can be observed 7397 getTarget(obj) { 7398 return this.isMakeObserved_ ? RefInfo.get(obj) : obj; 7399 } 7400 get(target, key, receiver) { 7401 if (typeof key === 'symbol') { 7402 if (key === Symbol.iterator) { 7403 const conditionalTarget = this.getTarget(target); 7404 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7405 return (...args) => target[key](...args); 7406 } 7407 if (key === ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7408 return target; 7409 } 7410 if (this.isMakeObserved_ && key === ObserveV2.SYMBOL_MAKE_OBSERVED) { 7411 return true; 7412 } 7413 return target[key]; 7414 } 7415 7416 const conditionalTarget = this.getTarget(target); 7417 // makeObserved logic adds wrapper proxy later 7418 let ret = this.isMakeObserved_ ? target[key] : ObserveV2.autoProxyObject(target, key); 7419 if (typeof (ret) !== 'function') { 7420 ObserveV2.getObserve().addRef(conditionalTarget, key); 7421 return (typeof (ret) === 'object' && this.isMakeObserved_) ? RefInfo.get(ret).proxy : ret; 7422 } 7423 if (target instanceof Date) { 7424 if (ObjectProxyHandler.dateSetFunctions.has(key)) { 7425 return function (...args) { 7426 // execute original function with given arguments 7427 let result = ret.call(this, ...args); 7428 ObserveV2.getObserve().fireChange(conditionalTarget, ObjectProxyHandler.OB_DATE); 7429 return result; 7430 // bind 'this' to target inside the function 7431 }.bind(target); 7432 } 7433 else { 7434 ObserveV2.getObserve().addRef(conditionalTarget, ObjectProxyHandler.OB_DATE); 7435 } 7436 return ret.bind(target); 7437 } 7438 // function 7439 return ret.bind(receiver); 7440 } 7441 set(target, key, value) { 7442 if (typeof key === 'symbol') { 7443 if (!this.isMakeObserved_ && key !== ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7444 target[key] = value; 7445 } 7446 return true; 7447 } 7448 if (target[key] === value) { 7449 return true; 7450 } 7451 target[key] = value; 7452 ObserveV2.getObserve().fireChange(this.getTarget(target), key.toString()); 7453 return true; 7454 } 7455} 7456ObjectProxyHandler.OB_DATE = '__date__'; 7457ObjectProxyHandler.dateSetFunctions = new Set(['setFullYear', 'setMonth', 'setDate', 'setHours', 'setMinutes', 7458 'setSeconds', 'setMilliseconds', 'setTime', 'setUTCFullYear', 'setUTCMonth', 'setUTCDate', 'setUTCHours', 7459 'setUTCMinutes', 'setUTCSeconds', 'setUTCMilliseconds']); 7460; 7461/** 7462 * Common Proxy handler for Arrays for both decorators and makeObserved 7463 */ 7464class ArrayProxyHandler { 7465 constructor(isMakeObserved = false) { 7466 this.isMakeObserved_ = isMakeObserved; 7467 } 7468 // decorators work on object that holds the dependencies directly 7469 // makeObserved can't modify the object itself, so it creates a 7470 // wrapper object around it and that will hold the references 7471 // 7472 // this function is used to get the correct object that can be observed 7473 getTarget(obj) { 7474 return this.isMakeObserved_ ? RefInfo.get(obj) : obj; 7475 } 7476 get(target, key, receiver) { 7477 if (typeof key === 'symbol') { 7478 if (key === Symbol.iterator) { 7479 const conditionalTarget = this.getTarget(target); 7480 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7481 return (...args) => target[key](...args); 7482 } 7483 if (key === ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7484 return target; 7485 } 7486 if (this.isMakeObserved_ && key === ObserveV2.SYMBOL_MAKE_OBSERVED) { 7487 return true; 7488 } 7489 return target[key]; 7490 } 7491 7492 const conditionalTarget = this.getTarget(target); 7493 if (key === 'length') { 7494 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7495 return target[key]; 7496 } 7497 // makeObserved logic adds wrapper proxy later 7498 let ret = this.isMakeObserved_ ? target[key] : ObserveV2.autoProxyObject(target, key); 7499 if (typeof (ret) !== 'function') { 7500 ObserveV2.getObserve().addRef(conditionalTarget, key); 7501 return (typeof (ret) === 'object' && this.isMakeObserved_) ? RefInfo.get(ret).proxy : ret; 7502 } 7503 if (ArrayProxyHandler.arrayMutatingFunctions.has(key)) { 7504 return function (...args) { 7505 ret.call(target, ...args); 7506 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7507 // returning the 'receiver(proxied object)' ensures that when chain calls also 2nd function call 7508 // operates on the proxied object. 7509 return receiver; 7510 }; 7511 } 7512 else if (ArrayProxyHandler.arrayLengthChangingFunctions.has(key)) { 7513 return function (...args) { 7514 const result = ret.call(target, ...args); 7515 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7516 return result; 7517 }; 7518 } 7519 else if (!SendableType.isArray(target)) { 7520 return ret.bind(receiver); 7521 } 7522 else if (key === 'forEach') { 7523 // to make ForEach Component and its Item can addref 7524 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7525 return function (callbackFn) { 7526 const result = ret.call(target, (value, index, array) => { 7527 // Collections.Array will report BusinessError: The foreach cannot be bound if call "receiver". 7528 // because the passed parameter is not the instance of the container class. 7529 // so we must call "target" here to deal with the collections situations. 7530 // But we also need to addref for each index. 7531 ObserveV2.getObserve().addRef(conditionalTarget, index.toString()); 7532 callbackFn(typeof value === 'object' ? RefInfo.get(value).proxy : value, index, receiver); 7533 }); 7534 return result; 7535 }; 7536 } 7537 else { 7538 return ret.bind(target); // SendableArray can't be bound -> functions not observed 7539 } 7540 } 7541 set(target, key, value) { 7542 if (typeof key === 'symbol') { 7543 if (!this.isMakeObserved_ && key !== ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7544 target[key] = value; 7545 } 7546 return true; 7547 } 7548 if (target[key] === value) { 7549 return true; 7550 } 7551 const originalLength = target.length; 7552 target[key] = value; 7553 const arrayLenChanged = target.length !== originalLength; 7554 ObserveV2.getObserve().fireChange(this.getTarget(target), arrayLenChanged ? ObserveV2.OB_LENGTH : key.toString()); 7555 return true; 7556 } 7557} 7558// shrinkTo and extendTo is collection.Array api. 7559ArrayProxyHandler.arrayLengthChangingFunctions = new Set(['push', 'pop', 'shift', 'splice', 'unshift', 'shrinkTo', 'extendTo']); 7560ArrayProxyHandler.arrayMutatingFunctions = new Set(['copyWithin', 'fill', 'reverse', 'sort']); 7561; 7562/** 7563 * Common Proxy handler for Maps and Sets for both decorators and makeObserved 7564 */ 7565class SetMapProxyHandler { 7566 constructor(isMakeObserved = false) { 7567 this.isMakeObserved_ = isMakeObserved; 7568 } 7569 // decorators work on object that holds the dependencies directly 7570 // makeObserved can't modify the object itself, so it creates a 7571 // wrapper object around it and that will hold the references 7572 // 7573 // this function is used to get the correct object that can be observed 7574 getTarget(obj) { 7575 return this.isMakeObserved_ ? RefInfo.get(obj) : obj; 7576 } 7577 get(target, key, receiver) { 7578 if (typeof key === 'symbol') { 7579 if (key === Symbol.iterator) { 7580 const conditionalTarget = this.getTarget(target); 7581 ObserveV2.getObserve().fireChange(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7582 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7583 return (...args) => target[key](...args); 7584 } 7585 if (key === ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7586 return target; 7587 } 7588 if (this.isMakeObserved_ && key === ObserveV2.SYMBOL_MAKE_OBSERVED) { 7589 return true; 7590 } 7591 return target[key]; 7592 } 7593 7594 const conditionalTarget = this.getTarget(target); 7595 if (key === 'size') { 7596 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7597 return target[key]; 7598 } 7599 // makeObserved logic adds wrapper proxy later 7600 let ret = this.isMakeObserved_ ? target[key] : ObserveV2.autoProxyObject(target, key); 7601 if (typeof (ret) !== 'function') { 7602 ObserveV2.getObserve().addRef(conditionalTarget, key); 7603 return (typeof (ret) === 'object' && this.isMakeObserved_) ? RefInfo.get(ret).proxy : ret; 7604 } 7605 if (key === 'has') { 7606 return (prop) => { 7607 const ret = target.has(prop); 7608 if (ret) { 7609 ObserveV2.getObserve().addRef(conditionalTarget, prop); 7610 } 7611 else { 7612 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7613 } 7614 return ret; 7615 }; 7616 } 7617 if (key === 'delete') { 7618 return (prop) => { 7619 if (target.has(prop)) { 7620 ObserveV2.getObserve().fireChange(conditionalTarget, prop); 7621 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7622 return target.delete(prop); 7623 } 7624 else { 7625 return false; 7626 } 7627 }; 7628 } 7629 if (key === 'clear') { 7630 return () => { 7631 if (target.size > 0) { 7632 target.forEach((_, prop) => { 7633 ObserveV2.getObserve().fireChange(conditionalTarget, prop.toString()); 7634 }); 7635 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7636 ObserveV2.getObserve().addRef(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7637 target.clear(); 7638 } 7639 }; 7640 } 7641 if (key === 'keys' || key === 'values' || key === 'entries') { 7642 return () => { 7643 ObserveV2.getObserve().addRef(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7644 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7645 return target[key](); 7646 }; 7647 } 7648 if (target instanceof Set || (this.isMakeObserved_ && SendableType.isSet(target))) { 7649 if (key === 'add') { 7650 return (val) => { 7651 ObserveV2.getObserve().fireChange(conditionalTarget, val.toString()); 7652 ObserveV2.getObserve().fireChange(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7653 if (!target.has(val)) { 7654 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7655 target.add(val); 7656 } 7657 return receiver; 7658 }; 7659 } 7660 if (key === 'forEach') { 7661 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7662 return function (callbackFn) { 7663 // need to execute it target because it is the internal function for build-in type, and proxy does not have such slot. 7664 // if necessary, addref for each item in Set and also wrap proxy for makeObserved if it is Object. 7665 // currently, just execute it in target because there is no Component need to iterate Set, only Array 7666 const result = ret.call(target, callbackFn); 7667 return result; 7668 }; 7669 } 7670 // Bind to receiver ==> functions are observed 7671 return (typeof ret === 'function') ? ret.bind(receiver) : ret; 7672 } 7673 if (target instanceof Map || (this.isMakeObserved_ && SendableType.isMap(target))) { 7674 if (key === 'get') { 7675 return (prop) => { 7676 if (target.has(prop)) { 7677 ObserveV2.getObserve().addRef(conditionalTarget, prop); 7678 } 7679 else { 7680 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7681 } 7682 let item = target.get(prop); 7683 return (typeof item === 'object' && this.isMakeObserved_) ? RefInfo.get(item).proxy : item; 7684 }; 7685 } 7686 if (key === 'set') { 7687 return (prop, val) => { 7688 if (!target.has(prop)) { 7689 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7690 } 7691 else if (target.get(prop) !== val) { 7692 ObserveV2.getObserve().fireChange(conditionalTarget, prop); 7693 } 7694 ObserveV2.getObserve().fireChange(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7695 target.set(prop, val); 7696 return receiver; 7697 }; 7698 } 7699 if (key === 'forEach') { 7700 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7701 return function (callbackFn) { 7702 // need to execute it target because it is the internal function for build-in type, and proxy does not have such slot. 7703 // if necessary, addref for each item in Map and also wrap proxy for makeObserved if it is Object. 7704 // currently, just execute it in target because there is no Component need to iterate Map, only Array 7705 const result = ret.call(target, callbackFn); 7706 return result; 7707 }; 7708 } 7709 } 7710 // Bind to receiver ==> functions are observed 7711 return (typeof ret === 'function') ? ret.bind(receiver) : ret; 7712 } 7713 set(target, key, value) { 7714 if (typeof key === 'symbol') { 7715 if (!this.isMakeObserved_ && key !== ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7716 target[key] = value; 7717 } 7718 return true; 7719 } 7720 if (target[key] === value) { 7721 return true; 7722 } 7723 target[key] = value; 7724 ObserveV2.getObserve().fireChange(this.getTarget(target), key.toString()); 7725 return true; 7726 } 7727} 7728SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY = '___ob_map_set'; 7729; 7730/* 7731 * Copyright (c) 2024 Huawei Device Co., Ltd. 7732 * Licensed under the Apache License, Version 2.0 (the "License"); 7733 * you may not use this file except in compliance with the License. 7734 * You may obtain a copy of the License at 7735 * 7736 * http://www.apache.org/licenses/LICENSE-2.0 7737 * 7738 * Unless required by applicable law or agreed to in writing, software 7739 * distributed under the License is distributed on an "AS IS" BASIS, 7740 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7741 * See the License for the specific language governing permissions and 7742 * limitations under the License. 7743 */ 7744/** 7745 * 7746 * This file includes only framework internal classes and functions 7747 * non are part of SDK. Do not access from app. 7748 * 7749 * 7750 * ObserveV2 is the singleton object for observing state variable access and 7751 * change 7752 */ 7753// in the case of ForEach, Repeat, AND If, two or more UINodes / elmtIds can render at the same time 7754// e.g. ForEach -> ForEach child Text, Repeat -> Nested Repeat, child Text 7755// Therefore, ObserveV2 needs to keep a strack of currently renderign ids / components 7756// in the same way as thsi is also done for PU stateMgmt with ViewPU.currentlyRenderedElmtIdStack_ 7757class StackOfRenderedComponents { 7758 constructor() { 7759 this.stack_ = new Array(); 7760 } 7761 push(id, cmp) { 7762 this.stack_.push(new StackOfRenderedComponentsItem(id, cmp)); 7763 } 7764 pop() { 7765 const item = this.stack_.pop(); 7766 return item ? [item.id_, item.cmp_] : undefined; 7767 } 7768 top() { 7769 if (this.stack_.length) { 7770 const item = this.stack_[this.stack_.length - 1]; 7771 return [item.id_, item.cmp_]; 7772 } 7773 else { 7774 return undefined; 7775 } 7776 } 7777} 7778class StackOfRenderedComponentsItem { 7779 constructor(id, cmp) { 7780 this.id_ = id; 7781 this.cmp_ = cmp; 7782 } 7783} 7784class ObserveV2 { 7785 constructor() { 7786 // see MonitorV2.observeObjectAccess: bindCmp is the MonitorV2 7787 // see modified ViewV2 and ViewPU observeComponentCreation, bindCmp is the ViewV2 or ViewPU 7788 // bindId: UINode elmtId or watchId, depending on what is being observed 7789 this.stackOfRenderedComponents_ = new StackOfRenderedComponents(); 7790 // Map bindId to WeakRef<ViewPU> | MonitorV2 7791 this.id2cmp_ = {}; 7792 // Map bindId -> Set of @ObservedV2 class objects 7793 // reverse dependency map for quickly removing all dependencies of a bindId 7794 this.id2targets_ = {}; 7795 // queued up Set of bindId 7796 // elmtIds of UINodes need re-render 7797 // @Monitor functions that need to executed 7798 this.elmtIdsChanged_ = new Set(); 7799 this.computedPropIdsChanged_ = new Set(); 7800 this.monitorIdsChanged_ = new Set(); 7801 this.monitorFuncsToRun_ = new Set(); 7802 this.persistenceChanged_ = new Set(); 7803 // avoid recursive execution of updateDirty 7804 // by state changes => fireChange while 7805 // UINode rerender or @Monitor function execution 7806 this.startDirty_ = false; 7807 // flag to indicate change observation is disabled 7808 this.disabled_ = false; 7809 // flag to indicate ComputedV2 calculation is ongoing 7810 this.calculatingComputedProp_ = false; 7811 } 7812 static getObserve() { 7813 if (!this.obsInstance_) { 7814 this.obsInstance_ = new ObserveV2(); 7815 } 7816 return this.obsInstance_; 7817 } 7818 // return true given value is @ObservedV2 object 7819 static IsObservedObjectV2(value) { 7820 return (value && typeof (value) === 'object' && value[ObserveV2.V2_DECO_META]); 7821 } 7822 // return true if given value is proxied observed object, either makeObserved or autoProxyObject 7823 static IsProxiedObservedV2(value) { 7824 return (value && typeof value === 'object' && value[ObserveV2.SYMBOL_PROXY_GET_TARGET]); 7825 } 7826 // return true given value is the return value of makeObserved 7827 static IsMakeObserved(value) { 7828 return (value && typeof (value) === 'object' && value[ObserveV2.SYMBOL_MAKE_OBSERVED]); 7829 } 7830 static IsTrackedProperty(parentObj, prop) { 7831 if (!parentObj || typeof parentObj !== 'object') { 7832 return false; 7833 } 7834 const trackedKey = ObserveV2.OB_PREFIX + prop; 7835 const consumerKey = ObserveV2.CONSUMER_PREFIX + prop; 7836 const computedKey = ComputedV2.COMPUTED_CACHED_PREFIX + prop; 7837 if (trackedKey in parentObj || consumerKey in parentObj || computedKey in parentObj) { 7838 return true; 7839 } 7840 return false; 7841 } 7842 static getCurrentRecordedId() { 7843 const bound = ObserveV2.getObserve().stackOfRenderedComponents_.top(); 7844 return bound ? bound[0] : -1; 7845 } 7846 // At the start of observeComponentCreation or 7847 // MonitorV2 observeObjectAccess 7848 startRecordDependencies(cmp, id, doClearBinding = true) { 7849 if (cmp != null) { 7850 doClearBinding && this.clearBinding(id); 7851 this.stackOfRenderedComponents_.push(id, cmp); 7852 } 7853 } 7854 // At the start of observeComponentCreation or 7855 // MonitorV2 observeObjectAccess 7856 stopRecordDependencies() { 7857 const bound = this.stackOfRenderedComponents_.pop(); 7858 if (bound === undefined) { 7859 stateMgmtConsole.error('stopRecordDependencies finds empty stack. Internal error!'); 7860 return; 7861 } 7862 let targetsSet; 7863 if ((targetsSet = this.id2targets_[bound[0]]) !== undefined && targetsSet.size) { 7864 // only add IView | MonitorV2 | ComputedV2 if at least one dependency was 7865 // recorded when rendering this ViewPU/ViewV2/Monitor/ComputedV2 7866 // ViewPU is the likely case where no dependecy gets recorded 7867 // for others no dependencies are unlikely to happen 7868 this.id2cmp_[bound[0]] = new WeakRef(bound[1]); 7869 } 7870 } 7871 // clear any previously created dependency view model object to elmtId 7872 // find these view model objects with the reverse map id2targets_ 7873 clearBinding(id) { 7874 var _a; 7875 // multiple weakRefs might point to the same target - here we get Set of unique targets 7876 const targetSet = new Set(); 7877 (_a = this.id2targets_[id]) === null || _a === void 0 ? void 0 : _a.forEach((weak) => { 7878 if (weak.deref() instanceof Object) { 7879 targetSet.add(weak.deref()); 7880 } 7881 }); 7882 targetSet.forEach((target) => { 7883 var _a, _b; 7884 const idRefs = target[ObserveV2.ID_REFS]; 7885 const symRefs = target[ObserveV2.SYMBOL_REFS]; 7886 if (idRefs) { 7887 (_a = idRefs[id]) === null || _a === void 0 ? void 0 : _a.forEach(key => { var _a; return (_a = symRefs === null || symRefs === void 0 ? void 0 : symRefs[key]) === null || _a === void 0 ? void 0 : _a.delete(id); }); 7888 delete idRefs[id]; 7889 } 7890 else { 7891 for (let key in symRefs) { 7892 (_b = symRefs[key]) === null || _b === void 0 ? void 0 : _b.delete(id); 7893 } 7894 ; 7895 } 7896 }); 7897 delete this.id2targets_[id]; 7898 delete this.id2cmp_[id]; 7899 7900 7901 } 7902 /** 7903 * 7904 * this cleanUpId2CmpDeadReferences() 7905 * id2cmp is a 'map' object id => WeakRef<Object> where object is ViewV2, ViewPU, MonitorV2 or ComputedV2 7906 * This method iterates over the object entries and deleted all those entries whose value can no longer 7907 * be deref'ed. 7908 * 7909 * cleanUpId2TargetsDeadReferences() 7910 * is2targets is a 'map' object id => Set<WeakRef<Object>> 7911 * the method traverses over the object entries and for each value of type 7912 * Set<WeakRef<Object>> removes all those items from the set that can no longer be deref'ed. 7913 * 7914 * According to JS specifications, it is up to ArlTS runtime GC implementation when to collect unreferences objects. 7915 * Parameters such as available memory, ArkTS processing load, number and size of all JS objects for GC collection 7916 * can impact the time delay between an object loosing last reference and GC collecting this object. 7917 * 7918 * WeakRef deref() returns the object until GC has collected it. 7919 * The id2cmp and is2targets cleanup herein depends on WeakRef.deref() to return undefined, i.e. it depends on GC 7920 * collecting 'cmp' or 'target' objects. Only then the algorithm can remove the entry from id2cmp / from id2target. 7921 * It is therefore to be expected behavior that these map objects grow and they a contain a larger number of 7922 * MonitorV2, ComputedV2, and/or view model @Observed class objects that are no longer used / referenced by the application. 7923 * Only after ArkTS runtime GC has collected them, this function is able to clean up the id2cmp and is2targets. 7924 * 7925 * This cleanUpDeadReferences() function gets called from UINodeRegisterProxy.uiNodeCleanUpIdleTask() 7926 * 7927 */ 7928 cleanUpDeadReferences() { 7929 this.cleanUpId2CmpDeadReferences(); 7930 this.cleanUpId2TargetsDeadReferences(); 7931 } 7932 cleanUpId2CmpDeadReferences() { 7933 7934 for (const id in this.id2cmp_) { 7935 7936 let weakRef = this.id2cmp_[id]; 7937 if (weakRef && typeof weakRef === 'object' && 'deref' in weakRef && weakRef.deref() === undefined) { 7938 7939 delete this.id2cmp_[id]; 7940 } 7941 } 7942 } 7943 cleanUpId2TargetsDeadReferences() { 7944 for (const id in this.id2targets_) { 7945 const targetSet = this.id2targets_[id]; 7946 if (targetSet && targetSet instanceof Set) { 7947 for (let weakTarget of targetSet) { 7948 if (weakTarget.deref() === undefined) { 7949 7950 targetSet.delete(weakTarget); 7951 } 7952 } // for targetSet 7953 } 7954 } // for id2targets_ 7955 } 7956 /** 7957 * counts number of WeakRef<Object> entries in id2cmp_ 'map' object 7958 * @returns total count and count of WeakRefs that can be deref'ed 7959 * Methods only for testing 7960 */ 7961 get id2CompDeRefSize() { 7962 let totalCount = 0; 7963 let aliveCount = 0; 7964 let comp; 7965 for (const id in this.id2cmp_) { 7966 totalCount++; 7967 let weakRef = this.id2cmp_[id]; 7968 if (weakRef && 'deref' in weakRef && (comp = weakRef.deref()) && comp instanceof Object) { 7969 aliveCount++; 7970 } 7971 } 7972 return [totalCount, aliveCount]; 7973 } 7974 /** counts number of target WeakRef<object> entries in all the Sets inside id2targets 'map' object 7975 * @returns total count and those can be dereferenced 7976 * Methods only for testing 7977 */ 7978 get id2TargetsDerefSize() { 7979 let totalCount = 0; 7980 let aliveCount = 0; 7981 for (const id in this.id2targets_) { 7982 const targetSet = this.id2targets_[id]; 7983 if (targetSet && targetSet instanceof Set) { 7984 for (let weakTarget of targetSet) { 7985 totalCount++; 7986 if (weakTarget.deref()) { 7987 aliveCount++; 7988 } 7989 } // for targetSet 7990 } 7991 } // for id2targets_ 7992 return [totalCount, aliveCount]; 7993 } 7994 // Register the "parent" @Monitor id to known components 7995 // it's needed when running the dirty @Monitor functions 7996 registerMonitor(monitor, id) { 7997 this.id2cmp_[id] = new WeakRef(monitor); 7998 } 7999 // add dependency view model object 'target' property 'attrName' 8000 // to current this.bindId 8001 addRef(target, attrName) { 8002 const bound = this.stackOfRenderedComponents_.top(); 8003 if (!bound) { 8004 return; 8005 } 8006 if (bound[0] === UINodeRegisterProxy.monitorIllegalV1V2StateAccess) { 8007 const error = `${attrName}: ObserveV2.addRef: trying to use V2 state '${attrName}' to init/update child V2 @Component. Application error`; 8008 stateMgmtConsole.applicationError(error); 8009 throw new TypeError(error); 8010 } 8011 8012 this.addRef4IdInternal(bound[0], target, attrName); 8013 } 8014 addRef4Id(id, target, attrName) { 8015 8016 this.addRef4IdInternal(id, target, attrName); 8017 } 8018 addRef4IdInternal(id, target, attrName) { 8019 var _a, _b, _c, _d; 8020 var _e, _f; 8021 // Map: attribute/symbol -> dependent id 8022 const symRefs = (_a = target[_e = ObserveV2.SYMBOL_REFS]) !== null && _a !== void 0 ? _a : (target[_e] = {}); 8023 (_b = symRefs[attrName]) !== null && _b !== void 0 ? _b : (symRefs[attrName] = new Set()); 8024 symRefs[attrName].add(id); 8025 // Map id -> attribute/symbol 8026 // optimization for faster clearBinding 8027 const idRefs = target[ObserveV2.ID_REFS]; 8028 if (idRefs) { 8029 (_c = idRefs[id]) !== null && _c !== void 0 ? _c : (idRefs[id] = new Set()); 8030 idRefs[id].add(attrName); 8031 } 8032 const targetSet = (_d = (_f = this.id2targets_)[id]) !== null && _d !== void 0 ? _d : (_f[id] = new Set()); 8033 targetSet.add(new WeakRef(target)); 8034 } 8035 /** 8036 * 8037 * @param target set tracked attribute to new value without notifying the change 8038 * !! use with caution !! 8039 * @param attrName 8040 * @param newValue 8041 */ 8042 setUnmonitored(target, attrName, newValue) { 8043 const storeProp = ObserveV2.OB_PREFIX + attrName; 8044 if (storeProp in target) { 8045 // @Track attrName 8046 8047 target[storeProp] = newValue; 8048 } 8049 else { 8050 8051 // untracked attrName 8052 target[attrName] = newValue; 8053 } 8054 } 8055 /** 8056 * Execute given task while state change observation is disabled 8057 * A state mutation caused by the task will NOT trigger UI rerender 8058 * and @Monitor function execution. 8059 * 8060 * !!! Use with Caution !!! 8061 * 8062 * @param task a function to execute without monitoring state changes 8063 * @returns task function return value 8064 */ 8065 executeUnobserved(task) { 8066 8067 this.disabled_ = true; 8068 let ret; 8069 try { 8070 ret = task(); 8071 } 8072 catch (e) { 8073 stateMgmtConsole.applicationError(`executeUnobserved - task execution caused error ${e} !`); 8074 } 8075 this.disabled_ = false; 8076 8077 return ret; 8078 } 8079 // mark view model object 'target' property 'attrName' as changed 8080 // notify affected watchIds and elmtIds 8081 fireChange(target, attrName) { 8082 // enable to get more fine grained traces 8083 // including 2 (!) .end calls. 8084 if (!target[ObserveV2.SYMBOL_REFS] || this.disabled_) { 8085 return; 8086 } 8087 const bound = this.stackOfRenderedComponents_.top(); 8088 if (this.calculatingComputedProp_) { 8089 const prop = bound ? bound[1].getProp() : 'unknown computed property'; 8090 const error = `Usage of ILLEGAL @Computed function detected for ${prop}! The @Computed function MUST NOT change the state of any observed state variable!`; 8091 stateMgmtConsole.applicationError(error); 8092 throw new Error(error); 8093 } 8094 // enable this trace marker for more fine grained tracing of the update pipeline 8095 // note: two (!) end markers need to be enabled 8096 let changedIdSet = target[ObserveV2.SYMBOL_REFS][attrName]; 8097 if (!changedIdSet || !(changedIdSet instanceof Set)) { 8098 return; 8099 } 8100 8101 for (const id of changedIdSet) { 8102 // Cannot fireChange the object that is being created. 8103 if (bound && id === bound[0]) { 8104 continue; 8105 } 8106 // if this is the first id to be added to any Set of changed ids, 8107 // schedule an 'updateDirty' task 8108 // that will run after the current call stack has unwound. 8109 // purpose of check for startDirty_ is to avoid going into recursion. This could happen if 8110 // exec a re-render or exec a monitor function changes some state -> calls fireChange -> ... 8111 if ((this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size === 0) && 8112 /* update not already in progress */ !this.startDirty_) { 8113 Promise.resolve() 8114 .then(this.updateDirty.bind(this)) 8115 .catch(error => { 8116 stateMgmtConsole.applicationError(`Exception occurred during the update process involving @Computed properties, @Monitor functions or UINode re-rendering`, error); 8117 _arkUIUncaughtPromiseError(error); 8118 }); 8119 } 8120 // add bindId to the correct Set of pending changes. 8121 if (id < ComputedV2.MIN_COMPUTED_ID) { 8122 this.elmtIdsChanged_.add(id); 8123 } 8124 else if (id < MonitorV2.MIN_WATCH_ID) { 8125 this.computedPropIdsChanged_.add(id); 8126 } 8127 else if (id < PersistenceV2Impl.MIN_PERSISTENCE_ID) { 8128 this.monitorIdsChanged_.add(id); 8129 } 8130 else { 8131 this.persistenceChanged_.add(id); 8132 } 8133 } // for 8134 } 8135 updateDirty() { 8136 this.startDirty_ = true; 8137 this.updateDirty2(false); 8138 this.startDirty_ = false; 8139 } 8140 /** 8141 * execute /update in this order 8142 * - @Computed variables 8143 * - @Monitor functions 8144 * - UINode re-render 8145 * three nested loops, means: 8146 * process @Computed until no more @Computed need update 8147 * process @Monitor until no more @Computed and @Monitor 8148 * process UINode update until no more @Computed and @Monitor and UINode rerender 8149 * 8150 * @param updateUISynchronously should be set to true if called during VSYNC only 8151 * 8152 */ 8153 updateDirty2(updateUISynchronously = false) { 8154 aceTrace.begin('updateDirty2'); 8155 8156 // obtain and unregister the removed elmtIds 8157 UINodeRegisterProxy.obtainDeletedElmtIds(); 8158 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 8159 // priority order of processing: 8160 // 1- update computed properties until no more need computed props update 8161 // 2- update monitors paths until no more monitors paths and no more computed props 8162 // 3- run all monitor functions 8163 // 4- update UINodes until no more monitors, no more computed props, and no more UINodes 8164 // FIXME prevent infinite loops 8165 do { 8166 do { 8167 while (this.computedPropIdsChanged_.size) { 8168 // sort the ids and update in ascending order 8169 // If a @Computed property depends on other @Computed properties, their 8170 // ids will be smaller as they are defined first. 8171 const computedProps = Array.from(this.computedPropIdsChanged_).sort((id1, id2) => id1 - id2); 8172 this.computedPropIdsChanged_ = new Set(); 8173 this.updateDirtyComputedProps(computedProps); 8174 } 8175 if (this.persistenceChanged_.size) { 8176 const persistKeys = Array.from(this.persistenceChanged_); 8177 this.persistenceChanged_ = new Set(); 8178 PersistenceV2Impl.instance().onChangeObserved(persistKeys); 8179 } 8180 if (this.monitorIdsChanged_.size) { 8181 const monitors = this.monitorIdsChanged_; 8182 this.monitorIdsChanged_ = new Set(); 8183 this.updateDirtyMonitorPaths(monitors); 8184 } 8185 if (this.monitorFuncsToRun_.size) { 8186 const monitorFuncs = this.monitorFuncsToRun_; 8187 this.monitorFuncsToRun_ = new Set(); 8188 this.runDirtyMonitors(monitorFuncs); 8189 } 8190 } while (this.monitorIdsChanged_.size + this.persistenceChanged_.size + 8191 this.computedPropIdsChanged_.size + this.monitorFuncsToRun_.size > 0); 8192 if (this.elmtIdsChanged_.size) { 8193 const elmtIds = Array.from(this.elmtIdsChanged_).sort((elmtId1, elmtId2) => elmtId1 - elmtId2); 8194 this.elmtIdsChanged_ = new Set(); 8195 updateUISynchronously ? this.updateUINodesSynchronously(elmtIds) : this.updateUINodes(elmtIds); 8196 } 8197 } while (this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size > 0); 8198 8199 aceTrace.end(); 8200 } 8201 updateDirtyComputedProps(computed) { 8202 8203 aceTrace.begin(`ObservedV2.updateDirtyComputedProps ${computed.length} @Computed`); 8204 computed.forEach((id) => { 8205 let comp; 8206 let weakComp = this.id2cmp_[id]; 8207 if (weakComp && 'deref' in weakComp && (comp = weakComp.deref()) && comp instanceof ComputedV2) { 8208 const target = comp.getTarget(); 8209 if (target instanceof ViewV2 && !target.isViewActive()) { 8210 // add delayed ComputedIds id 8211 target.addDelayedComputedIds(id); 8212 } 8213 else { 8214 comp.fireChange(); 8215 } 8216 } 8217 }); 8218 aceTrace.end(); 8219 } 8220 updateDirtyMonitorPaths(monitors) { 8221 8222 aceTrace.begin(`ObservedV2.updateDirtyMonitorPaths: ${Array.from(monitors).length} @Monitor`); 8223 let weakMonitor; 8224 let monitor; 8225 let ret = 0; 8226 monitors.forEach((watchId) => { 8227 ret = 0; 8228 weakMonitor = this.id2cmp_[watchId]; 8229 if (weakMonitor && 'deref' in weakMonitor && (monitor = weakMonitor.deref()) && monitor instanceof MonitorV2) { 8230 ret = monitor.notifyChange(watchId); 8231 } 8232 // Collect @Monitor functions that need to be executed later 8233 if (ret > 0) { 8234 this.monitorFuncsToRun_.add(ret); 8235 } 8236 }); 8237 aceTrace.end(); 8238 } 8239 runDirtyMonitors(monitors) { 8240 8241 aceTrace.begin(`ObservedV2.runDirtyMonitors: ${Array.from(monitors).length} @Monitor`); 8242 let weakMonitor; 8243 let monitor; 8244 let monitorTarget; 8245 monitors.forEach((watchId) => { 8246 weakMonitor = this.id2cmp_[watchId]; 8247 if (weakMonitor && 'deref' in weakMonitor && (monitor = weakMonitor.deref()) && monitor instanceof MonitorV2) { 8248 if (((monitorTarget = monitor.getTarget()) instanceof ViewV2) && !monitorTarget.isViewActive()) { 8249 // monitor notifyChange delayed if target is a View that is not active 8250 monitorTarget.addDelayedMonitorIds(watchId); 8251 } 8252 else { 8253 monitor.runMonitorFunction(); 8254 } 8255 } 8256 }); 8257 aceTrace.end(); 8258 } 8259 /** 8260 * This version of UpdateUINodes does not wait for VSYNC, violates rules 8261 * calls UpdateElement, thereby avoids the long and frequent code path from 8262 * FlushDirtyNodesUpdate to CustomNode to ViewV2.updateDirtyElements to UpdateElement 8263 * Code left here to reproduce benchmark measurements, compare with future optimisation 8264 * @param elmtIds 8265 * 8266 */ 8267 updateUINodesSynchronously(elmtIds) { 8268 8269 aceTrace.begin(`ObserveV2.updateUINodesSynchronously: ${elmtIds.length} elmtId`); 8270 let view; 8271 let weak; 8272 elmtIds.forEach((elmtId) => { 8273 if ((weak = this.id2cmp_[elmtId]) && (typeof weak === 'object') && ('deref' in weak) && 8274 (view = weak.deref()) && ((view instanceof ViewV2) || (view instanceof ViewPU))) { 8275 if (view.isViewActive()) { 8276 // FIXME need to call syncInstanceId before update? 8277 view.UpdateElement(elmtId); 8278 } 8279 else if (view instanceof ViewV2) { 8280 // schedule delayed update once the view gets active 8281 view.scheduleDelayedUpdate(elmtId); 8282 } 8283 } // if ViewV2 or ViewPU 8284 }); 8285 aceTrace.end(); 8286 } 8287 // This is the code path similar to V2, follows the rule that UI updates on VSYNC. 8288 // ViewPU/ViewV2 queues the elmtId that need update, marks the CustomNode dirty in RenderContext 8289 // On next VSYNC runs FlushDirtyNodesUpdate to call rerender to call UpdateElement. Much longer code path 8290 // much slower 8291 updateUINodes(elmtIds) { 8292 8293 aceTrace.begin(`ObserveV2.updateUINodes: ${elmtIds.length} elmtId`); 8294 let viewWeak; 8295 let view; 8296 elmtIds.forEach((elmtId) => { 8297 viewWeak = this.id2cmp_[elmtId]; 8298 if (viewWeak && 'deref' in viewWeak && (view = viewWeak.deref()) && 8299 ((view instanceof ViewV2) || (view instanceof ViewPU))) { 8300 if (view.isViewActive()) { 8301 view.uiNodeNeedUpdateV2(elmtId); 8302 } 8303 else if (view instanceof ViewV2) { 8304 // schedule delayed update once the view gets active 8305 view.scheduleDelayedUpdate(elmtId); 8306 } 8307 } 8308 }); 8309 aceTrace.end(); 8310 } 8311 constructMonitor(owningObject, owningObjectName) { 8312 let watchProp = Symbol.for(MonitorV2.WATCH_PREFIX + owningObjectName); 8313 if (owningObject && (typeof owningObject === 'object') && owningObject[watchProp]) { 8314 Object.entries(owningObject[watchProp]).forEach(([pathString, monitorFunc]) => { 8315 var _a; 8316 var _b; 8317 if (monitorFunc && pathString && typeof monitorFunc === 'function') { 8318 const monitor = new MonitorV2(owningObject, pathString, monitorFunc); 8319 monitor.InitRun(); 8320 const refs = (_a = owningObject[_b = ObserveV2.MONITOR_REFS]) !== null && _a !== void 0 ? _a : (owningObject[_b] = {}); 8321 // store a reference inside owningObject 8322 // thereby MonitorV2 will share lifespan as owning @ComponentV2 or @ObservedV2 8323 // remember: id2cmp only has a WeakRef to MonitorV2 obj 8324 refs[monitorFunc.name] = monitor; 8325 } 8326 // FIXME Else handle error 8327 }); 8328 } // if target[watchProp] 8329 } 8330 constructComputed(owningObject, owningObjectName) { 8331 const computedProp = Symbol.for(ComputedV2.COMPUTED_PREFIX + owningObjectName); 8332 if (owningObject && (typeof owningObject === 'object') && owningObject[computedProp]) { 8333 Object.entries(owningObject[computedProp]).forEach(([computedPropertyName, computeFunc]) => { 8334 var _a, _b; 8335 var _c; 8336 8337 const computed = new ComputedV2(owningObject, computedPropertyName, computeFunc); 8338 computed.InitRun(); 8339 const refs = (_b = owningObject[_c = ObserveV2.COMPUTED_REFS]) !== null && _b !== void 0 ? _b : (owningObject[_c] = {}); 8340 // store a reference inside owningObject 8341 // thereby ComputedV2 will share lifespan as owning @ComponentV2 or @ObservedV2 8342 // remember: id2cmp only has a WeakRef to ComputedV2 obj 8343 refs[computedPropertyName] = computed; 8344 }); 8345 } 8346 } 8347 clearWatch(id) { 8348 this.clearBinding(id); 8349 } 8350 static autoProxyObject(target, key) { 8351 let val = target[key]; 8352 // Not an object, not a collection, no proxy required 8353 if (!val || typeof (val) !== 'object' || 8354 !(Array.isArray(val) || val instanceof Set || val instanceof Map || val instanceof Date)) { 8355 return val; 8356 } 8357 // Only collections require proxy observation, and if it has been observed, it does not need to be observed again. 8358 if (!val[ObserveV2.SYMBOL_PROXY_GET_TARGET]) { 8359 if (Array.isArray(val)) { 8360 target[key] = new Proxy(val, ObserveV2.arrayProxy); 8361 } 8362 else if (val instanceof Set || val instanceof Map) { 8363 target[key] = new Proxy(val, ObserveV2.setMapProxy); 8364 } 8365 else { 8366 target[key] = new Proxy(val, ObserveV2.objectProxy); 8367 } 8368 val = target[key]; 8369 } 8370 // If the return value is an Array, Set, Map 8371 if (!(val instanceof Date)) { 8372 ObserveV2.getObserve().addRef(ObserveV2.IsMakeObserved(val) ? RefInfo.get(UIUtilsImpl.instance().getTarget(val)) : 8373 val, ObserveV2.OB_LENGTH); 8374 } 8375 return val; 8376 } 8377 /** 8378 * Helper function to add meta data about decorator to ViewPU or ViewV2 8379 * @param proto prototype object of application class derived from ViewPU or ViewV2 8380 * @param varName decorated variable 8381 * @param deco '@Local', '@Event', etc 8382 */ 8383 static addVariableDecoMeta(proto, varName, deco) { 8384 var _a; 8385 var _b; 8386 // add decorator meta data 8387 const meta = (_a = proto[_b = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (proto[_b] = {}); 8388 meta[varName] = {}; 8389 meta[varName].deco = deco; 8390 // FIXME 8391 // when splitting ViewPU and ViewV2 8392 // use instanceOf. Until then, this is a workaround. 8393 // any @Local, @Trace, etc V2 event handles this function to return false 8394 Reflect.defineProperty(proto, 'isViewV2', { 8395 get() { return true; }, 8396 enumerable: false 8397 }); 8398 } 8399 static addParamVariableDecoMeta(proto, varName, deco, deco2) { 8400 var _a, _b; 8401 var _c; 8402 // add decorator meta data 8403 const meta = (_a = proto[_c = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (proto[_c] = {}); 8404 (_b = meta[varName]) !== null && _b !== void 0 ? _b : (meta[varName] = {}); 8405 if (deco) { 8406 meta[varName].deco = deco; 8407 } 8408 if (deco2) { 8409 meta[varName].deco2 = deco2; 8410 } 8411 // FIXME 8412 // when splitting ViewPU and ViewV2 8413 // use instanceOf. Until then, this is a workaround. 8414 // any @Local, @Trace, etc V2 event handles this function to return false 8415 Reflect.defineProperty(proto, 'isViewV2', { 8416 get() { return true; }, 8417 enumerable: false 8418 }); 8419 } 8420 static usesV2Variables(proto) { 8421 return (proto && typeof proto === 'object' && proto[ObserveV2.V2_DECO_META]); 8422 } 8423} // class ObserveV2 8424// meta data about decorated variable inside prototype 8425ObserveV2.V2_DECO_META = Symbol('__v2_deco_meta__'); 8426ObserveV2.SYMBOL_REFS = Symbol('__use_refs__'); 8427ObserveV2.ID_REFS = Symbol('__id_refs__'); 8428ObserveV2.MONITOR_REFS = Symbol('___monitor_refs_'); 8429ObserveV2.COMPUTED_REFS = Symbol('___computed_refs_'); 8430ObserveV2.SYMBOL_PROXY_GET_TARGET = Symbol('__proxy_get_target'); 8431ObserveV2.SYMBOL_MAKE_OBSERVED = Symbol('___make_observed__'); 8432ObserveV2.OB_PREFIX = '__ob_'; // OB_PREFIX + attrName => backing store attribute name 8433ObserveV2.OB_PREFIX_LEN = 5; 8434ObserveV2.CONSUMER_PREFIX = '__consumer_'; 8435// used by array Handler to create dependency on artificial 'length' 8436// property of array, mark it as changed when array has changed. 8437ObserveV2.OB_LENGTH = '___obj_length'; 8438ObserveV2.setMapProxy = new SetMapProxyHandler(); 8439ObserveV2.arrayProxy = new ArrayProxyHandler(); 8440ObserveV2.objectProxy = new ObjectProxyHandler(); 8441const trackInternal = (target, propertyKey) => { 8442 var _a; 8443 var _b; 8444 if (typeof target === 'function' && !Reflect.has(target, propertyKey)) { 8445 // dynamic track,and it not a static attribute 8446 target = target.prototype; 8447 } 8448 const storeProp = ObserveV2.OB_PREFIX + propertyKey; 8449 target[storeProp] = target[propertyKey]; 8450 Reflect.defineProperty(target, propertyKey, { 8451 get() { 8452 ObserveV2.getObserve().addRef(this, propertyKey); 8453 return ObserveV2.autoProxyObject(this, ObserveV2.OB_PREFIX + propertyKey); 8454 }, 8455 set(val) { 8456 // If the object has not been observed, you can directly assign a value to it. This improves performance. 8457 if (val !== this[storeProp]) { 8458 this[storeProp] = val; 8459 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 8460 ObserveV2.getObserve().fireChange(this, propertyKey); 8461 } 8462 } 8463 }, 8464 enumerable: true 8465 }); 8466 // this marks the proto as having at least one @Trace property inside 8467 // used by IsObservedObjectV2 8468 (_a = target[_b = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (target[_b] = {}); 8469}; // trackInternal 8470/* 8471 * Copyright (c) 2024 Huawei Device Co., Ltd. 8472 * Licensed under the Apache License, Version 2.0 (the "License"); 8473 * you may not use this file except in compliance with the License. 8474 * You may obtain a copy of the License at 8475 * 8476 * http://www.apache.org/licenses/LICENSE-2.0 8477 * 8478 * Unless required by applicable law or agreed to in writing, software 8479 * distributed under the License is distributed on an "AS IS" BASIS, 8480 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8481 * See the License for the specific language governing permissions and 8482 * limitations under the License. 8483 */ 8484/** 8485 * 8486 * This file includes only framework internal classes and functions 8487 * non are part of SDK. Do not access from app. 8488 * 8489 * 8490 * Helper class for handling V2 decorated variables 8491 */ 8492class VariableUtilV2 { 8493 /** 8494 * setReadOnlyAttr - helper function used to update @param 8495 * from parent @Component. Not allowed for @param @once . 8496 * @param target - the object, usually the ViewV2 8497 * @param attrName - @param variable name 8498 * @param newValue - update to new value 8499 */ 8500 static initParam(target, attrName, newValue) { 8501 var _a; 8502 const meta = (_a = target[ObserveV2.V2_DECO_META]) === null || _a === void 0 ? void 0 : _a[attrName]; 8503 if (!meta || meta.deco !== '@param') { 8504 const error = `Use initParam(${attrName}) only to init @param. Internal error!`; 8505 stateMgmtConsole.error(error); 8506 throw new Error(error); 8507 } 8508 // prevent update for @param @once 8509 const storeProp = ObserveV2.OB_PREFIX + attrName; 8510 8511 target[storeProp] = newValue; 8512 ObserveV2.getObserve().addRef(target, attrName); 8513 } 8514 /** 8515 * setReadOnlyAttr - helper function used to update @param 8516 * from parent @Component. Not allowed for @param @once . 8517 * @param target - the object, usually the ViewV2 8518 * @param attrName - @param variable name 8519 * @param newValue - update to new value 8520 */ 8521 static updateParam(target, attrName, newValue) { 8522 var _a; 8523 // prevent update for @param @once 8524 const meta = (_a = target[ObserveV2.V2_DECO_META]) === null || _a === void 0 ? void 0 : _a[attrName]; 8525 if (!meta || meta.deco !== '@param') { 8526 const error = `Use updateParm(${attrName}) only to update @param. Internal error!`; 8527 stateMgmtConsole.error(error); 8528 throw new Error(error); 8529 } 8530 const storeProp = ObserveV2.OB_PREFIX + attrName; 8531 // @Observed class and @Track attrName 8532 if (newValue === target[storeProp]) { 8533 8534 return; 8535 } 8536 if (meta.deco2 === '@once') { 8537 // @param @once - init but no update 8538 8539 } 8540 else { 8541 8542 target[storeProp] = newValue; 8543 ObserveV2.getObserve().fireChange(target, attrName); 8544 } 8545 } 8546} 8547class ProviderConsumerUtilV2 { 8548 /** 8549 * meta added to the ViewV2 8550 * varName: { deco: '@Provider' | '@Consumer', aliasName: ..... } 8551 * prefix_@Provider_aliasName: {'varName': ..., 'aliasName': ...., 'deco': '@Provider' | '@Consumer' 8552 */ 8553 static metaAliasKey(aliasName, deco) { 8554 return `${ProviderConsumerUtilV2.ALIAS_PREFIX}_${deco}_${aliasName}`; 8555 } 8556 /** 8557 * Helper function to add meta data about @Provider and @Consumer decorators to ViewV2 8558 * similar to @see addVariableDecoMeta, but adds the alias to allow search from @Consumer for @Provider counterpart 8559 * @param proto prototype object of application class derived from ViewV2 8560 * @param varName decorated variable 8561 * @param deco '@Local', '@Event', etc 8562 */ 8563 static addProvideConsumeVariableDecoMeta(proto, varName, aliasName, deco) { 8564 var _a; 8565 var _b; 8566 // add decorator meta data to prototype 8567 const meta = (_a = proto[_b = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (proto[_b] = {}); 8568 // note: aliasName is the actual alias not the prefixed version 8569 meta[varName] = { 'deco': deco, 'aliasName': aliasName }; 8570 // prefix to avoid name collisions with variable of same name as the alias! 8571 const aliasProp = ProviderConsumerUtilV2.metaAliasKey(aliasName, deco); 8572 meta[aliasProp] = { 'varName': varName, 'aliasName': aliasName, 'deco': deco }; 8573 } 8574 /** 8575 * find a @Provider'ed variable from its nearest ancestor ViewV2. 8576 * @param searchingAliasName The key name to search for. 8577 * @returns A tuple containing the ViewPU instance where the provider is found 8578 * and the provider name 8579 * If root @Component reached without finding, returns undefined. 8580 */ 8581 static findProvider(view, aliasName) { 8582 var _a; 8583 let checkView = view === null || view === void 0 ? void 0 : view.getParent(); 8584 const searchingPrefixedAliasName = ProviderConsumerUtilV2.metaAliasKey(aliasName, '@Provider'); 8585 8586 while (checkView) { 8587 const meta = (_a = checkView.constructor) === null || _a === void 0 ? void 0 : _a.prototype[ObserveV2.V2_DECO_META]; 8588 if (checkView instanceof ViewV2 && meta && meta[searchingPrefixedAliasName]) { 8589 const aliasMeta = meta[searchingPrefixedAliasName]; 8590 const providedVarName = (aliasMeta && (aliasMeta.deco === '@Provider') ? aliasMeta.varName : undefined); 8591 if (providedVarName) { 8592 8593 return [checkView, providedVarName]; 8594 } 8595 } 8596 checkView = checkView.getParent(); 8597 } 8598 ; // while 8599 stateMgmtConsole.warn(`findProvider: ${view.debugInfo__()} @Consumer('${aliasName}'), no matching @Provider found amongst ancestor @ComponentV2's!`); 8600 return undefined; 8601 } 8602 /** 8603 * Connects a consumer property of a view (`consumeView`) to a provider property of another view (`provideView`). 8604 * This function establishes a link between the consumer and provider, allowing the consumer to access and update 8605 * the provider's value directly. If the provider view is garbage collected, attempts to access the provider 8606 * property will throw an error. 8607 * 8608 * @param consumeView - The view object that consumes data from the provider. 8609 * @param consumeVarName - The name of the property in the consumer view that will be linked to the provider. 8610 * @param provideView - The view object that provides the data to the consumer. 8611 * @param provideVarName - The name of the property in the provider view that the consumer will access. 8612 * 8613 */ 8614 static connectConsumer2Provider(consumeView, consumeVarName, provideView, provideVarName) { 8615 var _a; 8616 const weakView = new WeakRef(provideView); 8617 const provideViewName = (_a = provideView.constructor) === null || _a === void 0 ? void 0 : _a.name; 8618 Reflect.defineProperty(consumeView, consumeVarName, { 8619 get() { 8620 let view = weakView.deref(); 8621 8622 ObserveV2.getObserve().addRef(this, consumeVarName); 8623 if (!view) { 8624 const error = `${this.debugInfo__()}: get() on @Consumer ${consumeVarName}: providing @ComponentV2 with @Provider ${provideViewName} no longer exists. Application error.`; 8625 stateMgmtConsole.error(error); 8626 throw new Error(error); 8627 } 8628 return view[provideVarName]; 8629 }, 8630 set(val) { 8631 let view = weakView.deref(); 8632 // If the object has not been observed, you can directly assign a value to it. This improves performance. 8633 8634 if (!view) { 8635 const error = `${this.debugInfo__()}: set() on @Consumer ${consumeVarName}: providing @ComponentV2 with @Provider ${provideViewName} no longer exists. Application error.`; 8636 stateMgmtConsole.error(error); 8637 throw new Error(error); 8638 } 8639 if (val !== view[provideVarName]) { 8640 8641 view[provideVarName] = val; 8642 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 8643 ObserveV2.getObserve().fireChange(this, consumeVarName); 8644 } 8645 } 8646 }, 8647 enumerable: true 8648 }); 8649 return provideView[provideVarName]; 8650 } 8651 static defineConsumerWithoutProvider(consumeView, consumeVarName, consumerLocalVal) { 8652 8653 const storeProp = ObserveV2.OB_PREFIX + consumeVarName; 8654 consumeView[storeProp] = consumerLocalVal; // use local init value, also as backing store 8655 Reflect.defineProperty(consumeView, consumeVarName, { 8656 get() { 8657 ObserveV2.getObserve().addRef(this, consumeVarName); 8658 return ObserveV2.autoProxyObject(this, ObserveV2.OB_PREFIX + consumeVarName); 8659 }, 8660 set(val) { 8661 if (val !== this[storeProp]) { 8662 this[storeProp] = val; 8663 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 8664 ObserveV2.getObserve().fireChange(this, consumeVarName); 8665 } 8666 } 8667 }, 8668 enumerable: true 8669 }); 8670 return consumeView[storeProp]; 8671 } 8672} 8673ProviderConsumerUtilV2.ALIAS_PREFIX = '___pc_alias_'; 8674/* 8675 Internal decorator for @Trace without usingV2ObservedTrack call. 8676 Real @Trace decorator function is in v2_decorators.ts 8677*/ 8678const Trace_Internal = (target, propertyKey) => { 8679 return trackInternal(target, propertyKey); 8680}; 8681/* 8682 Internal decorator for @ObservedV2 without usingV2ObservedTrack call. 8683 Real @ObservedV2 decorator function is in v2_decorators.ts 8684*/ 8685function ObservedV2_Internal(BaseClass) { 8686 return observedV2Internal(BaseClass); 8687} 8688/* 8689 @ObservedV2 decorator function uses this in v2_decorators.ts 8690*/ 8691function observedV2Internal(BaseClass) { 8692 // prevent @Track inside @ObservedV2 class 8693 if (BaseClass.prototype && Reflect.has(BaseClass.prototype, TrackedObject.___IS_TRACKED_OPTIMISED)) { 8694 const error = `'@Observed class ${BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name}': invalid use of V1 @Track decorator inside V2 @ObservedV2 class. Need to fix class definition to use @Track.`; 8695 stateMgmtConsole.applicationError(error); 8696 throw new Error(error); 8697 } 8698 if (BaseClass.prototype && !Reflect.has(BaseClass.prototype, ObserveV2.V2_DECO_META)) { 8699 // not an error, suspicious of developer oversight 8700 stateMgmtConsole.warn(`'@Observed class ${BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name}': no @Track property inside. Is this intended? Check our application.`); 8701 } 8702 // Use ID_REFS only if number of observed attrs is significant 8703 const attrList = Object.getOwnPropertyNames(BaseClass.prototype); 8704 const count = attrList.filter(attr => attr.startsWith(ObserveV2.OB_PREFIX)).length; 8705 if (count > 5) { 8706 8707 BaseClass.prototype[ObserveV2.ID_REFS] = {}; 8708 } 8709 const observedClass = class extends BaseClass { 8710 constructor(...args) { 8711 super(...args); 8712 AsyncAddComputedV2.addComputed(this, BaseClass.name); 8713 AsyncAddMonitorV2.addMonitor(this, BaseClass.name); 8714 } 8715 }; 8716 Object.defineProperty(observedClass, 'name', { value: BaseClass.name }); 8717 return observedClass; 8718} 8719/* 8720 * Copyright (c) 2024 Huawei Device Co., Ltd. 8721 * Licensed under the Apache License, Version 2.0 (the "License"); 8722 * you may not use this file except in compliance with the License. 8723 * You may obtain a copy of the License at 8724 * 8725 * http://www.apache.org/licenses/LICENSE-2.0 8726 * 8727 * Unless required by applicable law or agreed to in writing, software 8728 * distributed under the License is distributed on an "AS IS" BASIS, 8729 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8730 * See the License for the specific language governing permissions and 8731 * limitations under the License. 8732 */ 8733/** 8734 * 8735 * This file includes only framework internal classes and functions 8736 * non are part of SDK. Do not access from app. 8737 * 8738 * It includes @Monitor function decorator supporting classes MonitorV2 and AsyncMonitorV2 8739 * 8740 */ 8741class MonitorValueV2 { 8742 constructor(path, id) { 8743 this.path = path; 8744 this.id = id; 8745 this.props = path.split('.'); 8746 this.dirty = false; 8747 this.isPresent = false; 8748 } 8749 setValue(isInit, newValue) { 8750 this.now = newValue; 8751 if (isInit) { 8752 this.before = this.now; 8753 this.isPresent = true; 8754 return false; // not dirty at init 8755 } 8756 // Consider value dirty if it wasn't present before setting the new value 8757 this.dirty = !this.isPresent || this.before !== this.now; 8758 this.isPresent = true; 8759 return this.dirty; 8760 } 8761 setNotFound() { 8762 this.isPresent = false; 8763 this.before = undefined; 8764 } 8765 // mv newValue to oldValue, set dirty to false 8766 reset() { 8767 this.before = this.now; 8768 this.dirty = false; 8769 } 8770 isDirty() { 8771 return this.dirty; 8772 } 8773} 8774/** 8775 * MonitorV2 8776 * one MonitorV2 object per @Monitor function 8777 * watchId - similar to elmtId, identify one MonitorV2 in Observe.idToCmp Map 8778 * observeObjectAccess = get each object on the 'path' to create dependency and add them with Observe.addRef 8779 * fireChange - exec @Monitor function and re-new dependencies with observeObjectAccess 8780 */ 8781class MonitorV2 { 8782 constructor(target, pathsString, func) { 8783 var _a; 8784 var _b; 8785 this.values_ = new Array(); 8786 this.target_ = target; 8787 this.monitorFunction = func; 8788 this.watchId_ = ++MonitorV2.nextWatchId_; 8789 // split space separated array of paths 8790 let paths = pathsString.split(/\s+/g); 8791 paths.forEach(path => this.values_.push(new MonitorValueV2(path, ++MonitorV2.nextWatchId_))); 8792 // add watchId to owning ViewV2 or view model data object 8793 // ViewV2 uses to call clearBinding(id) 8794 // FIXME data object leave data inside ObservedV2, because they can not 8795 // call clearBinding(id) before they get deleted. 8796 const meta = (_a = target[_b = MonitorV2.WATCH_INSTANCE_PREFIX]) !== null && _a !== void 0 ? _a : (target[_b] = {}); 8797 meta[pathsString] = this.watchId_; 8798 } 8799 getTarget() { 8800 return this.target_; 8801 } 8802 /** 8803 Return array of those monitored paths 8804 that changed since previous invocation 8805 */ 8806 get dirty() { 8807 let ret = new Array(); 8808 this.values_.forEach(monitorValue => { 8809 if (monitorValue.isDirty()) { 8810 ret.push(monitorValue.path); 8811 } 8812 }); 8813 return ret; 8814 } 8815 /** 8816 * return IMonitorValue for given path 8817 * or if no path is specified any dirty (changed) monitor value 8818 */ 8819 value(path) { 8820 for (let monitorValue of this.values_) { 8821 if ((path === undefined && monitorValue.isDirty()) || monitorValue.path === path) { 8822 return monitorValue; 8823 } 8824 } 8825 return undefined; 8826 } 8827 InitRun() { 8828 // Register this Monitor to known components 8829 ObserveV2.getObserve().registerMonitor(this, this.watchId_); 8830 // Record dependencies for all the paths 8831 this.values_.forEach((item) => { 8832 ObserveV2.getObserve().startRecordDependencies(this, item.id); 8833 const [success, value] = this.analysisProp(true, item); 8834 ObserveV2.getObserve().stopRecordDependencies(); 8835 if (!success) { 8836 8837 return; 8838 } 8839 item.setValue(true, value); 8840 }); 8841 return this; 8842 } 8843 // Called by ObserveV2 when a monitor path has changed. 8844 // Analyze the changed path and return this Monitor's 8845 // watchId if the path was dirty and the monitor function 8846 // needs to be executed later. 8847 notifyChange(pathId) { 8848 let value = this.values_.find((item) => item.id === pathId); 8849 8850 return this.recordDependenciesForProp(value) ? this.watchId_ : -1; 8851 } 8852 // Called by ObserveV2 once if any monitored path was dirty. 8853 // Executes the monitor function. 8854 runMonitorFunction() { 8855 if (this.dirty.length === 0) { 8856 8857 return; 8858 } 8859 8860 try { 8861 // exec @Monitor function 8862 this.monitorFunction.call(this.target_, this); 8863 } 8864 catch (e) { 8865 stateMgmtConsole.applicationError(`@Monitor exception caught for ${this.monitorFunction.name}`, e.toString()); 8866 throw e; 8867 } 8868 finally { 8869 this.reset(); 8870 } 8871 } 8872 // called after @Monitor function call 8873 reset() { 8874 this.values_.forEach(item => item.reset()); 8875 } 8876 // record dependencies for given MonitorValue, when any monitored path 8877 // has changed and notifyChange is called 8878 recordDependenciesForProp(monitoredValue) { 8879 ObserveV2.getObserve().startRecordDependencies(this, monitoredValue.id); 8880 const [success, value] = this.analysisProp(false, monitoredValue); 8881 ObserveV2.getObserve().stopRecordDependencies(); 8882 if (!success) { 8883 8884 monitoredValue.setNotFound(); 8885 return false; 8886 } 8887 return monitoredValue.setValue(false, value); // dirty? 8888 } 8889 // record / update object dependencies by reading each object along the path 8890 // return the value, i.e. the value of the last path item 8891 analysisProp(isInit, monitoredValue) { 8892 let parentObj = this.target_; // main pointer 8893 let specialCur; // special pointer for Array 8894 let obj; // main property 8895 let lastProp; // last property name in path 8896 let specialProp; // property name for Array 8897 let props = monitoredValue.props; // get the props 8898 for (let i = 0; i < props.length; i++) { 8899 lastProp = props[i]; // get the current property name 8900 if (parentObj && typeof parentObj === 'object' && Reflect.has(parentObj, lastProp)) { 8901 obj = parentObj[lastProp]; // store property value, obj maybe Proxy added by V2 8902 if (Array.isArray(UIUtilsImpl.instance().getTarget(obj))) { 8903 // if obj is Array, store the infomation at the 'first' time. 8904 // if we reset the specialCur, that means we do not need to care Array. 8905 if (!specialCur) { 8906 // only for the 'first' time, store infomation. 8907 // this is for multi-dimension array, only the first Array need to be checked. 8908 specialCur = parentObj; 8909 specialProp = lastProp; 8910 } 8911 } 8912 else { 8913 if (specialCur && i === props.length - 1) { 8914 // if final target is the item of Array, return to use special info. 8915 break; 8916 } 8917 else { 8918 // otherwise turn back to normal property read... 8919 specialCur = undefined; 8920 } 8921 } 8922 if (i < props.length - 1) { 8923 // move the parentObj to its property, and go on 8924 parentObj = obj; 8925 } 8926 } 8927 else { 8928 isInit && stateMgmtConsole.warn(`@Monitor prop ${monitoredValue.path} initialize not found, make sure it exists!`); 8929 return [false, undefined]; 8930 } 8931 } 8932 if (specialCur) { 8933 // if case for Array, use special info.. 8934 lastProp = specialProp; 8935 parentObj = specialCur; 8936 } 8937 if (!ObserveV2.IsMakeObserved(obj) && !ObserveV2.IsTrackedProperty(parentObj, lastProp)) { 8938 stateMgmtConsole.applicationError(`@Monitor "${monitoredValue.path}" cannot be monitored, make sure it is decorated !!`); 8939 return [false, undefined]; 8940 } 8941 return [true, obj]; 8942 } 8943 static clearWatchesFromTarget(target) { 8944 var _a; 8945 let meta; 8946 if (!target || typeof target !== 'object' || 8947 !(meta = target[MonitorV2.WATCH_INSTANCE_PREFIX]) || typeof meta !== 'object') { 8948 return; 8949 } 8950 8951 Array.from(Object.values(meta)).forEach((watchId) => ObserveV2.getObserve().clearWatch(watchId)); 8952 } 8953} 8954MonitorV2.WATCH_PREFIX = '___watch_'; 8955MonitorV2.WATCH_INSTANCE_PREFIX = '___watch__obj_'; 8956// start with high number to avoid same id as elmtId for components. 8957MonitorV2.MIN_WATCH_ID = 0x1000000000000; 8958MonitorV2.nextWatchId_ = MonitorV2.MIN_WATCH_ID; 8959// Performance Improvement 8960class AsyncAddMonitorV2 { 8961 static addMonitor(target, name) { 8962 if (AsyncAddMonitorV2.watches.length === 0) { 8963 Promise.resolve(true) 8964 .then(AsyncAddMonitorV2.run) 8965 .catch(error => { 8966 stateMgmtConsole.applicationError(`Exception caught in @Monitor function ${name}`, error); 8967 _arkUIUncaughtPromiseError(error); 8968 }); 8969 } 8970 AsyncAddMonitorV2.watches.push([target, name]); 8971 } 8972 static run() { 8973 for (let item of AsyncAddMonitorV2.watches) { 8974 ObserveV2.getObserve().constructMonitor(item[0], item[1]); 8975 } 8976 AsyncAddMonitorV2.watches = []; 8977 } 8978} 8979AsyncAddMonitorV2.watches = []; 8980/* 8981 * Copyright (c) 2024 Huawei Device Co., Ltd. 8982 * Licensed under the Apache License, Version 2.0 (the "License"); 8983 * you may not use this file except in compliance with the License. 8984 * You may obtain a copy of the License at 8985 * 8986 * http://www.apache.org/licenses/LICENSE-2.0 8987 * 8988 * Unless required by applicable law or agreed to in writing, software 8989 * distributed under the License is distributed on an "AS IS" BASIS, 8990 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8991 * See the License for the specific language governing permissions and 8992 * limitations under the License. 8993 */ 8994/** 8995 * 8996 * This file includes only framework internal classes and functions 8997 * non are part of SDK. Do not access from app. 8998 * 8999 */ 9000/** 9001 * ComputedV2 9002 * one ComputedV2 object per @Computed variable 9003 * computedId_ - similar to elmtId, identify one ComputedV2 in Observe.idToCmp Map 9004 * observeObjectAccess = calculate the compute function and create dependencies to 9005 * source variables 9006 * fireChange - execute compute function and re-new dependencies with observeObjectAccess 9007 */ 9008class ComputedV2 { 9009 constructor(target, prop, func) { 9010 this.target_ = target; 9011 this.propertyComputeFunc_ = func; 9012 this.computedId_ = ++ComputedV2.nextCompId_; 9013 this.prop_ = prop; 9014 } 9015 InitRun() { 9016 let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + this.prop_; 9017 let propertyKey = this.prop_; 9018 Reflect.defineProperty(this.target_, propertyKey, { 9019 get() { 9020 ObserveV2.getObserve().addRef(this, propertyKey); 9021 return ObserveV2.autoProxyObject(this, cachedProp); 9022 }, 9023 set(_) { 9024 const error = `@Computed ${propertyKey} is readonly, cannot set value for it`; 9025 stateMgmtConsole.applicationError(error); 9026 throw new Error(error); 9027 }, 9028 enumerable: true 9029 }); 9030 this.target_[cachedProp] = this.observeObjectAccess(); 9031 return this.computedId_; 9032 } 9033 fireChange() { 9034 let newVal = this.observeObjectAccess(); 9035 let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + this.prop_; 9036 if (this.target_[cachedProp] !== newVal) { 9037 this.target_[cachedProp] = newVal; 9038 ObserveV2.getObserve().fireChange(this.target_, this.prop_); 9039 } 9040 } 9041 getTarget() { 9042 return this.target_; 9043 } 9044 getProp() { 9045 return this.prop_; 9046 } 9047 // register current watchId while executing compute function 9048 observeObjectAccess() { 9049 ObserveV2.getObserve().startRecordDependencies(this, this.computedId_); 9050 let ret; 9051 try { 9052 ret = this.propertyComputeFunc_.call(this.target_); 9053 } 9054 catch (e) { 9055 stateMgmtConsole.applicationError(`@Computed Exception caught for ${this.propertyComputeFunc_.name}`, e.toString()); 9056 ret = undefined; 9057 throw e; 9058 } 9059 finally { 9060 ObserveV2.getObserve().stopRecordDependencies(); 9061 } 9062 return ret; 9063 } 9064} 9065// start with high number to avoid same id as elmtId for components. 9066ComputedV2.MIN_COMPUTED_ID = 0x1000000000; 9067ComputedV2.nextCompId_ = ComputedV2.MIN_COMPUTED_ID; 9068ComputedV2.COMPUTED_PREFIX = '___comp_'; 9069ComputedV2.COMPUTED_CACHED_PREFIX = '___comp_cached_'; 9070class AsyncAddComputedV2 { 9071 static addComputed(target, name) { 9072 if (AsyncAddComputedV2.computedVars.length === 0) { 9073 Promise.resolve(true) 9074 .then(AsyncAddComputedV2.run) 9075 .catch(error => { 9076 stateMgmtConsole.applicationError(`Exception caught in @Computed ${name}`, error); 9077 _arkUIUncaughtPromiseError(error); 9078 }); 9079 } 9080 AsyncAddComputedV2.computedVars.push({ target: target, name: name }); 9081 } 9082 static run() { 9083 AsyncAddComputedV2.computedVars.forEach((computedVar) => ObserveV2.getObserve().constructComputed(computedVar.target, computedVar.name)); 9084 // according to stackoverflow this is the fastest way to clear an Array 9085 // ref https://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript 9086 AsyncAddComputedV2.computedVars.length = 0; 9087 } 9088} 9089AsyncAddComputedV2.computedVars = new Array(); 9090/* 9091 * Copyright (c) 2024 Huawei Device Co., Ltd. 9092 * Licensed under the Apache License, Version 2.0 (the "License"); 9093 * you may not use this file except in compliance with the License. 9094 * You may obtain a copy of the License at 9095 * 9096 * http://www.apache.org/licenses/LICENSE-2.0 9097 * 9098 * Unless required by applicable law or agreed to in writing, software 9099 * distributed under the License is distributed on an "AS IS" BASIS, 9100 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9101 * See the License for the specific language governing permissions and 9102 * limitations under the License. 9103 */ 9104/** 9105 * 9106 * This file includes only framework internal classes and functions 9107 * non are part of SDK. Do not access from app. 9108 * 9109 * Implementation of @ComponentV2 is ViewV2 9110 * When transpiling @ComponentV2, the transpiler generates a class that extends from ViewV2. 9111 * 9112 */ 9113class ViewV2 extends PUV2ViewBase { 9114 constructor(parent, elmtId = UINodeRegisterProxy.notRecordingDependencies, extraInfo = undefined) { 9115 super(parent, elmtId, extraInfo); 9116 // Set of elmtIds that need re-render 9117 this.dirtDescendantElementIds_ = new Set(); 9118 // Set of elements for delayed update 9119 this.elmtIdsDelayedUpdate = new Set(); 9120 this.monitorIdsDelayedUpdate = new Set(); 9121 this.computedIdsDelayedUpdate = new Set(); 9122 /** 9123 * on first render create a new Instance of Repeat 9124 * on re-render connect to existing instance 9125 * @param arr 9126 * @returns 9127 */ 9128 this.__mkRepeatAPI = (arr) => { 9129 // factory is for future extensions, currently always return the same 9130 const elmtId = ObserveV2.getCurrentRecordedId(); 9131 let repeat = this.elmtId2Repeat_.get(elmtId); 9132 if (!repeat) { 9133 repeat = new __Repeat(this, arr); 9134 this.elmtId2Repeat_.set(elmtId, repeat); 9135 } 9136 else { 9137 repeat.updateArr(arr); 9138 } 9139 return repeat; 9140 }; 9141 this.setIsV2(true); 9142 9143 } 9144 /** 9145 * The `freezeState` parameter determines whether this @ComponentV2 is allowed to freeze, when inactive 9146 * Its called with value of the `freezeWhenInactive` parameter from the @ComponentV2 decorator, 9147 * or it may be called with `undefined` depending on how the UI compiler works. 9148 * 9149 * @param freezeState Only the value `true` will be used to set the freeze state, 9150 * otherwise it inherits from its parent instance if its freezeState is true 9151 */ 9152 finalizeConstruction(freezeState) { 9153 ObserveV2.getObserve().constructComputed(this, this.constructor.name); 9154 ObserveV2.getObserve().constructMonitor(this, this.constructor.name); 9155 // Always use ID_REFS in ViewV2 9156 this[ObserveV2.ID_REFS] = {}; 9157 // set to true if freeze parameter set for this @ComponentV2 to true 9158 // otherwise inherit from its parentComponent (if it exists). 9159 this.isCompFreezeAllowed_ = freezeState || this.isCompFreezeAllowed_; 9160 9161 } 9162 debugInfo__() { 9163 return `@ComponentV2 '${this.constructor.name}'[${this.id__()}]`; 9164 } 9165 get isViewV2() { 9166 return true; 9167 } 9168 /** 9169 * Virtual function implemented in ViewPU and ViewV2 9170 * Unregisters and purges all child elements associated with the specified Element ID in ViewV2. 9171 * 9172 * @param rmElmtId - The Element ID to be purged and deleted 9173 * @returns {boolean} - Returns `true` if the Element ID was successfully deleted, `false` otherwise. 9174 */ 9175 purgeDeleteElmtId(rmElmtId) { 9176 9177 const result = this.updateFuncByElmtId.delete(rmElmtId); 9178 if (result) { 9179 const childOpt = this.getChildViewV2ForElmtId(rmElmtId); 9180 if (childOpt) { 9181 childOpt.setDeleting(); 9182 childOpt.setDeleteStatusRecursively(); 9183 } 9184 // it means rmElmtId has finished all the unregistration from the js side, ElementIdToOwningViewPU_ does not need to keep it 9185 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(rmElmtId); 9186 } 9187 // Needed only for V2 9188 ObserveV2.getObserve().clearBinding(rmElmtId); 9189 return result; 9190 } 9191 // super class will call this function from 9192 // its aboutToBeDeleted implementation 9193 aboutToBeDeletedInternal() { 9194 9195 // if this isDeleting_ is true already, it may be set delete status recursively by its parent, so it is not necessary 9196 // to set and resursively set its children any more 9197 if (!this.isDeleting_) { 9198 this.isDeleting_ = true; 9199 this.setDeleteStatusRecursively(); 9200 } 9201 // tell UINodeRegisterProxy that all elmtIds under 9202 // this ViewV2 should be treated as already unregistered 9203 9204 // purge the elmtIds owned by this ViewV2 from the updateFuncByElmtId and also the state variable dependent elmtIds 9205 Array.from(this.updateFuncByElmtId.keys()).forEach((elmtId) => { 9206 // FIXME split View: enable delete this purgeDeleteElmtId(elmtId); 9207 }); 9208 // unregistration of ElementIDs 9209 9210 // it will unregister removed elementids from all the ViewV2, equals purgeDeletedElmtIdsRecursively 9211 this.purgeDeletedElmtIds(); 9212 // unregisters its own id once its children are unregistered above 9213 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs([this.id__()]); 9214 9215 /* in case ViewPU is currently frozen 9216 ViewPU inactiveComponents_ delete(`${this.constructor.name}[${this.id__()}]`); 9217 */ 9218 MonitorV2.clearWatchesFromTarget(this); 9219 this.updateFuncByElmtId.clear(); 9220 if (this.parent_) { 9221 this.parent_.removeChild(this); 9222 } 9223 } 9224 initialRenderView() { 9225 9226 this.initialRender(); 9227 9228 } 9229 observeComponentCreation2(compilerAssignedUpdateFunc, classObject) { 9230 if (this.isDeleting_) { 9231 stateMgmtConsole.error(`@ComponentV2 ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation2 `); 9232 return; 9233 } 9234 const _componentName = (classObject && ('name' in classObject)) ? Reflect.get(classObject, 'name') : 'unspecified UINode'; 9235 const _popFunc = (classObject && 'pop' in classObject) ? classObject.pop : () => { }; 9236 const updateFunc = (elmtId, isFirstRender) => { 9237 this.syncInstanceId(); 9238 9239 ViewStackProcessor.StartGetAccessRecordingFor(elmtId); 9240 ObserveV2.getObserve().startRecordDependencies(this, elmtId); 9241 compilerAssignedUpdateFunc(elmtId, isFirstRender); 9242 if (!isFirstRender) { 9243 _popFunc(); 9244 } 9245 let node = this.getNodeById(elmtId); 9246 if (node !== undefined) { 9247 node.cleanStageValue(); 9248 } 9249 ObserveV2.getObserve().stopRecordDependencies(); 9250 ViewStackProcessor.StopGetAccessRecording(); 9251 9252 this.restoreInstanceId(); 9253 }; 9254 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 9255 // needs to move set before updateFunc. 9256 // make sure the key and object value exist since it will add node in attributeModifier during updateFunc. 9257 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject }); 9258 // add element id -> owning ViewV2 9259 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 9260 try { 9261 updateFunc(elmtId, /* is first render */ true); 9262 } 9263 catch (error) { 9264 // avoid the incompatible change that move set function before updateFunc. 9265 this.updateFuncByElmtId.delete(elmtId); 9266 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 9267 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 9268 throw error; 9269 } 9270 9271 } 9272 /** 9273 * 9274 * @param paramVariableName 9275 * @param @once paramVariableName 9276 * @param is read only, therefore, init from parent needs to be done without 9277 * causing property setter() to be called 9278 * @param newValue 9279 */ 9280 initParam(paramVariableName, newValue) { 9281 this.checkIsV1Proxy(paramVariableName, newValue); 9282 VariableUtilV2.initParam(this, paramVariableName, newValue); 9283 } 9284 /** 9285 * 9286 * @param paramVariableName 9287 * @param @once paramVariableName 9288 * @param is read only, therefore, update from parent needs to be done without 9289 * causing property setter() to be called 9290 * @param @once reject any update 9291 * @param newValue 9292 */ 9293 updateParam(paramVariableName, newValue) { 9294 this.checkIsV1Proxy(paramVariableName, newValue); 9295 VariableUtilV2.updateParam(this, paramVariableName, newValue); 9296 } 9297 checkIsV1Proxy(paramVariableName, value) { 9298 if (ObservedObject.IsObservedObject(value)) { 9299 throw new Error(`Cannot assign the ComponentV1 value to the ComponentV2 for the property '${paramVariableName}'`); 9300 } 9301 } 9302 /** 9303 * inform that UINode with given elmtId needs rerender 9304 * does NOT exec @Watch function. 9305 * only used on V2 code path from ObserveV2.fireChange. 9306 * 9307 * FIXME will still use in the future? 9308 */ 9309 uiNodeNeedUpdateV2(elmtId) { 9310 if (this.isFirstRender()) { 9311 return; 9312 } 9313 9314 if (!this.isActive_) { 9315 this.scheduleDelayedUpdate(elmtId); 9316 return; 9317 } 9318 if (!this.dirtDescendantElementIds_.size) { // && !this runReuse_) { 9319 // mark ComposedElement dirty when first elmtIds are added 9320 // do not need to do this every time 9321 this.syncInstanceId(); 9322 this.markNeedUpdate(); 9323 this.restoreInstanceId(); 9324 } 9325 this.dirtDescendantElementIds_.add(elmtId); 9326 9327 9328 } 9329 /** 9330 * For each recorded dirty Element in this custom component 9331 * run its update function 9332 * 9333 */ 9334 updateDirtyElements() { 9335 9336 do { 9337 9338 // see which elmtIds are managed by this View 9339 // and clean up all book keeping for them 9340 this.purgeDeletedElmtIds(); 9341 // process all elmtIds marked as needing update in ascending order. 9342 // ascending order ensures parent nodes will be updated before their children 9343 // prior cleanup ensure no already deleted Elements have their update func executed 9344 const dirtElmtIdsFromRootNode = Array.from(this.dirtDescendantElementIds_).sort(ViewV2.compareNumber); 9345 // if state changed during exec update lambda inside UpdateElement, then the dirty elmtIds will be added 9346 // to newly created this.dirtDescendantElementIds_ Set 9347 dirtElmtIdsFromRootNode.forEach(elmtId => { 9348 this.UpdateElement(elmtId); 9349 this.dirtDescendantElementIds_.delete(elmtId); 9350 }); 9351 if (this.dirtDescendantElementIds_.size) { 9352 stateMgmtConsole.applicationError(`${this.debugInfo__()}: New UINode objects added to update queue while re-render! - Likely caused by @Component state change during build phase, not allowed. Application error!`); 9353 } 9354 } while (this.dirtDescendantElementIds_.size); 9355 9356 9357 } 9358 UpdateElement(elmtId) { 9359 if (this.isDeleting_) { 9360 9361 return; 9362 } 9363 9364 if (elmtId === this.id__()) { 9365 // do not attempt to update itself 9366 9367 return; 9368 } 9369 // do not process an Element that has been marked to be deleted 9370 const entry = this.updateFuncByElmtId.get(elmtId); 9371 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 9372 if (typeof updateFunc !== 'function') { 9373 9374 } 9375 else { 9376 const componentName = entry.getComponentName(); 9377 9378 9379 try { 9380 updateFunc(elmtId, /* isFirstRender */ false); 9381 } 9382 catch (e) { 9383 stateMgmtConsole.applicationError(`Exception caught in update function of ${componentName} for elmtId ${elmtId}`, e.toString()); 9384 throw e; 9385 } 9386 finally { 9387 9388 } 9389 9390 this.finishUpdateFunc(elmtId); 9391 9392 9393 } 9394 9395 } 9396 /** 9397 * Retrieve child by given id 9398 * @param id 9399 * @returns child if child with this id exists and it is instance of ViewV2 9400 */ 9401 getViewV2ChildById(id) { 9402 const childWeakRef = this.childrenWeakrefMap_.get(id); 9403 const child = childWeakRef ? childWeakRef.deref() : undefined; 9404 return (child && child instanceof ViewV2) ? child : undefined; 9405 } 9406 /** 9407 * findViewPUInHierarchy function needed for @Component and @ComponentV2 mixed 9408 * parent - child hierarchies. Not used by ViewV2 9409 */ 9410 findViewPUInHierarchy(id) { 9411 // this ViewV2 is not a ViewPU, continue searching amongst children 9412 let retVal = undefined; 9413 for (const [key, value] of this.childrenWeakrefMap_.entries()) { 9414 retVal = value.deref().findViewPUInHierarchy(id); 9415 if (retVal) { 9416 break; 9417 } 9418 } 9419 return retVal; 9420 } 9421 /* Adds the elmtId to elmtIdsDelayedUpdate for delayed update 9422 once the view gets active 9423 */ 9424 scheduleDelayedUpdate(elmtId) { 9425 this.elmtIdsDelayedUpdate.add(elmtId); 9426 } 9427 // WatchIds that needs to be fired later gets added to monitorIdsDelayedUpdate 9428 // monitor fireChange will be triggered for all these watchIds once this view gets active 9429 addDelayedMonitorIds(watchId) { 9430 9431 this.monitorIdsDelayedUpdate.add(watchId); 9432 } 9433 addDelayedComputedIds(watchId) { 9434 9435 this.computedIdsDelayedUpdate.add(watchId); 9436 } 9437 setActiveInternal(newState) { 9438 9439 if (this.isCompFreezeAllowed()) { 9440 9441 this.isActive_ = newState; 9442 if (this.isActive_) { 9443 this.performDelayedUpdate(); 9444 } 9445 } 9446 for (const child of this.childrenWeakrefMap_.values()) { 9447 const childView = child.deref(); 9448 if (childView) { 9449 childView.setActiveInternal(newState); 9450 } 9451 } 9452 9453 } 9454 performDelayedUpdate() { 9455 9456 if (this.computedIdsDelayedUpdate.size) { 9457 // exec computed functions 9458 ObserveV2.getObserve().updateDirtyComputedProps([...this.computedIdsDelayedUpdate]); 9459 } 9460 if (this.monitorIdsDelayedUpdate.size) { 9461 // exec monitor functions 9462 ObserveV2.getObserve().runDirtyMonitors(this.monitorIdsDelayedUpdate); 9463 } 9464 if (this.elmtIdsDelayedUpdate.size) { 9465 // update re-render of updated element ids once the view gets active 9466 if (this.dirtDescendantElementIds_.size === 0) { 9467 this.dirtDescendantElementIds_ = new Set(this.elmtIdsDelayedUpdate); 9468 } 9469 else { 9470 this.elmtIdsDelayedUpdate.forEach((element) => { 9471 this.dirtDescendantElementIds_.add(element); 9472 }); 9473 } 9474 } 9475 this.markNeedUpdate(); 9476 this.elmtIdsDelayedUpdate.clear(); 9477 this.monitorIdsDelayedUpdate.clear(); 9478 this.computedIdsDelayedUpdate.clear(); 9479 9480 } 9481 /* 9482 findProvidePU finds @Provided property recursively by traversing ViewPU's towards that of the UI tree root @Component: 9483 if 'this' ViewPU has a @Provide('providedPropName') return it, otherwise ask from its parent ViewPU. 9484 function needed for mixed @Component and @ComponentV2 parent child hierarchies. 9485 */ 9486 findProvidePU(providedPropName) { 9487 var _a; 9488 return (_a = this.getParent()) === null || _a === void 0 ? void 0 : _a.findProvidePU(providedPropName); 9489 } 9490 get localStorage_() { 9491 // FIXME check this also works for root @ComponentV2 9492 return (this.getParent()) ? this.getParent().localStorage_ : new LocalStorage({ /* empty */}); 9493 } 9494 /** 9495 * @function observeRecycleComponentCreation 9496 * @description custom node recycle creation not supported for V2. So a dummy function is implemented to report 9497 * an error message 9498 * @param name custom node name 9499 * @param recycleUpdateFunc custom node recycle update which can be converted to a normal update function 9500 * @return void 9501 */ 9502 observeRecycleComponentCreation(name, recycleUpdateFunc) { 9503 stateMgmtConsole.error(`${this.debugInfo__()}: Recycle not supported for ComponentV2 instances`); 9504 } 9505 debugInfoDirtDescendantElementIdsInternal(depth = 0, recursive = false, counter) { 9506 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 9507 retVaL += `ViewV2 keeps no info about dirty elmtIds`; 9508 if (recursive) { 9509 this.childrenWeakrefMap_.forEach((value, key, map) => { 9510 var _a; 9511 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoDirtDescendantElementIdsInternal(depth + 1, recursive, counter); 9512 }); 9513 } 9514 if (recursive && depth === 0) { 9515 retVaL += `\nTotal: ${counter.total}`; 9516 } 9517 return retVaL; 9518 } 9519 debugInfoStateVars() { 9520 return ''; // TODO DFX, read out META 9521 } 9522} 9523/* 9524 * Copyright (c) 2024 Huawei Device Co., Ltd. 9525 * Licensed under the Apache License, Version 2.0 (the "License"); 9526 * you may not use this file except in compliance with the License. 9527 * You may obtain a copy of the License at 9528 * 9529 * http://www.apache.org/licenses/LICENSE-2.0 9530 * 9531 * Unless required by applicable law or agreed to in writing, software 9532 * distributed under the License is distributed on an "AS IS" BASIS, 9533 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9534 * See the License for the specific language governing permissions and 9535 * limitations under the License. 9536 */ 9537function ObservedV2(BaseClass) { 9538 ConfigureStateMgmt.instance.usingV2ObservedTrack(`@ObservedV2`, BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name); 9539 return observedV2Internal(BaseClass); 9540} 9541/** 9542 * @Trace class property decorator, property inside @ObservedV2 class 9543 * 9544 * turns given property into getter and setter functions 9545 * adds property target[storeProp] as the backing store 9546 * 9547 * part of SDK 9548 * @from 12 9549 */ 9550const Trace = (target, propertyKey) => { 9551 ConfigureStateMgmt.instance.usingV2ObservedTrack(`@Trace`, propertyKey); 9552 return trackInternal(target, propertyKey); 9553}; 9554/** 9555 * @Local @ComponentV2/ViewV2 variable decorator 9556 * 9557 * allowed value: simple or object type value allowed. Objects must be instances of 9558 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9559 * local init required 9560 * no init or update from parent @ComponentV2 9561 * new value assignment allowed = has setter 9562 * 9563 * part of SDK 9564 * @from 12 9565 * 9566 */ 9567const Local = (target, propertyKey) => { 9568 ObserveV2.addVariableDecoMeta(target, propertyKey, '@Local'); 9569 return trackInternal(target, propertyKey); 9570}; 9571/** 9572 * @Param class property decorator 9573 * 9574 * allowed value: simple or object type value allowed. Objects must be instances of 9575 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9576 * local init optional 9577 * init from parent @ComponentV2 is mandatory when no local init, otherwise optional 9578 * updates from parent @ComponentV2 if initialized from parent @ComponentV2, 9579 * no update when @Once is used. 9580 * new value assignment not allowed = has no setter. 9581 * 9582 * part of SDK 9583 * @from 12 9584 * 9585 */ 9586const Param = (proto, propertyKey) => { 9587 9588 ObserveV2.addParamVariableDecoMeta(proto, propertyKey, '@param', undefined); 9589 let storeProp = ObserveV2.OB_PREFIX + propertyKey; 9590 proto[storeProp] = proto[propertyKey]; 9591 Reflect.defineProperty(proto, propertyKey, { 9592 get() { 9593 ObserveV2.getObserve().addRef(this, propertyKey); 9594 return ObserveV2.autoProxyObject(this, ObserveV2.OB_PREFIX + propertyKey); 9595 }, 9596 set(val) { 9597 var _a; 9598 const meta = (_a = proto[ObserveV2.V2_DECO_META]) === null || _a === void 0 ? void 0 : _a[propertyKey]; 9599 if (meta && meta.deco2 !== '@once') { 9600 stateMgmtConsole.applicationError(`@param ${propertyKey.toString()}: can not assign a new value, application error.`); 9601 return; 9602 } 9603 if (val !== this[storeProp]) { 9604 this[storeProp] = val; 9605 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 9606 ObserveV2.getObserve().fireChange(this, propertyKey); 9607 } 9608 } 9609 }, 9610 // @param can not be assigned, no setter 9611 enumerable: true 9612 }); 9613}; // Param 9614/** 9615 * @Once supplementary @ComponentV2 variable decorator to @Param decorator 9616 * must use like this @Param @Once varName. Can not be used without @param. 9617 * prevents @Param variable updates from parent component 9618 * 9619 * @param proto 9620 * @param propertyKey 9621 * 9622 * part of SDK 9623 * @from 12 9624 * 9625 */ 9626const Once = (proto, propertyKey) => { 9627 9628 ObserveV2.addParamVariableDecoMeta(proto, propertyKey, undefined, '@once'); 9629}; 9630/** 9631 * @Event class variable decorator, class must be @ComponentV2 9632 * 9633 * Allowed value: Function, can have parameters and return a value. 9634 * local init: optional for functions without return value, default is () => void 9635 * Local init is mandatory for functions with return value. 9636 * init from parent @Component: optional. 9637 * update from parent @Component: never 9638 * new value assignment not allowed 9639 * 9640 * part of SDK 9641 * @from 12 9642 * 9643 */ 9644const Event = (target, propertyKey) => { 9645 var _a; 9646 ObserveV2.addVariableDecoMeta(target, propertyKey, '@event'); 9647 (_a = target[propertyKey]) !== null && _a !== void 0 ? _a : (target[propertyKey] = () => { }); 9648}; 9649/** 9650 * @Provider variable decorator of @ComponentV2 variable 9651 * 9652 * @Provider(alias? : string) varName : typeName = initValue 9653 * 9654 * @param alias defaults to varName 9655 * 9656 * allowed value: simple or object type value allowed. Objects must be instances of 9657 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9658 * local init required 9659 * no init or update from parent @ComponentV2 9660 * provides its value to any @Consumer counter part 9661 * new value assignment allowed = has setter 9662 * 9663 * part of SDK 9664 * @since 12 9665 */ 9666const Provider = (aliasName) => { 9667 return (proto, varName) => { 9668 const providedUnderName = aliasName || varName; 9669 ProviderConsumerUtilV2.addProvideConsumeVariableDecoMeta(proto, varName, providedUnderName, '@Provider'); 9670 trackInternal(proto, varName); 9671 }; 9672}; // @Provider 9673/** 9674 * @Consumer variable decorator of @ComponentV2 variable 9675 * 9676 * @Consumer(alias? : string) varName : typeName = defaultValue 9677* 9678 * @param alias defaults to varName 9679 * 9680 * allowed value: simple or object type value allowed. Objects must be instances of 9681 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9682 * syncs two-way with the @Provider variable with same `alias` name in nearest ancestor @ComponentV2 9683 * local init required, used only if no @Provider counter part is found. 9684 * no init or update from parent @ComponentV2 via constructor allowed 9685 * new value assignment allowed, changes sys back to @Provider of one exists, otherwise update local value. 9686 * 9687 * part of SDK 9688 * @since 12 9689 */ 9690const Consumer = (aliasName) => { 9691 return (proto, varName) => { 9692 const searchForProvideWithName = aliasName || varName; 9693 // redefining the property happens when owning ViewV2 gets constructed 9694 // and @Consumer gets connected to @provide counterpart 9695 ProviderConsumerUtilV2.addProvideConsumeVariableDecoMeta(proto, varName, searchForProvideWithName, '@Consumer'); 9696 const providerName = (aliasName === undefined || aliasName === null || 9697 (typeof aliasName === 'string' && aliasName.trim() === '')) ? varName : aliasName; 9698 const storeProp = ObserveV2.CONSUMER_PREFIX + varName; 9699 proto[storeProp] = providerName; 9700 Reflect.defineProperty(proto, varName, { 9701 get() { 9702 // this get function should never be called, 9703 // because transpiler will always assign it a value first. 9704 stateMgmtConsole.warn('@Consumer outer "get" should never be called, internal error!'); 9705 return undefined; 9706 }, 9707 set(val) { 9708 let providerInfo = ProviderConsumerUtilV2.findProvider(this, providerName); 9709 if (providerInfo && providerInfo[0] && providerInfo[1]) { 9710 ProviderConsumerUtilV2.connectConsumer2Provider(this, varName, providerInfo[0], providerInfo[1]); 9711 } 9712 else { 9713 ProviderConsumerUtilV2.defineConsumerWithoutProvider(this, varName, val); 9714 } 9715 }, 9716 enumerable: true 9717 }); 9718 }; 9719}; // @Consumer 9720/** 9721 * @Monitor class function decorator, inside either @ComponentV2 or @ObservedV2 class 9722 * 9723 * @Monitor(path: string, paths: string[]) functionName (m : IMonitor) : void 9724 * 9725 * @param path : string , path of monitored object properties (strictly objects, no arrays, maps etc) 9726 * property names separated by '.'. 9727 * @param paths : string[] , further, optional paths to monitor 9728 * 9729 * 9730 * The decorated function must have one parameter of type IMonitor and no return value. 9731 * 9732 * Example: @Monitor('varName.obj', 'varName.obj.proA', 'varName2') onChange(m : IMonitor) : void { ... } 9733 * monitors assignments to this.varName.obj, this.varName.obj.propA, and this.varName2 . 9734 * 9735 * part of SDK 9736 * @since 12 9737 */ 9738const Monitor = function (key, ...keys) { 9739 const pathsUniqueString = keys ? [key, ...keys].join(' ') : key; 9740 return function (target, _, descriptor) { 9741 9742 let watchProp = Symbol.for(MonitorV2.WATCH_PREFIX + target.constructor.name); 9743 const monitorFunc = descriptor.value; 9744 target[watchProp] ? target[watchProp][pathsUniqueString] = monitorFunc : target[watchProp] = { [pathsUniqueString]: monitorFunc }; 9745 }; 9746}; 9747/** 9748 * @Computed TS computed class member variable decorator, inside either @ComponentV2 or @ObservedV2 class 9749 * 9750 * must be a computed class property following TS syntax, e.g. @Computed get varName() { return this.state1 + this.state2 } 9751 * value assignment / set not allowed = has no setter. 9752 * The framework updates the value of the @Computed variable whenever its input changes 9753 * Therefore, the getter function must only use variables whose changes can be observed. 9754 * The getter function implementation must not mutate any state. 9755 * Changes of the return value of the getter function must be observable to use for constructing UI. 9756 * This means if the return value is an object, it must be @ObservedV2 class instance with @Trace 'ed properties, 9757 * or of Array, Map, Set, or Date type. 9758 * The app should not modify the return value because re-execution of the getter function would overwrite these changes. 9759 * 9760 * part of SDK 9761 * @from 12 9762 * 9763 */ 9764const Computed = (target, propertyKey, descriptor) => { 9765 9766 let watchProp = Symbol.for(ComputedV2.COMPUTED_PREFIX + target.constructor.name); 9767 const computeFunction = descriptor.get; 9768 target[watchProp] ? target[watchProp][propertyKey] = computeFunction 9769 : target[watchProp] = { [propertyKey]: computeFunction }; 9770}; 9771/* 9772 * Copyright (c) 2024 Huawei Device Co., Ltd. 9773 * Licensed under the Apache License, Version 2.0 (the "License"); 9774 * you may not use this file except in compliance with the License. 9775 * You may obtain a copy of the License at 9776 * 9777 * http://www.apache.org/licenses/LICENSE-2.0 9778 * 9779 * Unless required by applicable law or agreed to in writing, software 9780 * distributed under the License is distributed on an "AS IS" BASIS, 9781 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9782 * See the License for the specific language governing permissions and 9783 * limitations under the License. 9784 */ 9785const V2_STATE_PREFIX = '__ob_'; 9786const V2_PREFIX_LENGTH = V2_STATE_PREFIX.length; 9787; 9788class Meta { 9789 static define(proto, prop, value) { 9790 const meta = Meta.proto2props.get(proto); 9791 if (!meta) { 9792 Meta.proto2props.set(proto, { [prop]: value }); 9793 } 9794 else { 9795 meta[prop] = value; 9796 } 9797 } 9798 static get(obj, prop) { 9799 let proto = obj.__proto__; 9800 while (proto) { 9801 let meta = Meta.proto2props.get(proto); 9802 if (meta && meta[prop]) { 9803 return meta[prop]; 9804 } 9805 proto = proto.__proto__; 9806 } 9807 return undefined; 9808 } 9809 static gets(obj) { 9810 const ret = {}; 9811 let proto = obj.__proto__; 9812 while (proto) { 9813 let meta = Meta.proto2props.get(proto); 9814 Object.assign(ret, meta); 9815 proto = proto.__proto__; 9816 } 9817 return ret; 9818 } 9819 static getOwn(obj, prop) { 9820 const meta = Meta.proto2props.get(obj.__proto__); 9821 return meta && meta[prop]; 9822 } 9823} 9824Meta.proto2props = new WeakMap(); 9825function __Type__(type, alias) { 9826 const options = JSONCoder.getOptions(type); 9827 if (alias) { 9828 options.alias = alias; 9829 } 9830 return (target, prop) => { 9831 const tar = typeof target === 'function' ? target.prototype : target; 9832 Meta.define(tar, prop, options); 9833 }; 9834} 9835function ObservedReplacer(replacer) { 9836 const defaultReplacer = function (key, value) { 9837 return value; 9838 }; 9839 const realReplacer = replacer || defaultReplacer; 9840 return function (key, value) { 9841 if (typeof value !== 'object' || Array.isArray(value)) { 9842 return realReplacer.call(this, key, value); 9843 } 9844 if (value instanceof Set) { 9845 return realReplacer.call(this, key, Array.from(value)); 9846 } 9847 if (value instanceof Map) { 9848 return realReplacer.call(this, key, Array.from(value.entries())); 9849 } 9850 const ret = {}; 9851 const meta = Meta.gets(value); 9852 Object.keys(value).forEach(key => { 9853 let saveKey = key.startsWith(V2_STATE_PREFIX) ? key.substring(V2_PREFIX_LENGTH) : key; 9854 let options = meta && meta[saveKey]; 9855 if (options && options.disabled) { 9856 return; 9857 } 9858 ret[(options && options.alias) || saveKey] = value[saveKey]; 9859 }); 9860 return realReplacer.call(this, key, ret); 9861 }; 9862} 9863/** 9864 * JSONCoder 9865 * 9866 * The JSONCoder utility enhances the serialization and deserialization capabilities beyond 9867 * the standard JSON.stringify and JSON.parse methods. While JSON.stringify serializes 9868 * object properties and their values, it drops functions, class, property, and function decorators, 9869 * and does not support Map, Set, or Date serialization. JSONCoder addresses these limitations, 9870 * providing robust support for a wider range of JavaScript features. 9871 * 9872 * Main Features: 9873 * - Adds support for serializing and deserializing class instances, including methods and decorators. 9874 * - Supports serialization of complex data structures like Map, Set, and Date. 9875 * - Provides full reconstruction of class instances through the JSONCoder.parseTo method. 9876 * 9877 * Usage Scenarios: 9878 * - Serializing class instances to JSON for network transmission or storage. 9879 * - Deserializing JSON data back into fully functional class instances, preserving methods and decorators. 9880 * - Converting JSON data received from network or database into state management observable view models (e.g., @ObservedV2 class objects). 9881 * 9882 * The name 'JSONCoder' is derived from the 'JSON stringify/parse', reflecting its purpose to enhance JSON serialization and deserialization for classes. 9883 * 9884 */ 9885class JSONCoder { 9886 /** 9887 * Serializes the given object into a string. This string includes additional meta info 9888 * allowing `stringify` to fully reconstruct the original object, including its class 9889 * type and properties. 9890 * 9891 * @template T - The type of the object being serialized. 9892 * @param { T } value - The object to serialize. 9893 * @param { (this: JSONAny, key: string, value: JSONAny) => JSONAny } [replacer] - A function that alters the behavior when stringify 9894 * @param { string | number } [space] - For format 9895 * @returns { string } The serialized string representation of the object. 9896 */ 9897 static stringify(value, replacer, space) { 9898 return JSON.stringify(value, ObservedReplacer(replacer), space); 9899 } 9900 /** 9901 * Parses a JSON string or object and applies the nested key-values to a class object. 9902 * The main usage scenario is to convert JSON data received from a network or database 9903 * to a state management observable view model. 9904 * 9905 * @template T - The type of the object being parsed. 9906 * @param { TypeConstructor<T> | TransformOptions<T> } type - The class prototype or constructor function that has no parameters. 9907 * @param { object | string } source - The JSON string or JSON object. 9908 * @returns { T | T[] } The parsed object of type T or T[]. 9909 */ 9910 static parse(type, source) { 9911 const json = typeof source === 'string' ? JSON.parse(source) : source; 9912 const options = JSONCoder.getOptions(type); 9913 return Array.isArray(json) ? 9914 JSONCoder.parseIntoArray([], json, options) : 9915 JSONCoder.parseInto(JSONCoder.newItem(json, options), json); 9916 } 9917 /** 9918 * Deserializes a string produced by `parseTo` back into the original object, 9919 * fully reconstructing its class type and properties. 9920 * 9921 * @template T - The original object being parsed. 9922 * @param { T | T[] } type - The original object. 9923 * @param { object | string } source - The JSON string or JSON object. 9924 * @param { TypeConstructor<T> | TransformOptions<T> } [type] - The class prototype or constructor function that has no parameters. 9925 * @returns { T | T[] } The parsed object of type T or T[]. 9926 */ 9927 static parseTo(target, source, type) { 9928 const json = typeof source === 'string' ? JSON.parse(source) : source; 9929 const t1 = Array.isArray(json); 9930 const t2 = Array.isArray(target); 9931 const options = JSONCoder.getOptions(type); 9932 if (t1 && t2) { 9933 JSONCoder.parseIntoArray(target, json, options); 9934 } 9935 else if (!t1 && !t2) { 9936 JSONCoder.parseInto(target, json); 9937 } 9938 else { 9939 throw new Error(`The type of target '${t2}' mismatches the type of source '${t1}'`); 9940 } 9941 return target; 9942 } 9943 /** 9944 * Get the type options from the object creator. 9945 * 9946 * @template T - The object being parsed. 9947 * @param { TypeConstructor<T> | TransformOptions<T> | string } [type] - The type info of the object creator. 9948 * @returns { TransformOptions<T> } The options of the type info. 9949 */ 9950 static getOptions(type) { 9951 const paramType = typeof type; 9952 const options = {}; 9953 if (paramType === 'object') { 9954 Object.assign(options, type); 9955 } 9956 else if (paramType === 'function') { 9957 options.factory = (_) => type; 9958 } 9959 else if (paramType === 'string') { 9960 options.alias = type; 9961 } 9962 return options; 9963 } 9964 static getAlias2Prop(meta, target) { 9965 const ret = new Map(); 9966 Object.keys(meta).forEach(prop => { 9967 const options = meta[prop]; 9968 ret.set(options.alias || prop, prop); 9969 }); 9970 return ret; 9971 } 9972 static parseInto(target, source) { 9973 if (typeof source !== 'object') { 9974 throw new Error(`The type of target '${typeof target}' mismatches the type of source '${typeof source}'`); 9975 } 9976 const meta = Meta.gets(target); 9977 const alias2prop = JSONCoder.getAlias2Prop(meta, target); 9978 Object.keys(source).forEach((key) => { 9979 const prop = alias2prop.get(key) || key; 9980 const options = meta && meta[prop]; 9981 if (options && options.disabled) { 9982 return; 9983 } 9984 JSONCoder.parseItemInto(target, prop, source, options); 9985 }); 9986 return target; 9987 } 9988 static parseItemInto(target, targetKey, source, options) { 9989 if (source === null || source === undefined) { 9990 return; 9991 } 9992 let tarType = typeof target[targetKey]; 9993 if (tarType === 'function') { 9994 return; 9995 } 9996 const sourceKey = (options === null || options === void 0 ? void 0 : options.alias) || targetKey; 9997 // Handling invalid values 9998 const value = JSONCoder.getTargetValue(source[sourceKey], options); 9999 if (value === undefined || value === null) { 10000 if (tarType === 'object') { 10001 if (target[targetKey] instanceof Map || target[targetKey] instanceof Set) { 10002 target[targetKey].clear(); 10003 } 10004 else if (Array.isArray(target[targetKey])) { 10005 target[targetKey].splice(0, target[targetKey].length); 10006 } 10007 else if (options && options.factory) { 10008 // if options.factory exists, can be assigned to undefined or null 10009 target[targetKey] = value; 10010 } 10011 } 10012 // other scene ignore all 10013 return; 10014 } 10015 // value is array, it maybe array or map or set 10016 if (Array.isArray(value)) { 10017 target[targetKey] = JSONCoder.parseIntoArray(target[targetKey], value, options); 10018 return; 10019 } 10020 // if target[targetKey] invalid, then attempt create 10021 if (target[targetKey] === null || target[targetKey] === undefined) { 10022 target[targetKey] = JSONCoder.newItem(value, options); 10023 tarType = typeof target[targetKey]; 10024 } 10025 if (typeof value !== 'object') { 10026 // value is Primitive Type 10027 if (target[targetKey] instanceof Date) { 10028 target[targetKey] = new Date(value); 10029 } 10030 else if (tarType === 'string') { 10031 target[targetKey] = value.toString(); 10032 } 10033 else if (tarType === typeof value) { 10034 target[targetKey] = value; 10035 } 10036 else if (target[targetKey] !== undefined) { 10037 throw new Error(`The type of target '${tarType}' mismatches the type of source '${typeof value}'`); 10038 } 10039 return; 10040 } 10041 // value is object, target[targetKey] is undefined or null 10042 if (target[targetKey] === null) { 10043 throw new Error(`Miss @Type in object defined, the property name is ${targetKey}`); 10044 } 10045 else if (target[targetKey] === undefined) { 10046 // ignore target[targetKey] undefined 10047 return; 10048 } 10049 this.parseInto(target[targetKey], value); 10050 } 10051 static newItem(json, options) { 10052 const type = options === null || options === void 0 ? void 0 : options.factory(json); 10053 return type && new type(); 10054 } 10055 static getTargetValue(value, options) { 10056 // future can convert the value to different type or value 10057 return value; 10058 } 10059 static parseIntoArray(target, source, options) { 10060 if (typeof target !== 'object') { 10061 throw new Error(`The type of target '${typeof target}' mismatches the type of source '${typeof source}'`); 10062 } 10063 // here, source maybe a array or map or set 10064 if (target instanceof Map) { 10065 target.clear(); 10066 for (let i = 0; i < source.length; ++i) { 10067 // If target is a map, item must be an array. Otherwise, ignore it 10068 const item = source[i]; 10069 if (!Array.isArray(item) || item.length < 2 || typeof item[0] !== 'string') { 10070 continue; 10071 } 10072 target.set(item[0], typeof item[1] !== 'object' ? item[1] : JSONCoder.parse(options, item[1])); 10073 } 10074 return target; 10075 } 10076 if (target instanceof Set) { 10077 target.clear(); 10078 for (let i = 0; i < source.length; ++i) { 10079 const item = source[i]; 10080 target.add(typeof item !== 'object' ? item : JSONCoder.parse(options, item)); 10081 } 10082 return target; 10083 } 10084 target.length = source.length; 10085 for (let i = 0; i < source.length; ++i) { 10086 const item = source[i]; 10087 if (typeof item !== 'object') { 10088 target[i] = item; 10089 continue; 10090 } 10091 if (i === 0) { 10092 if (!(options === null || options === void 0 ? void 0 : options.factory)) { 10093 target.length = 0; 10094 throw new Error(`Miss @Type in array defined`); 10095 } 10096 } 10097 target[i] = Array.isArray(item) ? 10098 JSONCoder.parseIntoArray(target[i] || [], item, options) : 10099 JSONCoder.parseInto(target[i] || JSONCoder.newItem(item, options), item); 10100 } 10101 return target; 10102 } 10103} 10104/* 10105 * Copyright (c) 2024 Huawei Device Co., Ltd. 10106 * Licensed under the Apache License, Version 2.0 (the "License"); 10107 * you may not use this file except in compliance with the License. 10108 * You may obtain a copy of the License at 10109 * 10110 * http://www.apache.org/licenses/LICENSE-2.0 10111 * 10112 * Unless required by applicable law or agreed to in writing, software 10113 * distributed under the License is distributed on an "AS IS" BASIS, 10114 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10115 * See the License for the specific language governing permissions and 10116 * limitations under the License. 10117 */ 10118class RefInfo { 10119 static get(target) { 10120 if (!target || typeof target !== 'object') { 10121 stateMgmtConsole.warn(`makeObserved target is not a valid object, return target directly`); 10122 return { proxy: target }; 10123 } 10124 // makeObserved does not support @Observed, @ObservedV2/@Trace class or makeObserved proxy, will return target directly 10125 if (ObservedObject.IsObservedObject(target) || ObserveV2.IsObservedObjectV2(target) || ObserveV2.IsMakeObserved(target)) { 10126 stateMgmtConsole.warn(`${target.constructor.name} is Observed ${ObservedObject.IsObservedObject(target)}, IsObservedV2 ${ObserveV2.IsObservedObjectV2(target)} or makeObserved proxy value ${ObserveV2.IsMakeObserved(target)}. makeObserved will stop work`); 10127 return { proxy: target }; 10128 } 10129 let ret = RefInfo.obj2ref.get(target); 10130 if (!ret) { 10131 if (Array.isArray(target) || SendableType.isArray(target)) { 10132 ret = { proxy: new Proxy(target, RefInfo.arrayProxy) }; 10133 } 10134 else if (target instanceof Set || SendableType.isSet(target) || 10135 target instanceof Map || SendableType.isMap(target)) { 10136 ret = { proxy: new Proxy(target, RefInfo.setMapProxy) }; 10137 } 10138 else { 10139 ret = { proxy: new Proxy(target, RefInfo.objectProxy) }; 10140 } 10141 RefInfo.obj2ref.set(target, ret); 10142 } 10143 return ret; 10144 } 10145} 10146RefInfo.obj2ref = new WeakMap(); 10147RefInfo.setMapProxy = new SetMapProxyHandler(true); 10148RefInfo.arrayProxy = new ArrayProxyHandler(true); 10149RefInfo.objectProxy = new ObjectProxyHandler(true); 10150/* 10151 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 10152 * Licensed under the Apache License, Version 2.0 (the "License"); 10153 * you may not use this file except in compliance with the License. 10154 * You may obtain a copy of the License at 10155 * 10156 * http://www.apache.org/licenses/LICENSE-2.0 10157 * 10158 * Unless required by applicable law or agreed to in writing, software 10159 * distributed under the License is distributed on an "AS IS" BASIS, 10160 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10161 * See the License for the specific language governing permissions and 10162 * limitations under the License. 10163 * 10164 * all definitions in this file are framework internal 10165*/ 10166var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 10167 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 10168 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 10169 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; 10170 return c > 3 && r && Object.defineProperty(target, key, r), r; 10171}; 10172// implementation for existing state observation system 10173class __RepeatItemPU { 10174 constructor(owningView, initialItem, initialIndex) { 10175 this._observedItem = new ObservedPropertyPU(initialItem, owningView, 'Repeat item'); 10176 if (initialIndex !== undefined) { 10177 this._observedIndex = new ObservedPropertyPU(initialIndex, owningView, 'Repeat index'); 10178 } 10179 } 10180 get item() { 10181 return this._observedItem.get(); 10182 } 10183 get index() { 10184 var _a; 10185 return (_a = this._observedIndex) === null || _a === void 0 ? void 0 : _a.get(); 10186 } 10187 updateItem(newItemValue) { 10188 this._observedItem.set(newItemValue); 10189 } 10190 updateIndex(newIndex) { 10191 var _a, _b, _c; 10192 if (!((_a = this._observedIndex) === null || _a === void 0 ? void 0 : _a.hasDependencies())) { 10193 return; 10194 } 10195 if (((_b = this._observedIndex) === null || _b === void 0 ? void 0 : _b.getUnmonitored()) !== newIndex) { 10196 (_c = this._observedIndex) === null || _c === void 0 ? void 0 : _c.set(newIndex); 10197 } 10198 } 10199} 10200// Framework internal, deep observation 10201// Using @ObservedV2_Internal instead of @ObservedV2 to avoid forcing V2 usage. 10202let __RepeatItemV2 = class __RepeatItemV2 { 10203 constructor(initialItem, initialIndex) { 10204 this.item = initialItem; 10205 this.index = initialIndex; 10206 } 10207 updateItem(newItemValue) { 10208 this.item = newItemValue; 10209 } 10210 updateIndex(newIndex) { 10211 if (this.index !== undefined) { 10212 this.index = newIndex; 10213 } 10214 } 10215}; 10216__decorate([ 10217 Trace_Internal 10218], __RepeatItemV2.prototype, "item", void 0); 10219__decorate([ 10220 Trace_Internal 10221], __RepeatItemV2.prototype, "index", void 0); 10222__RepeatItemV2 = __decorate([ 10223 ObservedV2_Internal 10224], __RepeatItemV2); 10225// helper 10226class __RepeatDefaultKeyGen { 10227 // Return the same IDs for the same items 10228 static func(item) { 10229 try { 10230 return __RepeatDefaultKeyGen.funcImpl(item); 10231 } 10232 catch (e) { 10233 throw new Error(`Repeat(). Default id gen failed. Application Error!`); 10234 } 10235 } 10236 // Return the same IDs for the same pairs <item, index> 10237 static funcWithIndex(item, index) { 10238 return `${index}__` + __RepeatDefaultKeyGen.func(item); 10239 } 10240 static funcImpl(item) { 10241 // fast keygen logic can be used with objects/symbols only 10242 if (typeof item !== 'object' && typeof item !== 'symbol') { 10243 return JSON.stringify(item); 10244 } 10245 // generate a numeric key, store mappings in WeakMap 10246 if (!this.weakMap_.has(item)) { 10247 return this.weakMap_.set(item, ++this.lastKey_), `${this.lastKey_}`; 10248 } 10249 // use cached key 10250 return `${this.weakMap_.get(item)}`; 10251 } 10252} 10253__RepeatDefaultKeyGen.weakMap_ = new WeakMap(); 10254__RepeatDefaultKeyGen.lastKey_ = 0; 10255; 10256; 10257// __Repeat implements ForEach with child re-use for both existing state observation 10258// and deep observation , for non-virtual and virtual code paths (TODO) 10259class __Repeat { 10260 constructor(owningView, arr) { 10261 this.config = {}; 10262 this.isVirtualScroll = false; 10263 this.config.owningView_ = owningView instanceof ViewV2 ? owningView : undefined; 10264 this.config.arr = arr !== null && arr !== void 0 ? arr : []; 10265 this.config.itemGenFuncs = {}; 10266 this.config.keyGenFunc = __RepeatDefaultKeyGen.funcWithIndex; 10267 this.config.typeGenFunc = (() => ''); 10268 this.config.totalCountSpecified = false; 10269 this.config.totalCount = this.config.arr.length; 10270 this.config.templateOptions = {}; 10271 // to be used with ViewV2 10272 const mkRepeatItemV2 = (item, index) => new __RepeatItemV2(item, index); 10273 // to be used with ViewPU 10274 const mkRepeatItemPU = (item, index) => new __RepeatItemPU(owningView, item, index); 10275 const isViewV2 = (this.config.owningView_ instanceof ViewV2); 10276 this.config.mkRepeatItem = isViewV2 ? mkRepeatItemV2 : mkRepeatItemPU; 10277 } 10278 each(itemGenFunc) { 10279 this.config.itemGenFuncs[''] = itemGenFunc; 10280 this.config.templateOptions[''] = this.normTemplateOptions({}); 10281 return this; 10282 } 10283 key(keyGenFunc) { 10284 this.config.keyGenFunc = keyGenFunc; 10285 return this; 10286 } 10287 virtualScroll(options) { 10288 if (Number.isInteger(options === null || options === void 0 ? void 0 : options.totalCount)) { 10289 this.config.totalCount = options.totalCount; 10290 this.config.totalCountSpecified = true; 10291 } 10292 else { 10293 this.config.totalCountSpecified = false; 10294 } 10295 this.isVirtualScroll = true; 10296 return this; 10297 } 10298 // function to decide which template to use, each template has an id 10299 templateId(typeGenFunc) { 10300 const typeGenFuncImpl = (item, index) => { 10301 try { 10302 return typeGenFunc(item, index); 10303 } 10304 catch (e) { 10305 stateMgmtConsole.applicationError(`Repeat with virtual scroll. Exception in templateId():`, e === null || e === void 0 ? void 0 : e.message); 10306 return ''; 10307 } 10308 }; 10309 // typeGenFunc wrapper with ttype validation 10310 const typeGenFuncSafe = (item, index) => { 10311 const itemType = typeGenFuncImpl(item, index); 10312 const itemFunc = this.config.itemGenFuncs[itemType]; 10313 if (typeof itemFunc !== 'function') { 10314 stateMgmtConsole.applicationError(`Repeat with virtual scroll. Missing Repeat.template for id '${itemType}'`); 10315 return ''; 10316 } 10317 return itemType; 10318 }; 10319 this.config.typeGenFunc = typeGenFuncSafe; 10320 return this; 10321 } 10322 // template: id + builder function to render specific type of data item 10323 template(type, itemGenFunc, options) { 10324 this.config.itemGenFuncs[type] = itemGenFunc; 10325 this.config.templateOptions[type] = this.normTemplateOptions(options); 10326 return this; 10327 } 10328 updateArr(arr) { 10329 this.config.arr = arr !== null && arr !== void 0 ? arr : []; 10330 return this; 10331 } 10332 render(isInitialRender) { 10333 var _a, _b, _c; 10334 if (!((_a = this.config.itemGenFuncs) === null || _a === void 0 ? void 0 : _a[''])) { 10335 throw new Error(`__Repeat item builder function unspecified. Usage error`); 10336 } 10337 if (!this.isVirtualScroll) { 10338 // Repeat 10339 (_b = this.impl) !== null && _b !== void 0 ? _b : (this.impl = new __RepeatImpl()); 10340 this.impl.render(this.config, isInitialRender); 10341 } 10342 else { 10343 // RepeatVirtualScroll 10344 (_c = this.impl) !== null && _c !== void 0 ? _c : (this.impl = new __RepeatVirtualScrollImpl()); 10345 this.impl.render(this.config, isInitialRender); 10346 } 10347 } 10348 // drag and drop API 10349 onMove(handler) { 10350 this.config.onMoveHandler = handler; 10351 return this; 10352 } 10353 // normalize template options 10354 normTemplateOptions(options) { 10355 const value = (options && Number.isInteger(options.cachedCount) && options.cachedCount >= 0) 10356 ? { 10357 cachedCount: Math.max(0, options.cachedCount), 10358 cachedCountSpecified: true 10359 } 10360 : { 10361 cachedCountSpecified: false 10362 }; 10363 return value; 10364 } 10365} 10366; // __Repeat<T> 10367/* 10368 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 10369 * Licensed under the Apache License, Version 2.0 (the "License"); 10370 * you may not use this file except in compliance with the License. 10371 * You may obtain a copy of the License at 10372 * 10373 * http://www.apache.org/licenses/LICENSE-2.0 10374 * 10375 * Unless required by applicable law or agreed to in writing, software 10376 * distributed under the License is distributed on an "AS IS" BASIS, 10377 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10378 * See the License for the specific language governing permissions and 10379 * limitations under the License. 10380 * 10381 * all definitions in this file are framework internal 10382*/ 10383class __RepeatImpl { 10384 /**/ 10385 constructor() { 10386 this.key2Item_ = new Map(); 10387 } 10388 /**/ 10389 render(config, isInitialRender) { 10390 this.arr_ = config.arr; 10391 this.itemGenFuncs_ = config.itemGenFuncs; 10392 this.typeGenFunc_ = config.typeGenFunc; 10393 this.keyGenFunction_ = config.keyGenFunc; 10394 this.mkRepeatItem_ = config.mkRepeatItem; 10395 this.onMoveHandler_ = config.onMoveHandler; 10396 isInitialRender ? this.initialRender() : this.reRender(); 10397 } 10398 genKeys() { 10399 const key2Item = new Map(); 10400 this.arr_.forEach((item, index) => { 10401 const key = this.keyGenFunction_(item, index); 10402 key2Item.set(key, { key, index }); 10403 }); 10404 if (key2Item.size < this.arr_.length) { 10405 stateMgmtConsole.warn('__RepeatImpl: Duplicates detected, fallback to index-based keyGen.'); 10406 // Causes all items to be re-rendered 10407 this.keyGenFunction_ = __RepeatDefaultKeyGen.funcWithIndex; 10408 return this.genKeys(); 10409 } 10410 return key2Item; 10411 } 10412 initialRender() { 10413 //console.log('__RepeatImpl initialRender() 0') 10414 this.key2Item_ = this.genKeys(); 10415 RepeatNative.startRender(); 10416 let index = 0; 10417 this.key2Item_.forEach((itemInfo, key) => { 10418 itemInfo.repeatItem = this.mkRepeatItem_(this.arr_[index], index); 10419 this.initialRenderItem(key, itemInfo.repeatItem); 10420 index++; 10421 }); 10422 let removedChildElmtIds = new Array(); 10423 // Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy 10424 RepeatNative.onMove(this.onMoveHandler_); 10425 RepeatNative.finishRender(removedChildElmtIds); 10426 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 10427 10428 } 10429 reRender() { 10430 const oldKey2Item = this.key2Item_; 10431 this.key2Item_ = this.genKeys(); 10432 // identify array items that have been deleted 10433 // these are candidates for re-use 10434 const deletedKeysAndIndex = new Array(); 10435 for (const [key, feInfo] of oldKey2Item) { 10436 if (!this.key2Item_.has(key)) { 10437 deletedKeysAndIndex.push(feInfo); 10438 } 10439 } 10440 // C++: mv children_ aside to tempchildren_ 10441 RepeatNative.startRender(); 10442 let index = 0; 10443 this.key2Item_.forEach((itemInfo, key) => { 10444 const item = this.arr_[index]; 10445 let oldItemInfo = oldKey2Item.get(key); 10446 if (oldItemInfo) { 10447 // case #1 retained array item 10448 // moved from oldIndex to index 10449 const oldIndex = oldItemInfo.index; 10450 itemInfo.repeatItem = oldItemInfo.repeatItem; 10451 10452 itemInfo.repeatItem.updateIndex(index); 10453 // C++ mv from tempChildren[oldIndex] to end of children_ 10454 RepeatNative.moveChild(oldIndex); 10455 // TBD moveChild() only when item types are same 10456 } 10457 else if (deletedKeysAndIndex.length) { 10458 // case #2: 10459 // new array item, there is an deleted array items whose 10460 // UINode children cab re-used 10461 const oldItemInfo = deletedKeysAndIndex.pop(); 10462 const reuseKey = oldItemInfo.key; 10463 const oldKeyIndex = oldItemInfo.index; 10464 const oldRepeatItem = oldItemInfo.repeatItem; 10465 itemInfo.repeatItem = oldRepeatItem; 10466 10467 itemInfo.repeatItem.updateItem(item); 10468 itemInfo.repeatItem.updateIndex(index); 10469 // update key2item_ Map 10470 this.key2Item_.set(key, itemInfo); 10471 // TBD moveChild() only when item types are same 10472 // C++ mv from tempChildren[oldIndex] to end of children_ 10473 RepeatNative.moveChild(oldKeyIndex); 10474 } 10475 else { 10476 // case #3: 10477 // new array item, there are no deleted array items 10478 // render new UINode children 10479 itemInfo.repeatItem = this.mkRepeatItem_(item, index); 10480 this.initialRenderItem(key, itemInfo.repeatItem); 10481 } 10482 index++; 10483 }); 10484 // keep this.id2item_. by removing all entries for remaining 10485 // deleted items 10486 deletedKeysAndIndex.forEach(delItem => { 10487 this.key2Item_.delete(delItem.key); 10488 }); 10489 // Finish up for.each update 10490 // C++ tempChildren.clear() , trigger re-layout 10491 let removedChildElmtIds = new Array(); 10492 // Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy 10493 RepeatNative.onMove(this.onMoveHandler_); 10494 RepeatNative.finishRender(removedChildElmtIds); 10495 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 10496 10497 } 10498 initialRenderItem(key, repeatItem) { 10499 var _a, _b; 10500 //console.log('__RepeatImpl initialRenderItem()') 10501 // render new UINode children 10502 10503 // C++: initial render will render to the end of children_ 10504 RepeatNative.createNewChildStart(key); 10505 // execute the itemGen function 10506 const itemType = (_a = this.typeGenFunc_(repeatItem.item, repeatItem.index)) !== null && _a !== void 0 ? _a : ''; 10507 const itemFunc = (_b = this.itemGenFuncs_[itemType]) !== null && _b !== void 0 ? _b : this.itemGenFuncs_['']; 10508 itemFunc(repeatItem); 10509 RepeatNative.createNewChildFinish(key); 10510 } 10511} 10512; 10513/* 10514 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 10515 * Licensed under the Apache License, Version 2.0 (the "License"); 10516 * you may not use this file except in compliance with the License. 10517 * You may obtain a copy of the License at 10518 * 10519 * http://www.apache.org/licenses/LICENSE-2.0 10520 * 10521 * Unless required by applicable law or agreed to in writing, software 10522 * distributed under the License is distributed on an "AS IS" BASIS, 10523 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10524 * See the License for the specific language governing permissions and 10525 * limitations under the License. 10526 * 10527 * all definitions in this file are framework internal 10528*/ 10529// Implements ForEach with child re-use for both existing state observation and 10530// deep observation. For virtual-scroll code paths 10531class __RepeatVirtualScrollImpl { 10532 constructor() { 10533 this.totalCountSpecified = false; 10534 // index <-> key maps 10535 this.key4Index_ = new Map(); 10536 this.index4Key_ = new Map(); 10537 // Map key -> RepeatItem 10538 // added to closure of following lambdas 10539 this.repeatItem4Key_ = new Map(); 10540 // RepeatVirtualScrollNode elmtId 10541 this.repeatElmtId_ = -1; 10542 // Last known active range (as sparse array) 10543 this.lastActiveRangeData_ = []; 10544 } 10545 render(config, isInitialRender) { 10546 this.arr_ = config.arr; 10547 this.itemGenFuncs_ = config.itemGenFuncs; 10548 this.keyGenFunc_ = config.keyGenFunc; 10549 this.typeGenFunc_ = config.typeGenFunc; 10550 // if totalCountSpecified==false, then need to create dependency on array length 10551 // so when array length changes, will update totalCount 10552 this.totalCountSpecified = config.totalCountSpecified; 10553 this.totalCount_ = (!this.totalCountSpecified || config.totalCount < 0) 10554 ? this.arr_.length 10555 : config.totalCount; 10556 this.templateOptions_ = config.templateOptions; 10557 this.mkRepeatItem_ = config.mkRepeatItem; 10558 this.onMoveHandler_ = config.onMoveHandler; 10559 if (isInitialRender) { 10560 this.initialRender(config.owningView_, ObserveV2.getCurrentRecordedId()); 10561 } 10562 else { 10563 this.reRender(); 10564 } 10565 } 10566 /**/ 10567 initialRender(owningView, repeatElmtId) { 10568 this.repeatElmtId_ = repeatElmtId; 10569 const onCreateNode = (forIndex) => { 10570 10571 if (forIndex < 0 || forIndex >= this.totalCount_ || forIndex >= this.arr_.length) { 10572 // STATE_MGMT_NOTE check also index < totalCount 10573 throw new Error(`__RepeatVirtualScrollImpl (${this.repeatElmtId_}) onCreateNode: for index=${forIndex} \ 10574 with data array length ${this.arr_.length}, totalCount=${this.totalCount_} out of range error.`); 10575 } 10576 // create dependency array item [forIndex] -> Repeat 10577 // so Repeat updates when the array item changes 10578 // STATE_MGMT_NOTE observe dependencies, adding the array is insurgent for Array of objects 10579 ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, forIndex.toString()); 10580 const repeatItem = this.mkRepeatItem_(this.arr_[forIndex], forIndex); 10581 let forKey = this.getOrMakeKey4Index(forIndex); 10582 this.repeatItem4Key_.set(forKey, repeatItem); 10583 // execute the itemGen function 10584 this.initialRenderItem(repeatItem); 10585 10586 }; // onCreateNode 10587 const onUpdateNode = (fromKey, forIndex) => { 10588 if (!fromKey || fromKey === '' || forIndex < 0 || forIndex >= this.totalCount_ || forIndex >= this.arr_.length) { 10589 throw new Error(`__RepeatVirtualScrollImpl (${this.repeatElmtId_}) onUpdateNode: fromKey "${fromKey}", \ 10590 forIndex=${forIndex}, with data array length ${this.arr_.length}, totalCount=${this.totalCount_}, invalid function input error.`); 10591 } 10592 // create dependency array item [forIndex] -> Repeat 10593 // so Repeat updates when the array item changes 10594 // STATE_MGMT_NOTE observe dependencies, adding the array is insurgent for Array of objects 10595 ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, forIndex.toString()); 10596 const repeatItem = this.repeatItem4Key_.get(fromKey); 10597 if (!repeatItem) { 10598 stateMgmtConsole.error(`__RepeatVirtualScrollImpl (${this.repeatElmtId_}) onUpdateNode: fromKey "${fromKey}", forIndex=${forIndex}, can not find RepeatItem for key. Unrecoverable error`); 10599 return; 10600 } 10601 const forKey = this.getOrMakeKey4Index(forIndex); 10602 10603 // update Map according to made update: 10604 // del fromKey entry and add forKey 10605 this.repeatItem4Key_.delete(fromKey); 10606 this.repeatItem4Key_.set(forKey, repeatItem); 10607 if (repeatItem.item !== this.arr_[forIndex] || repeatItem.index !== forIndex) { 10608 // repeatItem needs update, will trigger partial update to using UINodes: 10609 repeatItem.updateItem(this.arr_[forIndex]); 10610 repeatItem.updateIndex(forIndex); 10611 10612 ObserveV2.getObserve().updateDirty2(true); 10613 } 10614 }; // onUpdateNode 10615 const onGetKeys4Range = (from, to) => { 10616 if (to > this.totalCount_ || to > this.arr_.length) { 10617 stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId ${this.repeatElmtId_}: onGetKeys4Range from ${from} to ${to} \ 10618 with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \ 10619 Error!. Application fails to add more items to source data array. on time. Trying with corrected input parameters ...`); 10620 to = this.totalCount_; 10621 from = Math.min(to, from); 10622 } 10623 10624 const result = new Array(); 10625 // deep observe dependencies, 10626 // create dependency array item [i] -> Repeat 10627 // so Repeat updates when the array item or nested objects changes 10628 // not enough: ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, i.toString()); 10629 ViewStackProcessor.StartGetAccessRecordingFor(this.repeatElmtId_); 10630 ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false); 10631 for (let i = from; i <= to && i < this.arr_.length; i++) { 10632 result.push(this.getOrMakeKey4Index(i)); 10633 } 10634 ObserveV2.getObserve().stopRecordDependencies(); 10635 ViewStackProcessor.StopGetAccessRecording(); 10636 let needsRerender = false; 10637 result.forEach((key, index) => { 10638 const forIndex = index + from; 10639 // if repeatItem exists, and needs update then do the update, and call sync update as well 10640 // thereby ensure cached items are up-to-date on C++ side. C++ does not need to request update 10641 // from TS side 10642 const repeatItem4Key = this.repeatItem4Key_.get(key); 10643 // make sure the index is up-to-date 10644 if (repeatItem4Key && (repeatItem4Key.item !== this.arr_[forIndex] || repeatItem4Key.index !== forIndex)) { 10645 // repeatItem needs update, will trigger partial update to using UINodes: 10646 repeatItem4Key.updateItem(this.arr_[forIndex]); 10647 repeatItem4Key.updateIndex(forIndex); 10648 needsRerender = true; 10649 } 10650 }); // forEach 10651 if (needsRerender) { 10652 10653 ObserveV2.getObserve().updateDirty2(true); 10654 } 10655 10656 return result; 10657 }; // const onGetKeys4Range 10658 const onGetTypes4Range = (from, to) => { 10659 if (to > this.totalCount_ || to > this.arr_.length) { 10660 stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId: ${this.repeatElmtId_}: onGetTypes4Range from ${from} to ${to} \ 10661 with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \ 10662 Error! Application fails to add more items to source data array.on time.Trying with corrected input parameters ...`); 10663 to = this.totalCount_; 10664 from = Math.min(to, from); 10665 } 10666 10667 const result = new Array(); 10668 // deep observe dependencies, 10669 // create dependency array item [i] -> Repeat 10670 // so Repeat updates when the array item or nested objects changes 10671 // not enough: ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, i.toString()); 10672 ViewStackProcessor.StartGetAccessRecordingFor(this.repeatElmtId_); 10673 ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false); 10674 for (let i = from; i <= to && i < this.arr_.length; i++) { 10675 let ttype = this.typeGenFunc_(this.arr_[i], i); 10676 result.push(ttype); 10677 } // for 10678 ObserveV2.getObserve().stopRecordDependencies(); 10679 ViewStackProcessor.StopGetAccessRecording(); 10680 10681 return result; 10682 }; // const onGetTypes4Range 10683 const onSetActiveRange = (from, to) => { 10684 10685 // make sparse copy of this.arr_ 10686 this.lastActiveRangeData_ = new Array(this.arr_.length); 10687 if (from <= to) { 10688 for (let i = Math.max(0, from); i <= to && i < this.arr_.length; i++) { 10689 const item = this.arr_[i]; 10690 const ttype = this.typeGenFunc_(this.arr_[i], i); 10691 this.lastActiveRangeData_[i] = { item, ttype }; 10692 } 10693 } 10694 else { 10695 for (let i = 0; i <= to && i < this.arr_.length; i++) { 10696 const item = this.arr_[i]; 10697 const ttype = this.typeGenFunc_(this.arr_[i], i); 10698 this.lastActiveRangeData_[i] = { item, ttype }; 10699 } 10700 for (let i = Math.max(0, from); i < this.arr_.length; i++) { 10701 const item = this.arr_[i]; 10702 const ttype = this.typeGenFunc_(this.arr_[i], i); 10703 this.lastActiveRangeData_[i] = { item, ttype }; 10704 } 10705 } 10706 }; 10707 10708 RepeatVirtualScrollNative.create(this.totalCount_, Object.entries(this.templateOptions_), { 10709 onCreateNode, 10710 onUpdateNode, 10711 onGetKeys4Range, 10712 onGetTypes4Range, 10713 onSetActiveRange 10714 }); 10715 RepeatVirtualScrollNative.onMove(this.onMoveHandler_); 10716 10717 } 10718 reRender() { 10719 10720 if (this.hasVisibleItemsChanged()) { 10721 this.purgeKeyCache(); 10722 RepeatVirtualScrollNative.updateRenderState(this.totalCount_, true); 10723 10724 } 10725 else { 10726 // avoid re-render when data pushed outside visible area 10727 RepeatVirtualScrollNative.updateRenderState(this.totalCount_, false); 10728 10729 } 10730 } 10731 initialRenderItem(repeatItem) { 10732 // execute the itemGen function 10733 const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index); 10734 const itemFunc = this.itemGenFuncs_[itemType]; 10735 itemFunc(repeatItem); 10736 } 10737 hasVisibleItemsChanged() { 10738 var _a, _b; 10739 // has any item or ttype in the active range changed? 10740 for (let i in this.lastActiveRangeData_) { 10741 if (!(i in this.arr_)) { 10742 return true; 10743 } 10744 const oldItem = (_a = this.lastActiveRangeData_[+i]) === null || _a === void 0 ? void 0 : _a.item; 10745 const oldType = (_b = this.lastActiveRangeData_[+i]) === null || _b === void 0 ? void 0 : _b.ttype; 10746 const newItem = this.arr_[+i]; 10747 const newType = this.typeGenFunc_(this.arr_[+i], +i); 10748 if (oldItem !== newItem) { 10749 10750 return true; 10751 } 10752 if (oldType !== newType) { 10753 10754 return true; 10755 } 10756 } 10757 10758 return false; 10759 } 10760 /** 10761 * maintain: index <-> key mapping 10762 * create new key from keyGen function if not in cache 10763 * check for duplicate key, and create random key if duplicate found 10764 * @param forIndex 10765 * @returns unique key 10766 */ 10767 getOrMakeKey4Index(forIndex) { 10768 let key = this.key4Index_.get(forIndex); 10769 if (!key) { 10770 key = this.keyGenFunc_(this.arr_[forIndex], forIndex); 10771 const usedIndex = this.index4Key_.get(key); 10772 if (usedIndex !== undefined) { 10773 // duplicate key 10774 stateMgmtConsole.applicationError(`Repeat key gen function elmtId ${this.repeatElmtId_}: Detected duplicate key ${key} for indices ${forIndex} and ${usedIndex}. \ 10775 Generated random key will decrease Repeat performance. Correct the Key gen function in your application!`); 10776 key = `___${forIndex}_+_${key}_+_${Math.random()}`; 10777 } 10778 this.key4Index_.set(forIndex, key); 10779 this.index4Key_.set(key, forIndex); 10780 } 10781 return key; 10782 } 10783 purgeKeyCache() { 10784 this.key4Index_.clear(); 10785 this.index4Key_.clear(); 10786 } 10787} 10788; 10789/* 10790 * Copyright (c) 2024 Huawei Device Co., Ltd. 10791 * Licensed under the Apache License, Version 2.0 (the "License"); 10792 * you may not use this file except in compliance with the License. 10793 * You may obtain a copy of the License at 10794 * 10795 * http://www.apache.org/licenses/LICENSE-2.0 10796 * 10797 * Unless required by applicable law or agreed to in writing, software 10798 * distributed under the License is distributed on an "AS IS" BASIS, 10799 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10800 * See the License for the specific language governing permissions and 10801 * limitations under the License. 10802 */ 10803; 10804class StorageHelper { 10805 constructor() { 10806 this.oldTypeValues_ = new Map(); 10807 } 10808 getConnectedKey(type, keyOrDefaultCreator) { 10809 if (keyOrDefaultCreator === null || keyOrDefaultCreator === undefined) { 10810 stateMgmtConsole.applicationWarn(StorageHelper.NULL_OR_UNDEFINED_KEY + ', try to use the type name as key'); 10811 } 10812 if (typeof keyOrDefaultCreator === 'string') { 10813 return keyOrDefaultCreator; 10814 } 10815 return this.getTypeName(type); 10816 } 10817 getKeyOrTypeName(keyOrType) { 10818 if (typeof keyOrType === 'function') { 10819 keyOrType = this.getTypeName(keyOrType); 10820 } 10821 return keyOrType; 10822 } 10823 checkTypeByName(key, type, oldType) { 10824 if (this.getTypeName(type) !== oldType) { 10825 throw new Error(`The type mismatches when use the key '${key}' in storage`); 10826 } 10827 } 10828 checkTypeByInstanceOf(key, type, ins) { 10829 this.checkTypeIsFunc(type); 10830 if (!(ins instanceof type)) { 10831 throw new Error(`The type mismatches when use the key '${key}' in storage`); 10832 } 10833 } 10834 getTypeName(type) { 10835 this.checkTypeIsFunc(type); 10836 let name = type.name; 10837 while (name === undefined) { 10838 type = Object.getPrototypeOf(type); 10839 if (!type) { 10840 break; 10841 } 10842 name = type.name; 10843 } 10844 return name; 10845 } 10846 isKeyValid(key) { 10847 if (typeof key !== 'string') { 10848 throw new Error(StorageHelper.INVALID_TYPE); 10849 } 10850 // The key string is empty 10851 if (key === '') { 10852 stateMgmtConsole.applicationError(StorageHelper.EMPTY_STRING_KEY); 10853 return false; 10854 } 10855 const len = key.length; 10856 // The key string length should shorter than 1024 10857 if (len >= 1024) { 10858 stateMgmtConsole.applicationError(StorageHelper.INVALID_LENGTH_KEY); 10859 return false; 10860 } 10861 if (len < 2 || len > 255) { 10862 stateMgmtConsole.applicationWarn(StorageHelper.INVALID_LENGTH_KEY); 10863 } 10864 if (!/^[0-9a-zA-Z_]+$/.test(key)) { 10865 stateMgmtConsole.applicationWarn(StorageHelper.INVALID_CHARACTER_KEY); 10866 } 10867 return true; 10868 } 10869 checkTypeIsFunc(type) { 10870 if (typeof type !== 'function') { 10871 throw new Error(StorageHelper.INVALID_TYPE); 10872 } 10873 } 10874} 10875StorageHelper.INVALID_DEFAULT_VALUE = 'The default creator should be function when first connect'; 10876StorageHelper.DELETE_NOT_EXIST_KEY = 'The key to be deleted does not exist'; 10877StorageHelper.INVALID_TYPE = 'The type should have function constructor signature when use storage'; 10878StorageHelper.EMPTY_STRING_KEY = 'Cannot use empty string as the key'; 10879StorageHelper.INVALID_LENGTH_KEY = 'Cannot use the key! The length of key should be 2 to 255'; 10880StorageHelper.INVALID_CHARACTER_KEY = 'Cannot use the key! The value of key can only consist of letters, digits and underscores'; 10881StorageHelper.NULL_OR_UNDEFINED_KEY = `The parameter cannot be null or undefined`; 10882class AppStorageV2Impl extends StorageHelper { 10883 constructor() { 10884 super(); 10885 this.memorizedValues_ = new Map(); 10886 } 10887 static instance() { 10888 if (AppStorageV2Impl.instance_) { 10889 return AppStorageV2Impl.instance_; 10890 } 10891 AppStorageV2Impl.instance_ = new AppStorageV2Impl(); 10892 return AppStorageV2Impl.instance_; 10893 } 10894 connect(type, keyOrDefaultCreator, defaultCreator) { 10895 const key = this.getConnectedKey(type, keyOrDefaultCreator); 10896 if (!this.isKeyValid(key)) { 10897 return undefined; 10898 } 10899 if (typeof keyOrDefaultCreator === 'function') { 10900 defaultCreator = keyOrDefaultCreator; 10901 } 10902 if (!this.memorizedValues_.has(key)) { 10903 if (typeof defaultCreator !== 'function') { 10904 throw new Error(AppStorageV2Impl.INVALID_DEFAULT_VALUE); 10905 } 10906 const defaultValue = defaultCreator(); 10907 this.checkTypeByInstanceOf(key, type, defaultValue); 10908 this.memorizedValues_.set(key, defaultValue); 10909 this.oldTypeValues_.set(key, this.getTypeName(type)); 10910 return defaultValue; 10911 } 10912 this.checkTypeByName(key, type, this.oldTypeValues_.get(key)); 10913 const existedValue = this.memorizedValues_.get(key); 10914 return existedValue; 10915 } 10916 remove(keyOrType) { 10917 if (keyOrType === null || keyOrType === undefined) { 10918 stateMgmtConsole.applicationWarn(AppStorageV2Impl.NULL_OR_UNDEFINED_KEY); 10919 return; 10920 } 10921 const key = this.getKeyOrTypeName(keyOrType); 10922 if (!this.isKeyValid(key)) { 10923 return; 10924 } 10925 this.removeFromMemory(key); 10926 } 10927 getMemoryKeys() { 10928 return Array.from(this.memorizedValues_.keys()); 10929 } 10930 removeFromMemory(key) { 10931 const isDeleted = this.memorizedValues_.delete(key); 10932 if (!isDeleted) { 10933 stateMgmtConsole.applicationWarn(AppStorageV2Impl.DELETE_NOT_EXIST_KEY); 10934 } 10935 else { 10936 this.oldTypeValues_.delete(key); 10937 } 10938 } 10939} 10940AppStorageV2Impl.instance_ = undefined; 10941class PersistenceV2Impl extends StorageHelper { 10942 constructor() { 10943 super(); 10944 this.cb_ = undefined; 10945 this.map_ = new Proxy(new Map(), new SetMapProxyHandler()); 10946 this.keysArr_ = new Set(); 10947 this.idToKey_ = new Map(); 10948 } 10949 static instance() { 10950 if (PersistenceV2Impl.instance_) { 10951 return PersistenceV2Impl.instance_; 10952 } 10953 PersistenceV2Impl.instance_ = new PersistenceV2Impl(); 10954 return PersistenceV2Impl.instance_; 10955 } 10956 static configureBackend(storage) { 10957 PersistenceV2Impl.storage_ = storage; 10958 } 10959 connect(type, keyOrDefaultCreator, defaultCreator) { 10960 this.checkTypeIsClassObject(type); 10961 const key = this.getRightKey(type, keyOrDefaultCreator); 10962 if (!this.isKeyValid(key)) { 10963 return undefined; 10964 } 10965 if (typeof keyOrDefaultCreator === 'function') { 10966 defaultCreator = keyOrDefaultCreator; 10967 } 10968 // In memory 10969 if (this.map_.has(key)) { 10970 const existedValue = this.map_.get(key); 10971 this.checkTypeByName(key, type, this.oldTypeValues_.get(key)); 10972 return existedValue; 10973 } 10974 const observedValue = this.getConnectDefaultValue(key, type, defaultCreator); 10975 if (!observedValue) { 10976 return undefined; 10977 } 10978 const id = ++PersistenceV2Impl.nextPersistId_; 10979 this.idToKey_.set(id, key); 10980 // Not in memory, but in disk 10981 if (PersistenceV2Impl.storage_.has(key)) { 10982 return this.getValueFromDisk(key, id, observedValue, type); 10983 } 10984 // Neither in memory or in disk 10985 return this.setValueToDisk(key, id, observedValue, type); 10986 } 10987 keys() { 10988 try { 10989 if (!this.keysArr_.size) { 10990 this.keysArr_ = this.getKeysArrFromStorage(); 10991 } 10992 } 10993 catch (err) { 10994 if (this.cb_ && typeof this.cb_ === 'function') { 10995 this.cb_('', "unknown" /* Unknown */, `fail to get all persisted keys`); 10996 return; 10997 } 10998 throw new Error(err); 10999 } 11000 return Array.from(this.keysArr_); 11001 } 11002 remove(keyOrType) { 11003 if (keyOrType === null || keyOrType === undefined) { 11004 stateMgmtConsole.applicationWarn(PersistenceV2Impl.NULL_OR_UNDEFINED_KEY); 11005 return; 11006 } 11007 this.checkTypeIsClassObject(keyOrType); 11008 const key = this.getKeyOrTypeName(keyOrType); 11009 if (!this.isKeyValid(key)) { 11010 return; 11011 } 11012 this.disconnectValue(key); 11013 } 11014 save(keyOrType) { 11015 if (keyOrType === null || keyOrType === undefined) { 11016 stateMgmtConsole.applicationWarn(PersistenceV2Impl.NULL_OR_UNDEFINED_KEY); 11017 return; 11018 } 11019 this.checkTypeIsClassObject(keyOrType); 11020 const key = this.getKeyOrTypeName(keyOrType); 11021 if (!this.isKeyValid(key)) { 11022 return; 11023 } 11024 if (!this.map_.has(key)) { 11025 stateMgmtConsole.applicationWarn(`Cannot save the key '${key}'! The key is disconnected`); 11026 return; 11027 } 11028 try { 11029 PersistenceV2Impl.storage_.set(key, JSONCoder.stringify(this.map_.get(key))); 11030 } 11031 catch (err) { 11032 this.errorHelper(key, "serialization" /* Serialization */, err); 11033 } 11034 } 11035 notifyOnError(cb) { 11036 this.cb_ = cb; 11037 } 11038 onChangeObserved(persistKeys) { 11039 this.writeAllChangedToFile(persistKeys); 11040 } 11041 connectNewValue(key, newValue, typeName) { 11042 this.map_.set(key, newValue); 11043 this.oldTypeValues_.set(key, typeName); 11044 this.storeKeyToPersistenceV2(key); 11045 } 11046 disconnectValue(key) { 11047 this.map_.delete(key); 11048 this.oldTypeValues_.delete(key); 11049 this.removeFromPersistenceV2(key); 11050 } 11051 checkTypeIsClassObject(keyOrType) { 11052 if ((typeof keyOrType !== 'string' && typeof keyOrType !== 'function') || 11053 PersistenceV2Impl.NOT_SUPPORT_TYPES_.includes(keyOrType)) { 11054 throw new Error(PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_); 11055 } 11056 } 11057 getRightKey(type, keyOrDefaultCreator) { 11058 const key = this.getConnectedKey(type, keyOrDefaultCreator); 11059 if (key === undefined) { 11060 throw new Error(PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_); 11061 } 11062 if (key === PersistenceV2Impl.KEYS_ARR_) { 11063 this.errorHelper(key, "quota" /* Quota */, `The key '${key}' cannot be used`); 11064 return undefined; 11065 } 11066 return key; 11067 } 11068 getConnectDefaultValue(key, type, defaultCreator) { 11069 if (!PersistenceV2Impl.storage_) { 11070 this.errorHelper(key, "unknown" /* Unknown */, `The storage is null`); 11071 return undefined; 11072 } 11073 if (typeof defaultCreator !== 'function') { 11074 throw new Error(PersistenceV2Impl.INVALID_DEFAULT_VALUE); 11075 } 11076 const observedValue = defaultCreator(); 11077 this.checkTypeByInstanceOf(key, type, observedValue); 11078 if (this.isNotClassObject(observedValue)) { 11079 throw new Error(PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_); 11080 } 11081 return observedValue; 11082 } 11083 getValueFromDisk(key, id, observedValue, type) { 11084 let newObservedValue; 11085 try { 11086 const json = PersistenceV2Impl.storage_.get(key); 11087 // Adding ref for persistence 11088 ObserveV2.getObserve().startRecordDependencies(this, id); 11089 newObservedValue = JSONCoder.parseTo(observedValue, json); 11090 ObserveV2.getObserve().stopRecordDependencies(); 11091 } 11092 catch (err) { 11093 this.errorHelper(key, "serialization" /* Serialization */, err); 11094 } 11095 this.checkTypeByInstanceOf(key, type, newObservedValue); 11096 this.connectNewValue(key, newObservedValue, this.getTypeName(type)); 11097 return newObservedValue; 11098 } 11099 setValueToDisk(key, id, observedValue, type) { 11100 ObserveV2.getObserve().startRecordDependencies(this, id); 11101 // Map is a proxy object. When it is connected for the first time, map.has is used to add dependencies, 11102 // and map.set is used to trigger writing to disk. 11103 const hasKey = this.map_.has(key); 11104 ObserveV2.getObserve().stopRecordDependencies(); 11105 // Writing to persistence by ref 11106 if (!hasKey) { 11107 this.connectNewValue(key, observedValue, this.getTypeName(type)); 11108 } 11109 return observedValue; 11110 } 11111 writeAllChangedToFile(persistKeys) { 11112 for (let i = 0; i < persistKeys.length; ++i) { 11113 const id = persistKeys[i]; 11114 const key = this.idToKey_.get(id); 11115 try { 11116 const hasKey = this.map_.has(key); 11117 if (hasKey) { 11118 const value = this.map_.get(key); 11119 ObserveV2.getObserve().startRecordDependencies(this, id); 11120 const json = JSONCoder.stringify(value); 11121 ObserveV2.getObserve().stopRecordDependencies(); 11122 if (this.isOverSizeLimit(json)) { 11123 stateMgmtConsole.applicationError(`Cannot store the key '${key}'! The length of data must be less than ${PersistenceV2Impl.MAX_DATA_LENGTH_}`); 11124 } 11125 else { 11126 PersistenceV2Impl.storage_.set(key, json); 11127 } 11128 } 11129 } 11130 catch (err) { 11131 if (this.cb_ && typeof this.cb_ === 'function') { 11132 this.cb_(key, "serialization" /* Serialization */, err); 11133 continue; 11134 } 11135 stateMgmtConsole.applicationError(`For '${key}' key: ` + err); 11136 } 11137 } 11138 } 11139 isOverSizeLimit(json) { 11140 if (typeof json !== 'string') { 11141 return false; 11142 } 11143 return json.length >= PersistenceV2Impl.MAX_DATA_LENGTH_; 11144 } 11145 isNotClassObject(value) { 11146 return Array.isArray(value) || this.isNotSupportType(value); 11147 } 11148 isNotSupportType(value) { 11149 for (let i = 0; i < PersistenceV2Impl.NOT_SUPPORT_TYPES_.length; ++i) { 11150 if (value instanceof PersistenceV2Impl.NOT_SUPPORT_TYPES_[i]) { 11151 return true; 11152 } 11153 } 11154 return false; 11155 } 11156 storeKeyToPersistenceV2(key) { 11157 try { 11158 if (this.keysArr_.has(key)) { 11159 return; 11160 } 11161 // Initializing the keys arr in memory 11162 if (!this.keysArr_.size) { 11163 this.keysArr_ = this.getKeysArrFromStorage(); 11164 } 11165 this.keysArr_.add(key); 11166 // Updating the keys arr in disk 11167 this.storeKeysArrToStorage(this.keysArr_); 11168 } 11169 catch (err) { 11170 this.errorHelper(key, "unknown" /* Unknown */, `fail to store the key '${key}'`); 11171 } 11172 } 11173 removeFromPersistenceV2(key) { 11174 try { 11175 if (!PersistenceV2Impl.storage_.has(key)) { 11176 stateMgmtConsole.applicationWarn(PersistenceV2Impl.DELETE_NOT_EXIST_KEY); 11177 return; 11178 } 11179 PersistenceV2Impl.storage_.delete(key); 11180 // The first call to remove 11181 if (!this.keysArr_.has(key)) { 11182 this.keysArr_ = this.getKeysArrFromStorage(); 11183 } 11184 this.keysArr_.delete(key); 11185 this.storeKeysArrToStorage(this.keysArr_); 11186 } 11187 catch (err) { 11188 this.errorHelper(key, "unknown" /* Unknown */, `fail to remove the key '${key}'`); 11189 } 11190 } 11191 getKeysArrFromStorage() { 11192 if (!PersistenceV2Impl.storage_.has(PersistenceV2Impl.KEYS_ARR_)) { 11193 return this.keysArr_; 11194 } 11195 const jsonKeysArr = PersistenceV2Impl.storage_.get(PersistenceV2Impl.KEYS_ARR_); 11196 return new Set(JSON.parse(jsonKeysArr)); 11197 } 11198 storeKeysArrToStorage(keysArr) { 11199 PersistenceV2Impl.storage_.set(PersistenceV2Impl.KEYS_ARR_, JSON.stringify(Array.from(keysArr))); 11200 } 11201 errorHelper(key, reason, message) { 11202 if (this.cb_ && typeof this.cb_ === 'function') { 11203 this.cb_(key, reason, message); 11204 return; 11205 } 11206 if (!key) { 11207 key = 'unknown'; 11208 } 11209 throw new Error(`For '${key}' key: ` + message); 11210 } 11211} 11212PersistenceV2Impl.MIN_PERSISTENCE_ID = 0x1010000000000; 11213PersistenceV2Impl.nextPersistId_ = PersistenceV2Impl.MIN_PERSISTENCE_ID; 11214PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_ = 'Not support! Can only use the class object in Persistence'; 11215PersistenceV2Impl.KEYS_ARR_ = '___keys_arr'; 11216PersistenceV2Impl.MAX_DATA_LENGTH_ = 8000; 11217PersistenceV2Impl.NOT_SUPPORT_TYPES_ = [Array, Set, Map, WeakSet, WeakMap, Date, Boolean, Number, String, Symbol, BigInt, RegExp, Function, Promise, ArrayBuffer]; 11218PersistenceV2Impl.instance_ = undefined; 11219/* 11220 * Copyright (c) 2024 Huawei Device Co., Ltd. 11221 * Licensed under the Apache License, Version 2.0 (the "License"); 11222 * you may not use this file except in compliance with the License. 11223 * You may obtain a copy of the License at 11224 * 11225 * http://www.apache.org/licenses/LICENSE-2.0 11226 * 11227 * Unless required by applicable law or agreed to in writing, software 11228 * distributed under the License is distributed on an "AS IS" BASIS, 11229 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11230 * See the License for the specific language governing permissions and 11231 * limitations under the License. 11232 */ 11233class UIUtilsImpl { 11234 getTarget(source) { 11235 if (!source || typeof source !== 'object') { 11236 return source; 11237 } 11238 if (ObservedObject.IsObservedObject(source)) { 11239 // V1 Proxy object 11240 return ObservedObject.GetRawObject(source); 11241 } 11242 else if (source[ObserveV2.SYMBOL_PROXY_GET_TARGET]) { 11243 // V2 Proxy object 11244 return source[ObserveV2.SYMBOL_PROXY_GET_TARGET]; 11245 } 11246 else { 11247 // other situation, not handle 11248 return source; 11249 } 11250 } 11251 makeObserved(target) { 11252 // mark makeObserved using V2 feature 11253 ConfigureStateMgmt.instance.usingV2ObservedTrack('makeObserved', 'use'); 11254 return RefInfo.get(target).proxy; 11255 } 11256 static instance() { 11257 if (UIUtilsImpl.instance_) { 11258 return UIUtilsImpl.instance_; 11259 } 11260 UIUtilsImpl.instance_ = new UIUtilsImpl(); 11261 return UIUtilsImpl.instance_; 11262 } 11263} 11264UIUtilsImpl.instance_ = undefined; 11265/* 11266 * Copyright (c) 2024 Huawei Device Co., Ltd. 11267 * Licensed under the Apache License, Version 2.0 (the "License"); 11268 * you may not use this file except in compliance with the License. 11269 * You may obtain a copy of the License at 11270 * 11271 * http://www.apache.org/licenses/LICENSE-2.0 11272 * 11273 * Unless required by applicable law or agreed to in writing, software 11274 * distributed under the License is distributed on an "AS IS" BASIS, 11275 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11276 * See the License for the specific language governing permissions and 11277 * limitations under the License. 11278 */ 11279class GestureStyle extends NativeGestureStyle { 11280 constructor(arg) { 11281 super(arg); 11282 this.arg_ = arg; 11283 } 11284} 11285/* 11286 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 11287 * Licensed under the Apache License, Version 2.0 (the "License"); 11288 * you may not use this file except in compliance with the License. 11289 * You may obtain a copy of the License at 11290 * 11291 * http://www.apache.org/licenses/LICENSE-2.0 11292 * 11293 * Unless required by applicable law or agreed to in writing, software 11294 * distributed under the License is distributed on an "AS IS" BASIS, 11295 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11296 * See the License for the specific language governing permissions and 11297 * limitations under the License. 11298 */ 11299 11300PersistenceV2Impl.configureBackend(new Storage()); 11301PersistentStorage.configureBackend(new Storage()); 11302Environment.configureBackend(new EnvironmentSetting()); 11303 11304 11305