• 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  Reflect.set(target, `${TrackedObject.___TRACKED_PREFIX}${property}`, true);
20  Reflect.set(target, TrackedObject.___IS_TRACKED_OPTIMISED, true);
21  stateMgmtConsole.debug(`class ${target.constructor?.name}: property @Track ${property}`);
22}
23
24class TrackedObject {
25  public static readonly ___IS_TRACKED_OPTIMISED = `___IS_TRACKED_OPTIMISED`;
26  public static readonly ___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_PROP_PROPERTY`;
27  public static readonly ___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_OBJLINK_PROPERTY`;
28  public static readonly ___TRACKED_PREFIX = `___TRACKED_`;
29  private static readonly ___TRACKED_PREFIX_LEN = TrackedObject.___TRACKED_PREFIX.length;
30
31  public static isCompatibilityMode(obj: Object): boolean {
32    return !obj || (typeof obj !== "object") || !Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED);
33  }
34
35  public static needsPropertyReadCb(obj: Object): boolean {
36    return obj && (typeof obj === 'object') && Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED);
37  }
38
39  /**
40   * @Track new object assignment optimization
41   * can apply if old and new value are object, instance of same class, do not use compat mode.
42   * in this case function returns true and calls supplied notifyTrackedPropertyChanged cb function
43   * for each @Tracked'ed property whose value actually changed.
44   * if optimisation can not be applied calls notifyPropertyChanged and returns false
45   */
46  public static notifyObjectValueAssignment(obj1: Object, obj2: Object,
47    notifyPropertyChanged: () => void, // notify as assignment (none-optimised)
48    notifyTrackedPropertyChange: (propName) => void): boolean {
49    if (!obj1 || !obj2 || (typeof obj1 !== 'object') || (typeof obj2 !== 'object') ||
50      (obj1.constructor !== obj2.constructor) ||
51      TrackedObject.isCompatibilityMode(obj1)) {
52      stateMgmtConsole.debug(`TrackedObject.notifyObjectValueAssignment notifying change as assignment (non-optimised)`)
53      notifyPropertyChanged();
54      return false;
55    }
56
57    stateMgmtConsole.debug(`TrackedObject.notifyObjectValueAssignment notifying actually changed properties (optimised)`)
58    const obj1Raw = ObservedObject.GetRawObject(obj1);
59    const obj2Raw = ObservedObject.GetRawObject(obj2);
60    let shouldFakePropPropertyBeNotified: boolean = false;
61    Object.keys(obj2Raw)
62      .forEach(propName => {
63        // Collect only @Track'ed changed properties
64        if (Reflect.has(obj1Raw, `${TrackedObject.___TRACKED_PREFIX}${propName}`) &&
65          (Reflect.get(obj1Raw, propName) !== Reflect.get(obj2Raw, propName))) {
66          stateMgmtConsole.debug(`   ... '@Track ${propName}' value changed - notifying`);
67          notifyTrackedPropertyChange(propName);
68          shouldFakePropPropertyBeNotified = true;
69        } else {
70          stateMgmtConsole.debug(`   ... '${propName}' value unchanged or not @Track'ed - not notifying`);
71        }
72      });
73    // notify this non-existing object property has changed only if some of the tracked properties changed.
74    // SynchedPropertyOneWay.reset() report a 'read' on this property, thereby creating a dependency
75    // reporting the property as changed causes @Prop sync from source
76    if (shouldFakePropPropertyBeNotified) {
77      stateMgmtConsole.debug(`   ... TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY - notifying`);
78      notifyTrackedPropertyChange(TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY);
79    }
80
81    // always notify this non-existing object property has changed for SynchedPropertyNestedObject as
82    // the object has changed in assigment.
83    // SynchedPropertyNestedObject.set() reports a 'read' on this property, thereby creating a dependency
84    // reporting the property as changed causes @ObjectLink sync from source
85    stateMgmtConsole.debug(`   ... TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY - notifying`);
86    notifyTrackedPropertyChange(TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY);
87    return true;
88  }
89}