• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16// @Track class property decorator
17// indicates to framework to track individual object property value changes
18function Track(target: Object, property: string) {
19  ConfigureStateMgmt.instance.usingPUObservedTrack(`@Track`, property);
20  Reflect.set(target, `${TrackedObject.___TRACKED_PREFIX}${property}`, true);
21  Reflect.set(target, TrackedObject.___IS_TRACKED_OPTIMISED, true);
22  stateMgmtConsole.debug(`class ${target.constructor?.name}: property @Track ${property}`);
23}
24
25class TrackedObject {
26  public static readonly ___IS_TRACKED_OPTIMISED = `___IS_TRACKED_OPTIMISED`;
27  public static readonly ___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_PROP_PROPERTY`;
28  public static readonly ___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_OBJLINK_PROPERTY`;
29  public static readonly ___TRACKED_PREFIX = `___TRACKED_`;
30  private static readonly ___TRACKED_PREFIX_LEN = TrackedObject.___TRACKED_PREFIX.length;
31
32  public static isCompatibilityMode(obj: Object): boolean {
33    return !obj || (typeof obj !== 'object') || !Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED);
34  }
35
36  public static needsPropertyReadCb(obj: Object): boolean {
37    return obj && (typeof obj === 'object') && Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED);
38  }
39
40  /**
41   * @Track new object assignment optimization
42   * can apply if old and new value are object, instance of same class, do not use compat mode.
43   * in this case function returns true and calls supplied notifyTrackedPropertyChanged cb function
44   * for each @Tracked'ed property whose value actually changed.
45   * if optimisation can not be applied calls notifyPropertyChanged and returns false
46   */
47  public static notifyObjectValueAssignment(obj1: Object, obj2: Object,
48    notifyPropertyChanged: (isFromSource) => void, // notify as assignment (none-optimised)
49    notifyTrackedPropertyChange: (propName) => void, obSelf: ObservedPropertyAbstractPU<any>): boolean {
50    if (!obj1 || !obj2 || (typeof obj1 !== 'object') || (typeof obj2 !== 'object') ||
51      (obj1.constructor !== obj2.constructor) ||
52      TrackedObject.isCompatibilityMode(obj1)) {
53      stateMgmtConsole.debug(`TrackedObject.notifyObjectValueAssignment notifying change as assignment (non-optimised)`);
54      notifyPropertyChanged.call(obSelf);
55      return false;
56    }
57
58    stateMgmtConsole.debug(`TrackedObject.notifyObjectValueAssignment notifying actually changed properties (optimised)`);
59    const obj1Raw = ObservedObject.GetRawObject(obj1);
60    const obj2Raw = ObservedObject.GetRawObject(obj2);
61    let shouldFakePropPropertyBeNotified: boolean = false;
62    Object.keys(obj2Raw)
63      .forEach(propName => {
64        // Collect only @Track'ed changed properties
65        if (Reflect.has(obj1Raw, `${TrackedObject.___TRACKED_PREFIX}${propName}`) &&
66          (Reflect.get(obj1Raw, propName) !== Reflect.get(obj2Raw, propName))) {
67          stateMgmtConsole.debug(`   ... '@Track ${propName}' value changed - notifying`);
68          notifyTrackedPropertyChange.call(obSelf, propName);
69          shouldFakePropPropertyBeNotified = true;
70        } else {
71          stateMgmtConsole.debug(`   ... '${propName}' value unchanged or not @Track'ed - not notifying`);
72        }
73      });
74    // notify this non-existing object property has changed only if some of the tracked properties changed.
75    // SynchedPropertyOneWay reset() report a 'read' on this property, thereby creating a dependency
76    // reporting the property as changed causes @Prop sync from source
77    if (shouldFakePropPropertyBeNotified) {
78      stateMgmtConsole.debug(`   ... TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY - notifying`);
79      notifyTrackedPropertyChange.call(obSelf, TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY);
80    }
81
82    // always notify this non-existing object property has changed for SynchedPropertyNestedObject as
83    // the object has changed in assigment.
84    // SynchedPropertyNestedObject set() reports a 'read' on this property, thereby creating a dependency
85    // reporting the property as changed causes @ObjectLink sync from source
86    stateMgmtConsole.debug(`   ... TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY - notifying`);
87    notifyTrackedPropertyChange.call(obSelf, TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY);
88    return true;
89  }
90}