diff --git a/arkoala/arkui-common/src/arkts/Storage.ts b/arkoala/arkui-common/src/arkts/Storage.ts index 2d2abe098d7540bbda25edb32289b2865df9a01e..25290424194b795ab0b1d04cdcaf9d4d09b6ee52 100644 --- a/arkoala/arkui-common/src/arkts/Storage.ts +++ b/arkoala/arkui-common/src/arkts/Storage.ts @@ -13,7 +13,381 @@ * limitations under the License. */ -import { GlobalStateManager, mutableState, MutableState } from "@koalaui/runtime" +import { Disposable, GlobalStateManager, MutableState, StateManager } from "@koalaui/runtime" + +/** + * Defines the subscribed abstract property. + * @since 7 + * @systemapi + */ +export abstract class SubscribedAbstractProperty { + /** + * Returns the property name, + * e.g. let link = AppStorage.Link("foo") then link.info() == "foo" + * + * @returns { string } the property name if set or undefined + * @syscap SystemCapability.ArkUI.ArkUI.Full + * @crossplatform + * @since 10 + */ + info(): string { + return "TODO" + } + /** + * Called when data is obtained. + * @since 7 + * @systemapi + */ + abstract get(): T + + /** + * Called when data is created. + * @since 7 + * @systemapi + */ + abstract set(newValue: T): void + /** + * An app needs to call this function before the instance of SubscribedAbstractProperty + * goes out of scope / is subject to garbage collection. Its purpose is to unregister the + * variable from the two-way/one-way sync relationship that AppStorage/LocalStorage.link()/prop() + * and related functions create. + * + * @syscap SystemCapability.ArkUI.ArkUI.Full + * @crossplatform + * @since 10 + */ + abstract aboutToBeDeleted(): void +} + +interface Mutable { + readonly mutable: boolean +} + +class StorageEntry extends SubscribedAbstractProperty implements Disposable, Mutable { + readonly state: MutableState + readonly mutable: boolean + + constructor(state: MutableState, mutable?: boolean) { + super() + this.state = state + this.mutable = mutable ?? true + } + + get disposed(): boolean { + return this.state.disposed + } + + dispose(): void { + this.state.dispose() + } + + aboutToBeDeleted(): void { + // todo + } + + get(): V { + return this.state.value + } + + set(value: V): void { + if (this.mutable) this.state.value = value + } +} + +export class StorageMap { + private static _shared?: StorageMap + private readonly manager: StateManager = GlobalStateManager.instance + private readonly map: Map = new Map() + + static get shared(): StorageMap { + if (StorageMap._shared?.manager != GlobalStateManager.instance) { + StorageMap._shared = new StorageMap() // recreate shared storage if state manager is changed + } + return StorageMap._shared as StorageMap + } + + keys(): IterableIterator { + return this.map.keys() + } + + get size(): number { + return this.map.size + } + + has(key: string): boolean { + return this.map.has(key) + } + + entry(key: string): StorageEntry | undefined { + return this.map.get(key) as StorageEntry | undefined + } + + entryObject(key: string): Object | undefined { + return this.map.get(key) + } + + get(key: string): V | undefined { + const entry = this.entry(key) + return entry?.get() + } + + set(key: string, value: V, create?: boolean, mutable?: boolean): boolean { + const entry = this.entry(key) + if (entry) { + entry.set(value) + return true + } + if (create ?? true) { + this.create(key, value, mutable ?? true) + } + return false + } + + link(key: string, value: V): StorageEntry { + const entry = this.entry(key) + return entry ? entry : this.create(key, value) + } + + delete(key: string): boolean { + const entry = this.entryObject(key) + if (!entry) + return false; + (entry as Disposable).dispose(); + return this.map.delete(key) + } + + clear(): boolean { + if (this.map.size == 0) return false + for (const entry of this.map.values()) { + (entry as Disposable).dispose() + } + this.map.clear() + return true + } + + private create(key: string, value: V, mutable?: boolean): StorageEntry { + const entry = new StorageEntry(this.manager.mutableState(value, true), mutable ?? true) + this.map.set(key, entry) + return entry + } +} export class LocalStorage { -} \ No newline at end of file + // todo +} + +/** + * Defines the AppStorage interface. + * @since 7 + */ +export class AppStorage { + /** + * Called when a link is set. + * @since 7 + * @deprecated since 10 + */ + static Link(propName: string): SubscribedAbstractProperty { + return AppStorage.link(propName) + } + /** + * Called when a link is set. + * @since 10 + */ + static link(propName: string): SubscribedAbstractProperty { + throw new Error("AppStorage.link is not implemented") + } + + /** + * Called when a hyperlink is set. + * @since 7 + * @deprecated since 10 + */ + static SetAndLink(propName: string, defaultValue: V): SubscribedAbstractProperty { + return AppStorage.setAndLink(propName, defaultValue) + } + /** + * Called when a hyperlink is set. + * @since 10 + */ + static setAndLink(propName: string, defaultValue: V): SubscribedAbstractProperty { + throw new Error("AppStorage.setAndLink is not implemented") + } + + /** + * Called when a property is set. + * @since 7 + * @deprecated since 10 + */ + static Prop(propName: string): SubscribedAbstractProperty { + return AppStorage.prop(propName) + } + /** + * Called when a property is set. + * @since 10 + */ + static prop(propName: string): SubscribedAbstractProperty { + throw new Error("AppStorage.prop is not implemented") + } + + /** + * Called when dynamic properties are set. + * @since 7 + * @deprecated since 10 + */ + static SetAndProp(propName: string, defaultValue: V): SubscribedAbstractProperty { + return AppStorage.setAndProp(propName, defaultValue) + } + /** + * Called when dynamic properties are set. + * @since 10 + */ + static setAndProp(propName: string, defaultValue: V): SubscribedAbstractProperty { + throw new Error("AppStorage.setAndProp is not implemented") + } + + /** + * Called when owning or not. + * @since 7 + * @deprecated since 10 + */ + static Has(propName: string): boolean { + return AppStorage.has(propName) + } + /** + * Called when owning or not. + * @since 10 + */ + static has(propName: string): boolean { + return StorageMap.shared.has(propName) + } + + /** + * Called when data is obtained. + * @since 7 + * @deprecated since 10 + */ + static Get(propName: string): V | undefined { + return AppStorage.get(propName) + } + /** + * Called when data is obtained. + * @since 10 + */ + static get(propName: string): V | undefined { + return StorageMap.shared.get(propName) + } + + /** + * Called when setting. + * @since 7 + * @deprecated since 10 + */ + static Set(propName: string, newValue: V): boolean { + return AppStorage.set(propName, newValue) + } + /** + * Called when setting. + * @since 10 + */ + static set(propName: string, newValue: V): boolean { + return StorageMap.shared.set(propName, newValue, false) + } + + /** + * Called when setting or creating. + * @since 7 + * @deprecated since 10 + */ + static SetOrCreate(propName: string, newValue: V): void { + AppStorage.setOrCreate(propName, newValue) + } + /** + * Called when setting or creating. + * @since 10 + */ + static setOrCreate(propName: string, newValue: V): void { + StorageMap.shared.set(propName, newValue, true) + } + + /** + * Called when a deletion is made. + * @since 7 + * @deprecated since 10 + */ + static Delete(propName: string): boolean { + return AppStorage.delete(propName) + } + /** + * Called when a deletion is made. + * @since 10 + */ + static delete(propName: string): boolean { + return StorageMap.shared.delete(propName) + } + + /** + * Called when a dictionary is sorted. + * @since 7 + * @deprecated since 10 + */ + static Keys(): IterableIterator { + return AppStorage.keys() + } + /** + * Called when a dictionary is sorted. + * @since 10 + */ + static keys(): IterableIterator { + return StorageMap.shared.keys() + } + + /** + * Called when a cleanup occurs. + * @since 7 + * @deprecated since 9 + * @useinstead AppStorage.Clear + */ + static staticClear(): boolean { + return StorageMap.shared.clear() + } + + /** + * Called when a cleanup occurs. + * @since 9 + * @deprecated since 10 + */ + static Clear(): boolean { + return AppStorage.clear() + } + /** + * Called when a cleanup occurs. + * @since 10 + */ + static clear(): boolean { + return StorageMap.shared.clear() + } + + /** + * Called when the data can be changed. + * @since 7 + */ + static IsMutable(propName: string): boolean { + const entry = StorageMap.shared.entryObject(propName) + return entry ? (entry as Mutable).mutable : false + } + + /** + * Called when you check how much data is stored. + * @since 7 + */ + static Size(): number { + return AppStorage.size() + } + /** + * Called when you check how much data is stored. + * @since 10 + */ + static size(): number { + return StorageMap.shared.size + } +}