diff --git a/es/debugStore/DebugStore.d.ts b/es/debugStore/DebugStore.d.ts index 7599e026..1f880c46 100644 --- a/es/debugStore/DebugStore.d.ts +++ b/es/debugStore/DebugStore.d.ts @@ -12,6 +12,9 @@ interface DebugStoreSelectOption extends TreeStoreSelectOption { export declare class DebugStore> extends TreeStore implements AsyncRowStore { private executor; private relationAuth; + private dataLoaded; + private dataLoadedLock; + private dataLoadedLockUnlocker; constructor(storageSchema: StorageSchema, contextBuilder: (cxtString?: string) => (store: DebugStore) => Promise, authDeduceRelationMap: AuthDeduceRelationMap, selectFreeEntities?: (keyof ED)[], updateFreeDict?: { [A in keyof ED]?: string[]; }); @@ -26,5 +29,13 @@ export declare class DebugStore(entity: T, selection: Pick, context: Cxt, option: OP): Promise; registerTrigger(trigger: Trigger): void; registerChecker(checker: Checker): void; + resetInitialData(initialData: { + [T in keyof ED]?: Array; + }, stat?: { + create: number; + update: number; + remove: number; + commit: number; + }): void; } export {}; diff --git a/es/debugStore/DebugStore.js b/es/debugStore/DebugStore.js index d0e82c61..7e81c942 100644 --- a/es/debugStore/DebugStore.js +++ b/es/debugStore/DebugStore.js @@ -6,8 +6,15 @@ import { RelationAuth } from "oak-domain/lib/store/RelationAuth"; export class DebugStore extends TreeStore { executor; relationAuth; + dataLoaded; + dataLoadedLock; + dataLoadedLockUnlocker = () => undefined; constructor(storageSchema, contextBuilder, authDeduceRelationMap, selectFreeEntities, updateFreeDict) { super(storageSchema); + this.dataLoaded = false; + this.dataLoadedLock = new Promise((resolve) => { + this.dataLoadedLockUnlocker = () => resolve(); + }); this.executor = new TriggerExecutor((cxtString) => contextBuilder(cxtString)(this)); this.relationAuth = new RelationAuth(storageSchema, authDeduceRelationMap, selectFreeEntities, updateFreeDict); } @@ -38,6 +45,9 @@ export class DebugStore extends TreeStore { return result; } async operate(entity, operation, context, option) { + if (!this.dataLoaded) { + await this.dataLoadedLock; + } const autoCommit = !context.getCurrentTxnId(); let result; if (autoCommit) { @@ -63,6 +73,9 @@ export class DebugStore extends TreeStore { return result; } async select(entity, selection, context, option) { + if (!this.dataLoaded) { + await this.dataLoadedLock; + } const autoCommit = !context.getCurrentTxnId(); if (autoCommit) { await context.begin(); @@ -95,6 +108,9 @@ export class DebugStore extends TreeStore { } } async count(entity, selection, context, option) { + if (!this.dataLoaded) { + await this.dataLoadedLock; + } return super.countAsync(entity, selection, context, option); } registerTrigger(trigger) { @@ -103,4 +119,9 @@ export class DebugStore extends TreeStore { registerChecker(checker) { this.executor.registerChecker(checker); } + resetInitialData(initialData, stat) { + super.resetInitialData(initialData, stat); + this.dataLoaded = true; + this.dataLoadedLockUnlocker(); + } } diff --git a/es/debugStore/index.d.ts b/es/debugStore/index.d.ts index 18bb4f7e..85e43230 100644 --- a/es/debugStore/index.d.ts +++ b/es/debugStore/index.d.ts @@ -5,6 +5,6 @@ import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; export declare function clearMaterializedData(): void; export declare function createDebugStore>(storageSchema: StorageSchema, contextBuilder: (cxtString?: string) => (store: DebugStore) => Promise, triggers: Array>, checkers: Array>, watchers: Array>, timers: Array>, startRoutines: Array>, initialData: { [T in keyof ED]?: Array; -}, actionDict: ActionDictOfEntityDict, authDeduceRelationMap: AuthDeduceRelationMap, saveFn: (key: string, data: any) => void, loadFn: (key: string) => any, selectFreeEntities?: (keyof ED)[], updateFreeDict?: { +}, actionDict: ActionDictOfEntityDict, authDeduceRelationMap: AuthDeduceRelationMap, saveFn: (key: string, data: any) => Promise, loadFn: (key: string) => Promise, selectFreeEntities?: (keyof ED)[], updateFreeDict?: { [A in keyof ED]?: string[]; }): DebugStore; diff --git a/es/debugStore/index.js b/es/debugStore/index.js index e0d6159f..3db29efe 100644 --- a/es/debugStore/index.js +++ b/es/debugStore/index.js @@ -6,10 +6,10 @@ import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid'; async function initDataInStore(store, initialData, stat) { store.resetInitialData(initialData, stat); } -function getMaterializedData(loadFn) { +async function getMaterializedData(loadFn) { try { - const data = loadFn(LOCAL_STORAGE_KEYS.debugStore); - const stat = loadFn(LOCAL_STORAGE_KEYS.debugStoreStat); + const data = await loadFn(LOCAL_STORAGE_KEYS.debugStore); + const stat = await loadFn(LOCAL_STORAGE_KEYS.debugStoreStat); if (data && stat) { return { data, @@ -23,10 +23,10 @@ function getMaterializedData(loadFn) { } } let lastMaterializedVersion = 0; -function materializeData(data, stat, saveFn) { +async function materializeData(data, stat, saveFn) { try { - saveFn(LOCAL_STORAGE_KEYS.debugStore, data); - saveFn(LOCAL_STORAGE_KEYS.debugStoreStat, stat); + await saveFn(LOCAL_STORAGE_KEYS.debugStore, data); + await saveFn(LOCAL_STORAGE_KEYS.debugStoreStat, stat); lastMaterializedVersion = stat.commit; console.log('物化数据', data); } @@ -175,28 +175,31 @@ export function createDebugStore(storageSchema, contextBuilder, triggers, checke checkers.forEach(ele => store.registerChecker(ele)); assert(actionDict); // 如果没有物化数据则使用initialData初始化debugStore - const data = getMaterializedData(loadFn); - if (!data) { - initDataInStore(store, initialData); - console.log('使用初始化数据建立debugStore', initialData); - } - else { - // 对static的对象,使用initialData,剩下的使用物化数据 - for (const entity in initialData) { - if (storageSchema[entity].static) { - data.data[entity] = initialData[entity]; - } + const loadInitialData = async () => { + const data = await getMaterializedData(loadFn); + if (!data) { + initDataInStore(store, initialData); + console.log('使用初始化数据建立debugStore', initialData); } - initDataInStore(store, data.data, data.stat); - console.log('使用物化数据建立debugStore', data); - } + else { + // 对static的对象,使用initialData,剩下的使用物化数据 + for (const entity in initialData) { + if (storageSchema[entity].static) { + data.data[entity] = initialData[entity]; + } + } + initDataInStore(store, data.data, data.stat); + console.log('使用物化数据建立debugStore', data); + } + }; + loadInitialData(); lastMaterializedVersion = store.getStat().commit; // 当store中有更新事务提交时,物化store数据 - store.onCommit((result) => { + store.onCommit(async (result) => { if (Object.keys(result).length > 0) { const stat = store.getStat(); const data = store.getCurrentData(); - materializeData(data, stat, saveFn); + await materializeData(data, stat, saveFn); } }); // 启动watcher diff --git a/es/features/cache.d.ts b/es/features/cache.d.ts index c0da1851..810b7b92 100644 --- a/es/features/cache.d.ts +++ b/es/features/cache.d.ts @@ -30,7 +30,6 @@ export declare class Cache, aspectWrapper: AspectWrapper, frontendContextBuilder: () => (store: CacheStore) => FrontCxt, checkers: Array>, getFullData: () => any, localStorage: LocalStorage, savedEntities?: (keyof ED)[], keepFreshPeriod?: number); - private rebuildRefreshRows; /** * 处理cache中需要缓存的数据 */ diff --git a/es/features/cache.js b/es/features/cache.js index a7bd38d1..1d9ce878 100644 --- a/es/features/cache.js +++ b/es/features/cache.js @@ -30,42 +30,38 @@ export class Cache extends Feature { this.localStorage = localStorage; checkers.forEach((checker) => this.cacheStore.registerChecker(checker)); this.getFullDataFn = getFullData; + // 现在这个init变成了异步行为,不知道有没有影响。by Xc 20231126 this.initSavedLogic(); } - rebuildRefreshRows(entity, projection, result) { - const { data } = result; - const rows = []; - // 重新建立 - } /** * 处理cache中需要缓存的数据 */ - initSavedLogic() { + async initSavedLogic() { const data = {}; - this.savedEntities.forEach((entity) => { + await Promise.all(this.savedEntities.map(async (entity) => { // 加载缓存的数据项 const key = `${LOCAL_STORAGE_KEYS.cacheSaved}:${entity}`; - const cached = this.localStorage.load(key); + const cached = await this.localStorage.load(key); if (cached) { data[entity] = cached; } // 加载缓存的时间戳项 const key2 = `${LOCAL_STORAGE_KEYS.cacheRefreshRecord}:${entity}`; - const cachedTs = this.localStorage.load(key2); + const cachedTs = await this.localStorage.load(key2); if (cachedTs) { this.refreshRecords[entity] = cachedTs; } - }); + })); this.cacheStore.resetInitialData(data); - this.cacheStore.onCommit((result) => { + this.cacheStore.onCommit(async (result) => { const entities = Object.keys(result); const referenced = intersection(entities, this.savedEntities); if (referenced.length > 0) { const saved = this.cacheStore.getCurrentData(referenced); - Object.keys(saved).forEach((entity) => { + for (const entity in saved) { const key = `${LOCAL_STORAGE_KEYS.cacheSaved}:${entity}`; - this.localStorage.save(key, saved[entity]); - }); + await this.localStorage.save(key, saved[entity]); + } } }); } diff --git a/es/features/environment.d.ts b/es/features/environment.d.ts index 83b2c78b..e55fa4f7 100644 --- a/es/features/environment.d.ts +++ b/es/features/environment.d.ts @@ -1,9 +1,9 @@ import { Feature } from "../types/Feature"; -import { WebEnv, WechatMpEnv } from 'oak-domain/lib/types/Environment'; +import { NativeEnv, WebEnv, WechatMpEnv } from 'oak-domain/lib/types/Environment'; export declare class Environment extends Feature { - env?: WebEnv | WechatMpEnv; + env?: WebEnv | WechatMpEnv | NativeEnv; loading: boolean; constructor(); private initialize; - getEnv(): Promise; + getEnv(): Promise; } diff --git a/es/features/localStorage.d.ts b/es/features/localStorage.d.ts index e0681ec8..04aaebc4 100644 --- a/es/features/localStorage.d.ts +++ b/es/features/localStorage.d.ts @@ -2,12 +2,12 @@ import { Feature } from '../types/Feature'; export declare class LocalStorage extends Feature { keys: Record; constructor(); - setKey(key: string): void; - unsetKey(key: string): void; - save(key: string, item: any): void; - load(key: string): any; - clear(): void; - remove(key: string): void; - loadAll(): Record; - resetAll(data: Record): void; + private setKey; + private unsetKey; + save(key: string, item: any): Promise; + load(key: string): Promise; + clear(): Promise; + remove(key: string): Promise; + loadAll(): Promise>; + resetAll(data: Record): Promise; } diff --git a/es/features/localStorage.js b/es/features/localStorage.js index 0394c0f8..c0f4ef7d 100644 --- a/es/features/localStorage.js +++ b/es/features/localStorage.js @@ -26,7 +26,7 @@ export class LocalStorage extends Feature { unset(this.keys, key); } } - save(key, item) { + async save(key, item) { this.setKey(key); switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -42,7 +42,7 @@ export class LocalStorage extends Feature { } } } - load(key) { + async load(key) { this.setKey(key); switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -60,7 +60,7 @@ export class LocalStorage extends Feature { } } } - clear() { + async clear() { this.keys = {}; switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -76,7 +76,7 @@ export class LocalStorage extends Feature { } } } - remove(key) { + async remove(key) { this.unsetKey(key); switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -92,19 +92,19 @@ export class LocalStorage extends Feature { } } } - loadAll() { + async loadAll() { const data = {}; for (const k in this.keys) { Object.assign(data, { - [k]: this.load(k), + [k]: await this.load(k), }); } return data; } - resetAll(data) { + async resetAll(data) { this.clear(); for (const k in data) { - this.save(k, data[k]); + await this.save(k, data[k]); } } } diff --git a/es/features/localStorage.native.d.ts b/es/features/localStorage.native.d.ts new file mode 100644 index 00000000..40742a2d --- /dev/null +++ b/es/features/localStorage.native.d.ts @@ -0,0 +1,13 @@ +import { Feature } from '../types/Feature'; +export declare class LocalStorage extends Feature { + keys: Record; + constructor(); + setKey(key: string): void; + unsetKey(key: string): void; + save(key: string, item: any): Promise; + load(key: string): Promise; + clear(): Promise; + remove(key: string): Promise; + loadAll(): Promise>; + resetAll(data: Record): Promise; +} diff --git a/es/features/localStorage.native.js b/es/features/localStorage.native.js new file mode 100644 index 00000000..ac76f143 --- /dev/null +++ b/es/features/localStorage.native.js @@ -0,0 +1,67 @@ +import { unset } from 'oak-domain/lib/utils/lodash'; +import { LOCAL_STORAGE_KEYS } from '../constant/constant'; +import { Feature } from '../types/Feature'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +export class LocalStorage extends Feature { + keys; + constructor() { + super(); + if (process.env.NODE_ENV === 'development') { + // development环境下,debugStore的数据也默认存放在localStorage中 + this.keys = { + [LOCAL_STORAGE_KEYS.debugStore]: true, + [LOCAL_STORAGE_KEYS.debugStoreStat]: true, + }; + } + else { + this.keys = {}; + } + } + setKey(key) { + if (!this.keys[key]) { + this.keys[key] = true; + } + } + unsetKey(key) { + if (this.keys[key]) { + unset(this.keys, key); + } + } + async save(key, item) { + this.setKey(key); + await AsyncStorage.setItem(key, JSON.stringify(item)); + } + async load(key) { + this.setKey(key); + const value = await AsyncStorage.getItem(key); + if (value) { + return JSON.parse(value); + } + } + clear() { + return AsyncStorage.clear(); + } + remove(key) { + return AsyncStorage.removeItem(key); + } + async loadAll() { + const keys = await AsyncStorage.getAllKeys(); + const value = await AsyncStorage.multiGet(keys); + const result = {}; + value.forEach(([k, v]) => { + if (typeof v === 'string') { + result[k] = JSON.parse(v); + } + }); + return result; + } + resetAll(data) { + const value = []; + Object.keys(data).forEach((k) => { + if (data[k] !== undefined && data[k] !== null) { + value.push([k, JSON.stringify(data[k])]); + } + }); + return AsyncStorage.multiMerge(value); + } +} diff --git a/es/features/locales.d.ts b/es/features/locales.d.ts index 075360b9..296fd699 100644 --- a/es/features/locales.d.ts +++ b/es/features/locales.d.ts @@ -17,6 +17,7 @@ export declare class Locales, localStorage: LocalStorage, environment: Environment, defaultLng: string, makeBridgeUrlFn?: (url: string, headers?: Record) => string); private detectLanguange; private resetDataset; diff --git a/es/features/locales.js b/es/features/locales.js index 9bd081e1..740139cd 100644 --- a/es/features/locales.js +++ b/es/features/locales.js @@ -11,20 +11,24 @@ export class Locales extends Feature { language; defaultLng; i18n; + async initializeLng() { + const savedLng = await this.localStorage.load(LOCAL_STORAGE_KEYS.localeLng); + if (savedLng) { + this.language = savedLng; + } + else { + await this.detectLanguange(); + } + } constructor(cache, localStorage, environment, defaultLng, makeBridgeUrlFn) { super(); this.cache = cache; this.localStorage = localStorage; this.defaultLng = defaultLng; this.environment = environment; - const savedLng = localStorage.load(LOCAL_STORAGE_KEYS.localeLng); - if (savedLng) { - this.language = savedLng; - } - else { - this.language = defaultLng; - this.detectLanguange(); - } + this.language = defaultLng; + // 也是异步行为,不知道是否有影响 by Xc + this.initializeLng(); this.i18n = new I18n(undefined, { defaultLocale: defaultLng, locale: this.language, @@ -47,7 +51,7 @@ export class Locales extends Feature { const env = await this.environment.getEnv(); const { language } = env; this.language = language; - this.localStorage.save(LOCAL_STORAGE_KEYS.localeLng, language); + await this.localStorage.save(LOCAL_STORAGE_KEYS.localeLng, language); } resetDataset() { const i18ns = this.cache.get('i18n', { diff --git a/es/features/navigator.native.d.ts b/es/features/navigator.native.d.ts new file mode 100644 index 00000000..73438abc --- /dev/null +++ b/es/features/navigator.native.d.ts @@ -0,0 +1,24 @@ +import { Feature } from '../types/Feature'; +import { OakNavigateToParameters } from '../types/Page'; +import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; +import { EntityDict } from 'oak-domain/lib/types'; +export declare class Navigator extends Feature { + private namespace; + setNamespace(namespace: string): void; + getLocation(): void; + getNamespace(): void; + navigateTo(options: { + url: string; + } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean): Promise; + redirectTo(options: { + url: string; + } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean): Promise; + switchTab(options: { + url: string; + } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean): Promise; + navigateBack(delta?: number): Promise; + navigateBackOrRedirectTo(options: { + url: string; + isTabBar?: boolean; + } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean): void; +} diff --git a/es/features/navigator.native.js b/es/features/navigator.native.js new file mode 100644 index 00000000..59483652 --- /dev/null +++ b/es/features/navigator.native.js @@ -0,0 +1,29 @@ +import { Feature } from '../types/Feature'; +export class Navigator extends Feature { + namespace = ''; + setNamespace(namespace) { + this.namespace = namespace; + this.publish(); + } + getLocation() { + throw new Error('unimplemented'); + } + getNamespace() { + throw new Error('unimplemented'); + } + async navigateTo(options, state, disableNamespace) { + throw new Error('unimplemented'); + } + async redirectTo(options, state, disableNamespace) { + throw new Error('unimplemented'); + } + async switchTab(options, state, disableNamespace) { + throw new Error('unimplemented'); + } + async navigateBack(delta) { + throw new Error('unimplemented'); + } + navigateBackOrRedirectTo(options, state, disableNamespace) { + throw new Error('unimplemented'); + } +} diff --git a/es/page.mp.js b/es/page.mp.js index 73ec1be6..996dd63b 100644 --- a/es/page.mp.js +++ b/es/page.mp.js @@ -130,13 +130,16 @@ const oakBehavior = Behavior({ this.features.eventBus.unsubAll(type); }, save(key, item) { - this.features.localStorage.save(key, item); + return this.features.localStorage.save(key, item); }, load(key) { return this.features.localStorage.load(key); }, - clear() { - this.features.localStorage.clear(); + clear(key) { + if (key) { + return this.features.localStorage.remove(key); + } + return this.features.localStorage.clear(); }, setNotification(data) { this.features.notification.setNotification(data); diff --git a/es/page.native.d.ts b/es/page.native.d.ts index 7decf629..4f2d782e 100644 --- a/es/page.native.d.ts +++ b/es/page.native.d.ts @@ -1,3 +1,4 @@ +import React from 'react'; import { CommonAspectDict } from 'oak-common-aspect'; import { Aspect, EntityDict } from 'oak-domain/lib/types'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; @@ -6,4 +7,4 @@ import { Feature } from './types/Feature'; import { DataOption, OakComponentOption } from './types/Page'; import { SyncContext } from 'oak-domain/lib/store/SyncRowStore'; import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; -export declare function createComponent, FrontCxt extends SyncContext, AD extends Record>, FD extends Record, FormedData extends Record, TData extends Record = {}, TProperty extends DataOption = {}, TMethod extends Record = {}>(option: OakComponentOption, features: BasicFeatures> & FD): void; +export declare function createComponent, FrontCxt extends SyncContext, AD extends Record>, FD extends Record, FormedData extends Record, TData extends Record = {}, TProperty extends DataOption = {}, TMethod extends Record = {}>(option: OakComponentOption, features: BasicFeatures> & FD): React.ComponentType; diff --git a/es/page.native.js b/es/page.native.js index 495317fe..f2206b16 100644 --- a/es/page.native.js +++ b/es/page.native.js @@ -3,36 +3,10 @@ const DEFAULT_REACH_BOTTOM_DISTANCE = 50; export function createComponent(option, features) { const BaseComponent = createReactComponent(option, features); class Component extends BaseComponent { - scrollEvent = () => { - this.checkReachBottom(); - }; - registerPageScroll() { - window.addEventListener('scroll', this.scrollEvent); - } - unregisterPageScroll() { - window.removeEventListener('scroll', this.scrollEvent); - } - checkReachBottom() { - if (!this.supportPullDownRefresh()) { - return; - } - const isCurrentReachBottom = document.body.scrollHeight - - (window.innerHeight + window.scrollY) <= - DEFAULT_REACH_BOTTOM_DISTANCE; - if (!this.isReachBottom && isCurrentReachBottom && option.isList) { - this.isReachBottom = true; - // 执行触底事件 - this.loadMore(); - return; - } - this.isReachBottom = isCurrentReachBottom; - } async componentDidMount() { - this.registerPageScroll(); await super.componentDidMount(); } componentWillUnmount() { - this.unregisterPageScroll(); super.componentWillUnmount(); } render() { @@ -41,4 +15,5 @@ export function createComponent(option, features) { return Render; } } + return Component; } diff --git a/es/page.react.d.ts b/es/page.react.d.ts index e6aa00ac..28d3a950 100644 --- a/es/page.react.d.ts +++ b/es/page.react.d.ts @@ -31,9 +31,9 @@ export declare function createComponent; + load(key: string): Promise; + clear(key?: string | undefined): Promise; setNotification(data: NotificationProps): void; consumeNotification(): NotificationProps | undefined; setMessage(data: MessageProps): Promise; diff --git a/es/page.react.js b/es/page.react.js index 94e36626..e0935f10 100644 --- a/es/page.react.js +++ b/es/page.react.js @@ -20,13 +20,16 @@ class OakComponentBase extends React.PureComponent { this.features.eventBus.unsubAll(type); } save(key, item) { - this.features.localStorage.save(key, item); + return this.features.localStorage.save(key, item); } load(key) { return this.features.localStorage.load(key); } - clear() { - this.features.localStorage.clear(); + clear(key) { + if (key) { + return this.features.localStorage.remove(key); + } + return this.features.localStorage.clear(); } setNotification(data) { this.features.notification.setNotification(data); diff --git a/es/types/Initialize.d.ts b/es/types/Initialize.d.ts index 52941701..4974687a 100644 --- a/es/types/Initialize.d.ts +++ b/es/types/Initialize.d.ts @@ -1,4 +1,4 @@ -import { AsyncContext } from 'oak-domain'; +import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { ActionDictOfEntityDict, ColorDict, Importation, Exportation } from 'oak-domain/lib/types'; import { AuthDeduceRelationMap, EntityDict } from 'oak-domain/lib/types/Entity'; diff --git a/es/types/Page.d.ts b/es/types/Page.d.ts index 3910684f..da3e58d2 100644 --- a/es/types/Page.d.ts +++ b/es/types/Page.d.ts @@ -138,9 +138,9 @@ export type OakCommonComponentMethods void; pubEvent: (type: string, options?: any) => void; unsubAllEvents: (type: string) => void; - save: (key: string, item: any) => void; - load: (key: string) => any; - clear: () => void; + save: (key: string, item: any) => Promise; + load: (key: string) => Promise; + clear: (key?: string) => Promise; setNotification: (data: NotificationProps) => void; consumeNotification: () => NotificationProps | undefined; setMessage: (data: MessageProps) => void; diff --git a/es/utils/env/env.native.d.ts b/es/utils/env/env.native.d.ts new file mode 100644 index 00000000..232da621 --- /dev/null +++ b/es/utils/env/env.native.d.ts @@ -0,0 +1,2 @@ +import { NativeEnv } from 'oak-domain/lib/types/Environment'; +export declare function getEnv(): Promise; diff --git a/es/utils/env/env.native.js b/es/utils/env/env.native.js new file mode 100644 index 00000000..539677cd --- /dev/null +++ b/es/utils/env/env.native.js @@ -0,0 +1,9 @@ +import { Platform } from 'react-native'; +import { getLocales } from 'react-native-localize'; +export async function getEnv() { + const language = getLocales()[0].languageTag; + return { + ...Platform, + language, + }; +} diff --git a/lib/debugStore/DebugStore.d.ts b/lib/debugStore/DebugStore.d.ts index 7599e026..1f880c46 100644 --- a/lib/debugStore/DebugStore.d.ts +++ b/lib/debugStore/DebugStore.d.ts @@ -12,6 +12,9 @@ interface DebugStoreSelectOption extends TreeStoreSelectOption { export declare class DebugStore> extends TreeStore implements AsyncRowStore { private executor; private relationAuth; + private dataLoaded; + private dataLoadedLock; + private dataLoadedLockUnlocker; constructor(storageSchema: StorageSchema, contextBuilder: (cxtString?: string) => (store: DebugStore) => Promise, authDeduceRelationMap: AuthDeduceRelationMap, selectFreeEntities?: (keyof ED)[], updateFreeDict?: { [A in keyof ED]?: string[]; }); @@ -26,5 +29,13 @@ export declare class DebugStore(entity: T, selection: Pick, context: Cxt, option: OP): Promise; registerTrigger(trigger: Trigger): void; registerChecker(checker: Checker): void; + resetInitialData(initialData: { + [T in keyof ED]?: Array; + }, stat?: { + create: number; + update: number; + remove: number; + commit: number; + }): void; } export {}; diff --git a/lib/debugStore/DebugStore.js b/lib/debugStore/DebugStore.js index e6b9fd65..1f2a8cc6 100644 --- a/lib/debugStore/DebugStore.js +++ b/lib/debugStore/DebugStore.js @@ -9,8 +9,15 @@ const RelationAuth_1 = require("oak-domain/lib/store/RelationAuth"); class DebugStore extends oak_memory_tree_store_1.TreeStore { executor; relationAuth; + dataLoaded; + dataLoadedLock; + dataLoadedLockUnlocker = () => undefined; constructor(storageSchema, contextBuilder, authDeduceRelationMap, selectFreeEntities, updateFreeDict) { super(storageSchema); + this.dataLoaded = false; + this.dataLoadedLock = new Promise((resolve) => { + this.dataLoadedLockUnlocker = () => resolve(); + }); this.executor = new TriggerExecutor_1.TriggerExecutor((cxtString) => contextBuilder(cxtString)(this)); this.relationAuth = new RelationAuth_1.RelationAuth(storageSchema, authDeduceRelationMap, selectFreeEntities, updateFreeDict); } @@ -41,6 +48,9 @@ class DebugStore extends oak_memory_tree_store_1.TreeStore { return result; } async operate(entity, operation, context, option) { + if (!this.dataLoaded) { + await this.dataLoadedLock; + } const autoCommit = !context.getCurrentTxnId(); let result; if (autoCommit) { @@ -66,6 +76,9 @@ class DebugStore extends oak_memory_tree_store_1.TreeStore { return result; } async select(entity, selection, context, option) { + if (!this.dataLoaded) { + await this.dataLoadedLock; + } const autoCommit = !context.getCurrentTxnId(); if (autoCommit) { await context.begin(); @@ -98,6 +111,9 @@ class DebugStore extends oak_memory_tree_store_1.TreeStore { } } async count(entity, selection, context, option) { + if (!this.dataLoaded) { + await this.dataLoadedLock; + } return super.countAsync(entity, selection, context, option); } registerTrigger(trigger) { @@ -106,5 +122,10 @@ class DebugStore extends oak_memory_tree_store_1.TreeStore { registerChecker(checker) { this.executor.registerChecker(checker); } + resetInitialData(initialData, stat) { + super.resetInitialData(initialData, stat); + this.dataLoaded = true; + this.dataLoadedLockUnlocker(); + } } exports.DebugStore = DebugStore; diff --git a/lib/debugStore/index.d.ts b/lib/debugStore/index.d.ts index 18bb4f7e..85e43230 100644 --- a/lib/debugStore/index.d.ts +++ b/lib/debugStore/index.d.ts @@ -5,6 +5,6 @@ import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; export declare function clearMaterializedData(): void; export declare function createDebugStore>(storageSchema: StorageSchema, contextBuilder: (cxtString?: string) => (store: DebugStore) => Promise, triggers: Array>, checkers: Array>, watchers: Array>, timers: Array>, startRoutines: Array>, initialData: { [T in keyof ED]?: Array; -}, actionDict: ActionDictOfEntityDict, authDeduceRelationMap: AuthDeduceRelationMap, saveFn: (key: string, data: any) => void, loadFn: (key: string) => any, selectFreeEntities?: (keyof ED)[], updateFreeDict?: { +}, actionDict: ActionDictOfEntityDict, authDeduceRelationMap: AuthDeduceRelationMap, saveFn: (key: string, data: any) => Promise, loadFn: (key: string) => Promise, selectFreeEntities?: (keyof ED)[], updateFreeDict?: { [A in keyof ED]?: string[]; }): DebugStore; diff --git a/lib/debugStore/index.js b/lib/debugStore/index.js index a8cf3357..269ead9b 100644 --- a/lib/debugStore/index.js +++ b/lib/debugStore/index.js @@ -9,10 +9,10 @@ const uuid_1 = require("oak-domain/lib/utils/uuid"); async function initDataInStore(store, initialData, stat) { store.resetInitialData(initialData, stat); } -function getMaterializedData(loadFn) { +async function getMaterializedData(loadFn) { try { - const data = loadFn(constant_1.LOCAL_STORAGE_KEYS.debugStore); - const stat = loadFn(constant_1.LOCAL_STORAGE_KEYS.debugStoreStat); + const data = await loadFn(constant_1.LOCAL_STORAGE_KEYS.debugStore); + const stat = await loadFn(constant_1.LOCAL_STORAGE_KEYS.debugStoreStat); if (data && stat) { return { data, @@ -26,10 +26,10 @@ function getMaterializedData(loadFn) { } } let lastMaterializedVersion = 0; -function materializeData(data, stat, saveFn) { +async function materializeData(data, stat, saveFn) { try { - saveFn(constant_1.LOCAL_STORAGE_KEYS.debugStore, data); - saveFn(constant_1.LOCAL_STORAGE_KEYS.debugStoreStat, stat); + await saveFn(constant_1.LOCAL_STORAGE_KEYS.debugStore, data); + await saveFn(constant_1.LOCAL_STORAGE_KEYS.debugStoreStat, stat); lastMaterializedVersion = stat.commit; console.log('物化数据', data); } @@ -179,28 +179,31 @@ function createDebugStore(storageSchema, contextBuilder, triggers, checkers, wat checkers.forEach(ele => store.registerChecker(ele)); (0, assert_1.assert)(actionDict); // 如果没有物化数据则使用initialData初始化debugStore - const data = getMaterializedData(loadFn); - if (!data) { - initDataInStore(store, initialData); - console.log('使用初始化数据建立debugStore', initialData); - } - else { - // 对static的对象,使用initialData,剩下的使用物化数据 - for (const entity in initialData) { - if (storageSchema[entity].static) { - data.data[entity] = initialData[entity]; - } + const loadInitialData = async () => { + const data = await getMaterializedData(loadFn); + if (!data) { + initDataInStore(store, initialData); + console.log('使用初始化数据建立debugStore', initialData); } - initDataInStore(store, data.data, data.stat); - console.log('使用物化数据建立debugStore', data); - } + else { + // 对static的对象,使用initialData,剩下的使用物化数据 + for (const entity in initialData) { + if (storageSchema[entity].static) { + data.data[entity] = initialData[entity]; + } + } + initDataInStore(store, data.data, data.stat); + console.log('使用物化数据建立debugStore', data); + } + }; + loadInitialData(); lastMaterializedVersion = store.getStat().commit; // 当store中有更新事务提交时,物化store数据 - store.onCommit((result) => { + store.onCommit(async (result) => { if (Object.keys(result).length > 0) { const stat = store.getStat(); const data = store.getCurrentData(); - materializeData(data, stat, saveFn); + await materializeData(data, stat, saveFn); } }); // 启动watcher diff --git a/lib/features/cache.d.ts b/lib/features/cache.d.ts index c0da1851..810b7b92 100644 --- a/lib/features/cache.d.ts +++ b/lib/features/cache.d.ts @@ -30,7 +30,6 @@ export declare class Cache, aspectWrapper: AspectWrapper, frontendContextBuilder: () => (store: CacheStore) => FrontCxt, checkers: Array>, getFullData: () => any, localStorage: LocalStorage, savedEntities?: (keyof ED)[], keepFreshPeriod?: number); - private rebuildRefreshRows; /** * 处理cache中需要缓存的数据 */ diff --git a/lib/features/cache.js b/lib/features/cache.js index eb930632..b460893c 100644 --- a/lib/features/cache.js +++ b/lib/features/cache.js @@ -33,42 +33,38 @@ class Cache extends Feature_1.Feature { this.localStorage = localStorage; checkers.forEach((checker) => this.cacheStore.registerChecker(checker)); this.getFullDataFn = getFullData; + // 现在这个init变成了异步行为,不知道有没有影响。by Xc 20231126 this.initSavedLogic(); } - rebuildRefreshRows(entity, projection, result) { - const { data } = result; - const rows = []; - // 重新建立 - } /** * 处理cache中需要缓存的数据 */ - initSavedLogic() { + async initSavedLogic() { const data = {}; - this.savedEntities.forEach((entity) => { + await Promise.all(this.savedEntities.map(async (entity) => { // 加载缓存的数据项 const key = `${constant_1.LOCAL_STORAGE_KEYS.cacheSaved}:${entity}`; - const cached = this.localStorage.load(key); + const cached = await this.localStorage.load(key); if (cached) { data[entity] = cached; } // 加载缓存的时间戳项 const key2 = `${constant_1.LOCAL_STORAGE_KEYS.cacheRefreshRecord}:${entity}`; - const cachedTs = this.localStorage.load(key2); + const cachedTs = await this.localStorage.load(key2); if (cachedTs) { this.refreshRecords[entity] = cachedTs; } - }); + })); this.cacheStore.resetInitialData(data); - this.cacheStore.onCommit((result) => { + this.cacheStore.onCommit(async (result) => { const entities = Object.keys(result); const referenced = (0, lodash_1.intersection)(entities, this.savedEntities); if (referenced.length > 0) { const saved = this.cacheStore.getCurrentData(referenced); - Object.keys(saved).forEach((entity) => { + for (const entity in saved) { const key = `${constant_1.LOCAL_STORAGE_KEYS.cacheSaved}:${entity}`; - this.localStorage.save(key, saved[entity]); - }); + await this.localStorage.save(key, saved[entity]); + } } }); } diff --git a/lib/features/environment.d.ts b/lib/features/environment.d.ts index 83b2c78b..e55fa4f7 100644 --- a/lib/features/environment.d.ts +++ b/lib/features/environment.d.ts @@ -1,9 +1,9 @@ import { Feature } from "../types/Feature"; -import { WebEnv, WechatMpEnv } from 'oak-domain/lib/types/Environment'; +import { NativeEnv, WebEnv, WechatMpEnv } from 'oak-domain/lib/types/Environment'; export declare class Environment extends Feature { - env?: WebEnv | WechatMpEnv; + env?: WebEnv | WechatMpEnv | NativeEnv; loading: boolean; constructor(); private initialize; - getEnv(): Promise; + getEnv(): Promise; } diff --git a/lib/features/localStorage.d.ts b/lib/features/localStorage.d.ts index e0681ec8..04aaebc4 100644 --- a/lib/features/localStorage.d.ts +++ b/lib/features/localStorage.d.ts @@ -2,12 +2,12 @@ import { Feature } from '../types/Feature'; export declare class LocalStorage extends Feature { keys: Record; constructor(); - setKey(key: string): void; - unsetKey(key: string): void; - save(key: string, item: any): void; - load(key: string): any; - clear(): void; - remove(key: string): void; - loadAll(): Record; - resetAll(data: Record): void; + private setKey; + private unsetKey; + save(key: string, item: any): Promise; + load(key: string): Promise; + clear(): Promise; + remove(key: string): Promise; + loadAll(): Promise>; + resetAll(data: Record): Promise; } diff --git a/lib/features/localStorage.js b/lib/features/localStorage.js index 0b398b88..15840bb2 100644 --- a/lib/features/localStorage.js +++ b/lib/features/localStorage.js @@ -29,7 +29,7 @@ class LocalStorage extends Feature_1.Feature { (0, lodash_1.unset)(this.keys, key); } } - save(key, item) { + async save(key, item) { this.setKey(key); switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -45,7 +45,7 @@ class LocalStorage extends Feature_1.Feature { } } } - load(key) { + async load(key) { this.setKey(key); switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -63,7 +63,7 @@ class LocalStorage extends Feature_1.Feature { } } } - clear() { + async clear() { this.keys = {}; switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -79,7 +79,7 @@ class LocalStorage extends Feature_1.Feature { } } } - remove(key) { + async remove(key) { this.unsetKey(key); switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -95,19 +95,19 @@ class LocalStorage extends Feature_1.Feature { } } } - loadAll() { + async loadAll() { const data = {}; for (const k in this.keys) { Object.assign(data, { - [k]: this.load(k), + [k]: await this.load(k), }); } return data; } - resetAll(data) { + async resetAll(data) { this.clear(); for (const k in data) { - this.save(k, data[k]); + await this.save(k, data[k]); } } } diff --git a/lib/features/localStorage.native.d.ts b/lib/features/localStorage.native.d.ts new file mode 100644 index 00000000..40742a2d --- /dev/null +++ b/lib/features/localStorage.native.d.ts @@ -0,0 +1,13 @@ +import { Feature } from '../types/Feature'; +export declare class LocalStorage extends Feature { + keys: Record; + constructor(); + setKey(key: string): void; + unsetKey(key: string): void; + save(key: string, item: any): Promise; + load(key: string): Promise; + clear(): Promise; + remove(key: string): Promise; + loadAll(): Promise>; + resetAll(data: Record): Promise; +} diff --git a/lib/features/localStorage.native.js b/lib/features/localStorage.native.js new file mode 100644 index 00000000..bd8d84d9 --- /dev/null +++ b/lib/features/localStorage.native.js @@ -0,0 +1,72 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LocalStorage = void 0; +const tslib_1 = require("tslib"); +const lodash_1 = require("oak-domain/lib/utils/lodash"); +const constant_1 = require("../constant/constant"); +const Feature_1 = require("../types/Feature"); +const async_storage_1 = tslib_1.__importDefault(require("@react-native-async-storage/async-storage")); +class LocalStorage extends Feature_1.Feature { + keys; + constructor() { + super(); + if (process.env.NODE_ENV === 'development') { + // development环境下,debugStore的数据也默认存放在localStorage中 + this.keys = { + [constant_1.LOCAL_STORAGE_KEYS.debugStore]: true, + [constant_1.LOCAL_STORAGE_KEYS.debugStoreStat]: true, + }; + } + else { + this.keys = {}; + } + } + setKey(key) { + if (!this.keys[key]) { + this.keys[key] = true; + } + } + unsetKey(key) { + if (this.keys[key]) { + (0, lodash_1.unset)(this.keys, key); + } + } + async save(key, item) { + this.setKey(key); + await async_storage_1.default.setItem(key, JSON.stringify(item)); + } + async load(key) { + this.setKey(key); + const value = await async_storage_1.default.getItem(key); + if (value) { + return JSON.parse(value); + } + } + clear() { + return async_storage_1.default.clear(); + } + remove(key) { + return async_storage_1.default.removeItem(key); + } + async loadAll() { + const keys = await async_storage_1.default.getAllKeys(); + const value = await async_storage_1.default.multiGet(keys); + const result = {}; + value.forEach(([k, v]) => { + if (typeof v === 'string') { + result[k] = JSON.parse(v); + } + }); + return result; + } + resetAll(data) { + const value = []; + Object.keys(data).forEach((k) => { + if (data[k] !== undefined && data[k] !== null) { + value.push([k, JSON.stringify(data[k])]); + } + }); + return async_storage_1.default.multiMerge(value); + } +} +exports.LocalStorage = LocalStorage; diff --git a/lib/features/locales.d.ts b/lib/features/locales.d.ts index 075360b9..296fd699 100644 --- a/lib/features/locales.d.ts +++ b/lib/features/locales.d.ts @@ -17,6 +17,7 @@ export declare class Locales, localStorage: LocalStorage, environment: Environment, defaultLng: string, makeBridgeUrlFn?: (url: string, headers?: Record) => string); private detectLanguange; private resetDataset; diff --git a/lib/features/locales.js b/lib/features/locales.js index fb133cf6..9b57160d 100644 --- a/lib/features/locales.js +++ b/lib/features/locales.js @@ -14,20 +14,24 @@ class Locales extends Feature_1.Feature { language; defaultLng; i18n; + async initializeLng() { + const savedLng = await this.localStorage.load(constant_1.LOCAL_STORAGE_KEYS.localeLng); + if (savedLng) { + this.language = savedLng; + } + else { + await this.detectLanguange(); + } + } constructor(cache, localStorage, environment, defaultLng, makeBridgeUrlFn) { super(); this.cache = cache; this.localStorage = localStorage; this.defaultLng = defaultLng; this.environment = environment; - const savedLng = localStorage.load(constant_1.LOCAL_STORAGE_KEYS.localeLng); - if (savedLng) { - this.language = savedLng; - } - else { - this.language = defaultLng; - this.detectLanguange(); - } + this.language = defaultLng; + // 也是异步行为,不知道是否有影响 by Xc + this.initializeLng(); this.i18n = new i18n_js_1.I18n(undefined, { defaultLocale: defaultLng, locale: this.language, @@ -50,7 +54,7 @@ class Locales extends Feature_1.Feature { const env = await this.environment.getEnv(); const { language } = env; this.language = language; - this.localStorage.save(constant_1.LOCAL_STORAGE_KEYS.localeLng, language); + await this.localStorage.save(constant_1.LOCAL_STORAGE_KEYS.localeLng, language); } resetDataset() { const i18ns = this.cache.get('i18n', { diff --git a/lib/features/navigator.native.d.ts b/lib/features/navigator.native.d.ts new file mode 100644 index 00000000..73438abc --- /dev/null +++ b/lib/features/navigator.native.d.ts @@ -0,0 +1,24 @@ +import { Feature } from '../types/Feature'; +import { OakNavigateToParameters } from '../types/Page'; +import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; +import { EntityDict } from 'oak-domain/lib/types'; +export declare class Navigator extends Feature { + private namespace; + setNamespace(namespace: string): void; + getLocation(): void; + getNamespace(): void; + navigateTo(options: { + url: string; + } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean): Promise; + redirectTo(options: { + url: string; + } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean): Promise; + switchTab(options: { + url: string; + } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean): Promise; + navigateBack(delta?: number): Promise; + navigateBackOrRedirectTo(options: { + url: string; + isTabBar?: boolean; + } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean): void; +} diff --git a/lib/features/navigator.native.js b/lib/features/navigator.native.js new file mode 100644 index 00000000..5addb026 --- /dev/null +++ b/lib/features/navigator.native.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Navigator = void 0; +const Feature_1 = require("../types/Feature"); +class Navigator extends Feature_1.Feature { + namespace = ''; + setNamespace(namespace) { + this.namespace = namespace; + this.publish(); + } + getLocation() { + throw new Error('unimplemented'); + } + getNamespace() { + throw new Error('unimplemented'); + } + async navigateTo(options, state, disableNamespace) { + throw new Error('unimplemented'); + } + async redirectTo(options, state, disableNamespace) { + throw new Error('unimplemented'); + } + async switchTab(options, state, disableNamespace) { + throw new Error('unimplemented'); + } + async navigateBack(delta) { + throw new Error('unimplemented'); + } + navigateBackOrRedirectTo(options, state, disableNamespace) { + throw new Error('unimplemented'); + } +} +exports.Navigator = Navigator; diff --git a/lib/page.mp.js b/lib/page.mp.js index 23059077..457fef42 100644 --- a/lib/page.mp.js +++ b/lib/page.mp.js @@ -133,13 +133,16 @@ const oakBehavior = Behavior({ this.features.eventBus.unsubAll(type); }, save(key, item) { - this.features.localStorage.save(key, item); + return this.features.localStorage.save(key, item); }, load(key) { return this.features.localStorage.load(key); }, - clear() { - this.features.localStorage.clear(); + clear(key) { + if (key) { + return this.features.localStorage.remove(key); + } + return this.features.localStorage.clear(); }, setNotification(data) { this.features.notification.setNotification(data); diff --git a/lib/page.native.d.ts b/lib/page.native.d.ts index 7decf629..4f2d782e 100644 --- a/lib/page.native.d.ts +++ b/lib/page.native.d.ts @@ -1,3 +1,4 @@ +import React from 'react'; import { CommonAspectDict } from 'oak-common-aspect'; import { Aspect, EntityDict } from 'oak-domain/lib/types'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; @@ -6,4 +7,4 @@ import { Feature } from './types/Feature'; import { DataOption, OakComponentOption } from './types/Page'; import { SyncContext } from 'oak-domain/lib/store/SyncRowStore'; import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; -export declare function createComponent, FrontCxt extends SyncContext, AD extends Record>, FD extends Record, FormedData extends Record, TData extends Record = {}, TProperty extends DataOption = {}, TMethod extends Record = {}>(option: OakComponentOption, features: BasicFeatures> & FD): void; +export declare function createComponent, FrontCxt extends SyncContext, AD extends Record>, FD extends Record, FormedData extends Record, TData extends Record = {}, TProperty extends DataOption = {}, TMethod extends Record = {}>(option: OakComponentOption, features: BasicFeatures> & FD): React.ComponentType; diff --git a/lib/page.native.js b/lib/page.native.js index 20b95ff9..b7c1834b 100644 --- a/lib/page.native.js +++ b/lib/page.native.js @@ -6,36 +6,10 @@ const DEFAULT_REACH_BOTTOM_DISTANCE = 50; function createComponent(option, features) { const BaseComponent = (0, page_react_1.createComponent)(option, features); class Component extends BaseComponent { - scrollEvent = () => { - this.checkReachBottom(); - }; - registerPageScroll() { - window.addEventListener('scroll', this.scrollEvent); - } - unregisterPageScroll() { - window.removeEventListener('scroll', this.scrollEvent); - } - checkReachBottom() { - if (!this.supportPullDownRefresh()) { - return; - } - const isCurrentReachBottom = document.body.scrollHeight - - (window.innerHeight + window.scrollY) <= - DEFAULT_REACH_BOTTOM_DISTANCE; - if (!this.isReachBottom && isCurrentReachBottom && option.isList) { - this.isReachBottom = true; - // 执行触底事件 - this.loadMore(); - return; - } - this.isReachBottom = isCurrentReachBottom; - } async componentDidMount() { - this.registerPageScroll(); await super.componentDidMount(); } componentWillUnmount() { - this.unregisterPageScroll(); super.componentWillUnmount(); } render() { @@ -44,5 +18,6 @@ function createComponent(option, features) { return Render; } } + return Component; } exports.createComponent = createComponent; diff --git a/lib/page.react.d.ts b/lib/page.react.d.ts index e6aa00ac..28d3a950 100644 --- a/lib/page.react.d.ts +++ b/lib/page.react.d.ts @@ -31,9 +31,9 @@ export declare function createComponent; + load(key: string): Promise; + clear(key?: string | undefined): Promise; setNotification(data: NotificationProps): void; consumeNotification(): NotificationProps | undefined; setMessage(data: MessageProps): Promise; diff --git a/lib/page.react.js b/lib/page.react.js index 0cfb6d8d..053a3914 100644 --- a/lib/page.react.js +++ b/lib/page.react.js @@ -25,13 +25,16 @@ class OakComponentBase extends react_1.default.PureComponent { this.features.eventBus.unsubAll(type); } save(key, item) { - this.features.localStorage.save(key, item); + return this.features.localStorage.save(key, item); } load(key) { return this.features.localStorage.load(key); } - clear() { - this.features.localStorage.clear(); + clear(key) { + if (key) { + return this.features.localStorage.remove(key); + } + return this.features.localStorage.clear(); } setNotification(data) { this.features.notification.setNotification(data); diff --git a/lib/types/Initialize.d.ts b/lib/types/Initialize.d.ts index 52941701..4974687a 100644 --- a/lib/types/Initialize.d.ts +++ b/lib/types/Initialize.d.ts @@ -1,4 +1,4 @@ -import { AsyncContext } from 'oak-domain'; +import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { ActionDictOfEntityDict, ColorDict, Importation, Exportation } from 'oak-domain/lib/types'; import { AuthDeduceRelationMap, EntityDict } from 'oak-domain/lib/types/Entity'; diff --git a/lib/types/Page.d.ts b/lib/types/Page.d.ts index 3910684f..da3e58d2 100644 --- a/lib/types/Page.d.ts +++ b/lib/types/Page.d.ts @@ -138,9 +138,9 @@ export type OakCommonComponentMethods void; pubEvent: (type: string, options?: any) => void; unsubAllEvents: (type: string) => void; - save: (key: string, item: any) => void; - load: (key: string) => any; - clear: () => void; + save: (key: string, item: any) => Promise; + load: (key: string) => Promise; + clear: (key?: string) => Promise; setNotification: (data: NotificationProps) => void; consumeNotification: () => NotificationProps | undefined; setMessage: (data: MessageProps) => void; diff --git a/lib/utils/env/env.native.d.ts b/lib/utils/env/env.native.d.ts new file mode 100644 index 00000000..232da621 --- /dev/null +++ b/lib/utils/env/env.native.d.ts @@ -0,0 +1,2 @@ +import { NativeEnv } from 'oak-domain/lib/types/Environment'; +export declare function getEnv(): Promise; diff --git a/lib/utils/env/env.native.js b/lib/utils/env/env.native.js new file mode 100644 index 00000000..53cb2d23 --- /dev/null +++ b/lib/utils/env/env.native.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getEnv = void 0; +const react_native_1 = require("react-native"); +const react_native_localize_1 = require("react-native-localize"); +async function getEnv() { + const language = (0, react_native_localize_1.getLocales)()[0].languageTag; + return { + ...react_native_1.Platform, + language, + }; +} +exports.getEnv = getEnv; diff --git a/package.json b/package.json index 8d90cb05..6fca7af8 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ ], "dependencies": { "@fingerprintjs/fingerprintjs": "^4.0.0", + "@react-native-async-storage/async-storage": "^1.19.8", "dayjs": "^1.11.9", "echarts": "^5.4.2", "echarts-for-react": "^3.0.2", @@ -23,6 +24,7 @@ "oak-domain": "file:../oak-domain", "oak-memory-tree-store": "file:../oak-memory-tree-store", "ol": "^7.3.0", + "react-native-localize": "^3.0.4", "react-responsive": "^9.0.2", "rmc-pull-to-refresh": "^1.0.13", "socket.io-client": "^4.7.2", diff --git a/src/debugStore/DebugStore.ts b/src/debugStore/DebugStore.ts index 62db0468..2872f619 100644 --- a/src/debugStore/DebugStore.ts +++ b/src/debugStore/DebugStore.ts @@ -18,6 +18,9 @@ interface DebugStoreSelectOption extends TreeStoreSelectOption { export class DebugStore> extends TreeStore implements AsyncRowStore { private executor: TriggerExecutor; private relationAuth: RelationAuth; + private dataLoaded: boolean; + private dataLoadedLock: Promise; + private dataLoadedLockUnlocker = () => undefined as void; constructor( storageSchema: StorageSchema, @@ -29,6 +32,12 @@ export class DebugStore { + this.dataLoadedLockUnlocker = () => resolve(); + } + ); this.executor = new TriggerExecutor((cxtString) => contextBuilder(cxtString)(this)); this.relationAuth = new RelationAuth(storageSchema, authDeduceRelationMap, selectFreeEntities, updateFreeDict); } @@ -68,6 +77,9 @@ export class DebugStore(entity: T, selection: Pick, context: Cxt, option: OP): Promise { + if (!this.dataLoaded) { + await this.dataLoadedLock; + } return super.countAsync(entity, selection, context, option); } @@ -145,5 +163,18 @@ export class DebugStore(checker: Checker) { this.executor.registerChecker(checker); } + + resetInitialData(initialData: { + [T in keyof ED]?: Array; + }, stat?: { + create: number; + update: number; + remove: number; + commit: number; + }) { + super.resetInitialData(initialData, stat); + this.dataLoaded = true; + this.dataLoadedLockUnlocker(); + } } diff --git a/src/debugStore/index.ts b/src/debugStore/index.ts index cbdfb9a4..0ce75839 100644 --- a/src/debugStore/index.ts +++ b/src/debugStore/index.ts @@ -23,10 +23,10 @@ async function initDataInStore any) { +async function getMaterializedData(loadFn: (key: string) => Promise) { try { - const data = loadFn(LOCAL_STORAGE_KEYS.debugStore); - const stat = loadFn(LOCAL_STORAGE_KEYS.debugStoreStat); + const data = await loadFn(LOCAL_STORAGE_KEYS.debugStore); + const stat = await loadFn(LOCAL_STORAGE_KEYS.debugStoreStat); if (data && stat) { return { data, @@ -41,13 +41,13 @@ function getMaterializedData(loadFn: (key: string) => any) { let lastMaterializedVersion = 0; -function materializeData( +async function materializeData( data: any, stat: { create: number, update: number, remove: number, commit: number }, - saveFn: (key: string, data: any) => void) { + saveFn: (key: string, data: any) => Promise) { try { - saveFn(LOCAL_STORAGE_KEYS.debugStore, data); - saveFn(LOCAL_STORAGE_KEYS.debugStoreStat, stat); + await saveFn(LOCAL_STORAGE_KEYS.debugStore, data); + await saveFn(LOCAL_STORAGE_KEYS.debugStoreStat, stat); lastMaterializedVersion = stat.commit; console.log('物化数据', data); } catch (e) { @@ -215,8 +215,8 @@ export function createDebugStore, authDeduceRelationMap: AuthDeduceRelationMap, - saveFn: (key: string, data: any) => void, - loadFn: (key: string) => any, + saveFn: (key: string, data: any) => Promise, + loadFn: (key: string) => Promise, selectFreeEntities?: (keyof ED)[], updateFreeDict?: { [A in keyof ED]?: string[]; @@ -234,31 +234,36 @@ export function createDebugStore { + const data = await getMaterializedData(loadFn); + if (!data) { + initDataInStore(store, initialData!); + console.log('使用初始化数据建立debugStore', initialData); } - initDataInStore(store, data.data, data.stat); - console.log('使用物化数据建立debugStore', data); - } + else { + // 对static的对象,使用initialData,剩下的使用物化数据 + for (const entity in initialData) { + if (storageSchema[entity].static) { + data.data[entity] = initialData[entity]; + } + } + initDataInStore(store, data.data, data.stat); + console.log('使用物化数据建立debugStore', data); + } + }; + loadInitialData(); lastMaterializedVersion = store.getStat().commit; // 当store中有更新事务提交时,物化store数据 - store.onCommit((result) => { - if (Object.keys(result).length > 0) { - const stat = store.getStat(); - const data = store.getCurrentData(); - materializeData(data, stat, saveFn); + store.onCommit( + async (result) => { + if (Object.keys(result).length > 0) { + const stat = store.getStat(); + const data = store.getCurrentData(); + await materializeData(data, stat, saveFn); + } } - }); + ); // 启动watcher initializeWatchers(store, contextBuilder, watchers); diff --git a/src/features/cache.ts b/src/features/cache.ts index dfc8a7de..eaa3e091 100644 --- a/src/features/cache.ts +++ b/src/features/cache.ts @@ -75,55 +75,52 @@ export class Cache< ); this.getFullDataFn = getFullData; + + // 现在这个init变成了异步行为,不知道有没有影响。by Xc 20231126 this.initSavedLogic(); } - private rebuildRefreshRows(entity: T, projection: ED[T]['Selection']['data'], result: Awaited>) { - const { data } = result; - const rows = [] as Partial[]; - - // 重新建立 - } - /** * 处理cache中需要缓存的数据 */ - private initSavedLogic() { + private async initSavedLogic() { const data: { [T in keyof ED]?: ED[T]['OpSchema'][]; } = {}; - this.savedEntities.forEach( - (entity) => { - // 加载缓存的数据项 - const key = `${LOCAL_STORAGE_KEYS.cacheSaved}:${entity as string}`; - const cached = this.localStorage.load(key); - if (cached) { - data[entity] = cached; - } + await Promise.all( + this.savedEntities.map( + async (entity) => { + // 加载缓存的数据项 + const key = `${LOCAL_STORAGE_KEYS.cacheSaved}:${entity as string}`; + const cached = await this.localStorage.load(key); + if (cached) { + data[entity] = cached; + } - // 加载缓存的时间戳项 - const key2 = `${LOCAL_STORAGE_KEYS.cacheRefreshRecord}:${entity as string}`; - const cachedTs = this.localStorage.load(key2); - if (cachedTs) { - this.refreshRecords[entity] = cachedTs; + // 加载缓存的时间戳项 + const key2 = `${LOCAL_STORAGE_KEYS.cacheRefreshRecord}:${entity as string}`; + const cachedTs = await this.localStorage.load(key2); + if (cachedTs) { + this.refreshRecords[entity] = cachedTs; + } + } + ) + ); + this.cacheStore.resetInitialData(data); + this.cacheStore.onCommit( + async (result) => { + const entities = Object.keys(result); + const referenced = intersection(entities, this.savedEntities); + + if (referenced.length > 0) { + const saved = this.cacheStore.getCurrentData(referenced); + for (const entity in saved) { + const key = `${LOCAL_STORAGE_KEYS.cacheSaved}:${entity as string}`; + await this.localStorage.save(key, saved[entity]); + } } } ); - this.cacheStore.resetInitialData(data); - this.cacheStore.onCommit((result) => { - const entities = Object.keys(result); - const referenced = intersection(entities, this.savedEntities); - - if (referenced.length > 0) { - const saved = this.cacheStore.getCurrentData(referenced); - Object.keys(saved).forEach( - (entity) => { - const key = `${LOCAL_STORAGE_KEYS.cacheSaved}:${entity as string}`; - this.localStorage.save(key, saved[entity]); - } - ) - } - }); } getSchema() { @@ -142,12 +139,12 @@ export class Cache< dontPublish?: true, ) { try { - this.refreshing ++; + this.refreshing++; const { result, opRecords, message } = await this.aspectWrapper.exec(name, params); if (opRecords) { this.syncInner(opRecords); } - this.refreshing --; + this.refreshing--; callback && callback(result, opRecords); if (opRecords && opRecords.length > 0 && !dontPublish) { this.publish(); @@ -159,7 +156,7 @@ export class Cache< } catch (e) { // 如果是数据不一致错误,这里可以让用户知道 - this.refreshing --; + this.refreshing--; if (e instanceof OakException) { const { opRecord } = e; if (opRecord) { @@ -236,7 +233,7 @@ export class Cache< ); const gap2 = gap || this.keepFreshPeriod; - + const now = Date.now(); if (oldest < Number.MAX_SAFE_INTEGER && oldest > now - gap2) { // 说明可以用localCache的数据,不用去请求 @@ -253,7 +250,7 @@ export class Cache< data, }; } - else { + else { if (oldest > 0) { // 说明key曾经都取过了,只取updateAt在oldest之后的数据 selection.filter = combineFilters(entity, this.getSchema(), [selection.filter, { @@ -298,7 +295,7 @@ export class Cache< total, }; } - catch(err) { + catch (err) { undoFns && undoFns.forEach( (fn) => fn() ); @@ -440,7 +437,7 @@ export class Cache< return; } - fetchRows(missedRows: Array<{ entity: keyof ED, selection: ED[keyof ED]['Selection']}>) { + fetchRows(missedRows: Array<{ entity: keyof ED, selection: ED[keyof ED]['Selection'] }>) { if (!this.refreshing) { if (process.env.NODE_ENV === 'development') { console.warn('缓存被动去获取数据,请查看页面行为并加以优化', missedRows); @@ -457,7 +454,7 @@ export class Cache< }) } } - + private getInner( entity: T, selection: ED[T]['Selection'], diff --git a/src/features/environment.ts b/src/features/environment.ts index 3496d75d..3212f074 100644 --- a/src/features/environment.ts +++ b/src/features/environment.ts @@ -1,11 +1,11 @@ import { Feature } from "../types/Feature"; -import { WebEnv, WechatMpEnv } from 'oak-domain/lib/types/Environment'; +import { NativeEnv, WebEnv, WechatMpEnv } from 'oak-domain/lib/types/Environment'; import { getEnv } from '../utils/env/env'; import { assert } from 'oak-domain/lib/utils/assert'; import { OakEnvInitializedFailure } from "../types/Exception"; export class Environment extends Feature { - env?: WebEnv | WechatMpEnv; + env?: WebEnv | WechatMpEnv | NativeEnv; loading = false; constructor() { @@ -27,7 +27,7 @@ export class Environment extends Feature { } } - async getEnv(): Promise { + async getEnv(): Promise { if (this.env) { return this.env; } diff --git a/src/features/localStorage.native.ts b/src/features/localStorage.native.ts new file mode 100644 index 00000000..e9b8d294 --- /dev/null +++ b/src/features/localStorage.native.ts @@ -0,0 +1,84 @@ +import { unset } from 'oak-domain/lib/utils/lodash'; +import { LOCAL_STORAGE_KEYS } from '../constant/constant'; +import { Feature } from '../types/Feature'; +import AsyncStorage from '@react-native-async-storage/async-storage'; + +export class LocalStorage extends Feature { + keys: Record; + + constructor() { + super(); + if (process.env.NODE_ENV === 'development') { + // development环境下,debugStore的数据也默认存放在localStorage中 + this.keys = { + [LOCAL_STORAGE_KEYS.debugStore]: true, + [LOCAL_STORAGE_KEYS.debugStoreStat]: true, + }; + } + else { + this.keys = {}; + } + } + + setKey(key: string) { + if (!this.keys[key]) { + this.keys[key] = true; + } + } + + unsetKey(key: string) { + if (this.keys[key]) { + unset(this.keys, key); + } + } + + async save(key: string, item: any) { + this.setKey(key); + await AsyncStorage.setItem(key, JSON.stringify(item)); + } + + async load(key: string) { + this.setKey(key); + const value = await AsyncStorage.getItem(key); + if (value) { + return JSON.parse(value); + } + } + + clear() { + return AsyncStorage.clear(); + } + + remove(key: string) { + return AsyncStorage.removeItem(key); + } + + async loadAll() { + const keys = await AsyncStorage.getAllKeys(); + const value = await AsyncStorage.multiGet(keys); + const result: Record = {}; + + value.forEach( + ([k, v]) => { + if (typeof v === 'string') { + result[k] = JSON.parse(v); + } + } + ); + return result; + } + + resetAll(data: Record) { + const value = [] as [string, string][]; + Object.keys(data).forEach( + (k) => { + if (data[k] !== undefined && data[k] !== null) { + value.push( + [k, JSON.stringify(data[k])] + ); + } + } + ) + return AsyncStorage.multiMerge(value); + } +} diff --git a/src/features/localStorage.ts b/src/features/localStorage.ts index 07b018b5..0f91111f 100644 --- a/src/features/localStorage.ts +++ b/src/features/localStorage.ts @@ -19,19 +19,19 @@ export class LocalStorage extends Feature { } } - setKey(key: string) { + private setKey(key: string) { if (!this.keys[key]) { this.keys[key] = true; } } - unsetKey(key: string) { + private unsetKey(key: string) { if (this.keys[key]) { unset(this.keys, key); } } - save(key: string, item: any) { + async save(key: string, item: any) { this.setKey(key); switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -48,7 +48,7 @@ export class LocalStorage extends Feature { } } - load(key: string) { + async load(key: string) { this.setKey(key); switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -67,7 +67,7 @@ export class LocalStorage extends Feature { } } - clear() { + async clear() { this.keys = {}; switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -84,7 +84,7 @@ export class LocalStorage extends Feature { } } - remove(key: string) { + async remove(key: string) { this.unsetKey(key); switch (process.env.OAK_PLATFORM) { case 'wechatMp': { @@ -101,21 +101,21 @@ export class LocalStorage extends Feature { } } - loadAll() { + async loadAll() { const data: Record = {}; for (const k in this.keys) { Object.assign(data, { - [k]: this.load(k), + [k]: await this.load(k), }); } return data; } - resetAll(data: Record) { + async resetAll(data: Record) { this.clear(); for (const k in data) { - this.save(k, data[k]); + await this.save(k, data[k]); } } } diff --git a/src/features/locales.ts b/src/features/locales.ts index 7b759467..e28ac2cf 100644 --- a/src/features/locales.ts +++ b/src/features/locales.ts @@ -1,4 +1,4 @@ -import { EntityDict, Aspect, Context, AspectWrapper } from 'oak-domain/lib/types'; +import { EntityDict, Aspect, Context, AspectWrapper, WebEnv, NativeEnv } from 'oak-domain/lib/types'; import { Feature } from '../types/Feature'; import { CommonAspectDict } from 'oak-common-aspect'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; @@ -22,6 +22,16 @@ export class Locales, localStorage: LocalStorage, @@ -34,14 +44,9 @@ export class Locales( + options: { url: string } & OakNavigateToParameters, + state?: Record, + disableNamespace?: boolean + ) { + throw new Error('unimplemented'); + } + + async redirectTo< + ED extends EntityDict & BaseEntityDict, + T2 extends keyof ED + >( + options: { url: string } & OakNavigateToParameters, + state?: Record, + disableNamespace?: boolean + ) { + throw new Error('unimplemented'); + } + + async switchTab< + ED extends EntityDict & BaseEntityDict, + T2 extends keyof ED + >( + options: { url: string } & OakNavigateToParameters, + state?: Record, + disableNamespace?: boolean + ) { + throw new Error('unimplemented'); + } + + async navigateBack(delta?: number) { + throw new Error('unimplemented'); + } + + navigateBackOrRedirectTo< + ED extends EntityDict & BaseEntityDict, + T2 extends keyof ED + >( + options: { url: string; isTabBar?: boolean } & OakNavigateToParameters< + ED, + T2 + >, + state?: Record, + disableNamespace?: boolean + ) { + throw new Error('unimplemented'); + } +} \ No newline at end of file diff --git a/src/page.mp.ts b/src/page.mp.ts index 169c78da..a9f4cf37 100644 --- a/src/page.mp.ts +++ b/src/page.mp.ts @@ -238,15 +238,18 @@ const oakBehavior = Behavior< }, save(key: string, item: any) { - this.features.localStorage.save(key, item); + return this.features.localStorage.save(key, item); }, load(key: string) { return this.features.localStorage.load(key); }, - clear() { - this.features.localStorage.clear(); + clear(key?: string) { + if (key) { + return this.features.localStorage.remove(key); + } + return this.features.localStorage.clear(); }, setNotification(data: NotificationProps) { diff --git a/src/page.native.ts b/src/page.native.tsx similarity index 66% rename from src/page.native.ts rename to src/page.native.tsx index 3b38bb38..dd27c66f 100644 --- a/src/page.native.ts +++ b/src/page.native.tsx @@ -49,44 +49,12 @@ export function createComponent< >(option, features); class Component extends BaseComponent { - private scrollEvent = () => { - this.checkReachBottom(); - }; - - private registerPageScroll() { - window.addEventListener('scroll', this.scrollEvent); - } - - private unregisterPageScroll() { - window.removeEventListener('scroll', this.scrollEvent); - } - - private checkReachBottom() { - if (!this.supportPullDownRefresh()) { - return; - } - const isCurrentReachBottom = - document.body.scrollHeight - - (window.innerHeight + window.scrollY) <= - DEFAULT_REACH_BOTTOM_DISTANCE; - - if (!this.isReachBottom && isCurrentReachBottom && option.isList) { - this.isReachBottom = true; - // 执行触底事件 - this.loadMore(); - return; - } - - this.isReachBottom = isCurrentReachBottom; - } async componentDidMount() { - this.registerPageScroll(); await super.componentDidMount(); } componentWillUnmount(): void { - this.unregisterPageScroll(); super.componentWillUnmount(); } @@ -94,7 +62,9 @@ export function createComponent< const { oakPullDownRefreshLoading } = this.state; const Render = super.render(); - return Render; + return Render; } } + + return Component as React.ComponentType; } \ No newline at end of file diff --git a/src/page.react.tsx b/src/page.react.tsx index 389e45c5..df2eff56 100644 --- a/src/page.react.tsx +++ b/src/page.react.tsx @@ -85,15 +85,18 @@ abstract class OakComponentBase< } save(key: string, item: any) { - this.features.localStorage.save(key, item); + return this.features.localStorage.save(key, item); } load(key: string) { return this.features.localStorage.load(key); } - clear() { - this.features.localStorage.clear(); + clear(key?: string) { + if (key) { + return this.features.localStorage.remove(key); + } + return this.features.localStorage.clear(); } setNotification(data: NotificationProps) { diff --git a/src/types/Initialize.ts b/src/types/Initialize.ts index 759df401..aea9d931 100644 --- a/src/types/Initialize.ts +++ b/src/types/Initialize.ts @@ -1,4 +1,4 @@ -import { AsyncContext } from 'oak-domain'; +import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { ActionDictOfEntityDict, CascadeRemoveDefDict, ColorDict, Importation, Exportation } from 'oak-domain/lib/types'; import { AuthDeduceRelationMap, EntityDict } from 'oak-domain/lib/types/Entity'; diff --git a/src/types/Page.ts b/src/types/Page.ts index 974833c9..828335dd 100644 --- a/src/types/Page.ts +++ b/src/types/Page.ts @@ -330,9 +330,9 @@ export type OakCommonComponentMethods< pubEvent: (type: string, options?: any) => void; unsubAllEvents: (type: string) => void; - save: (key: string, item: any) => void; - load: (key: string) => any; - clear: () => void; + save: (key: string, item: any) => Promise; + load: (key: string) => Promise; + clear: (key?: string) => Promise; setNotification: (data: NotificationProps) => void; consumeNotification: () => NotificationProps | undefined; setMessage: (data: MessageProps) => void; diff --git a/src/utils/env/env.native.ts b/src/utils/env/env.native.ts new file mode 100644 index 00000000..9f14fcd3 --- /dev/null +++ b/src/utils/env/env.native.ts @@ -0,0 +1,11 @@ +import { NativeEnv } from 'oak-domain/lib/types/Environment'; +import { Platform } from 'react-native'; +import { getLocales } from 'react-native-localize'; + +export async function getEnv() { + const language = getLocales()[0].languageTag; + return { + ...Platform, + language, + } as NativeEnv; +} \ No newline at end of file diff --git a/tsconfig.es.json b/tsconfig.es.json index 7ac82d07..5f3e668a 100644 --- a/tsconfig.es.json +++ b/tsconfig.es.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "jsx": "react-jsx", + "jsx": "react-native", /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ @@ -69,7 +69,8 @@ "types": [ "node", "wechat-miniprogram", - "react" + "react", + "react-native" ], "include": [ "src/**/*.ts", diff --git a/tsconfig.json b/tsconfig.json index b43cdb8b..76fa8dc8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "jsx": "react-jsx", + "jsx": "react-native", /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ @@ -69,7 +69,8 @@ "types": [ "node", "wechat-miniprogram", - "react" + "react", + "react-native" ], "include": [ "src/**/*.ts", diff --git a/typings/react.d.ts b/typings/react.d.ts index 33f938c6..cc245a55 100644 --- a/typings/react.d.ts +++ b/typings/react.d.ts @@ -1,12 +1,13 @@ /// /// /// +/// declare namespace NodeJS { interface ProcessEnv { readonly NODE_ENV: 'development' | 'production' | 'test' | 'staging'; readonly PUBLIC_URL: string; - readonly OAK_PLATFORM: 'web' | 'wechatMp' | 'server'; + readonly OAK_PLATFORM: 'web' | 'wechatMp' | 'server' | 'native'; } } @@ -79,6 +80,11 @@ declare module '*.module.less' { export default classes; } +// for react-native +declare module '*.native.less' { + export default StyleProp; +} + /** * 微信标签申明 */