增加了大量和native相关的代码

This commit is contained in:
Xu Chang 2023-11-26 22:38:44 +08:00
parent 27633c7335
commit d24fd56763
66 changed files with 887 additions and 356 deletions

View File

@ -12,6 +12,9 @@ interface DebugStoreSelectOption extends TreeStoreSelectOption {
export declare class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends TreeStore<ED> implements AsyncRowStore<ED, Cxt> {
private executor;
private relationAuth;
private dataLoaded;
private dataLoadedLock;
private dataLoadedLockUnlocker;
constructor(storageSchema: StorageSchema<ED>, contextBuilder: (cxtString?: string) => (store: DebugStore<ED, Cxt>) => Promise<Cxt>, authDeduceRelationMap: AuthDeduceRelationMap<ED>, selectFreeEntities?: (keyof ED)[], updateFreeDict?: {
[A in keyof ED]?: string[];
});
@ -26,5 +29,13 @@ export declare class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt exte
count<T extends keyof ED, OP extends SelectOption>(entity: T, selection: Pick<ED[T]["Selection"], "filter" | "count">, context: Cxt, option: OP): Promise<number>;
registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>): void;
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>): void;
resetInitialData(initialData: {
[T in keyof ED]?: Array<ED[T]['OpSchema']>;
}, stat?: {
create: number;
update: number;
remove: number;
commit: number;
}): void;
}
export {};

View File

@ -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();
}
}

View File

@ -5,6 +5,6 @@ import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
export declare function clearMaterializedData(): void;
export declare function createDebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(storageSchema: StorageSchema<ED>, contextBuilder: (cxtString?: string) => (store: DebugStore<ED, Cxt>) => Promise<Cxt>, triggers: Array<Trigger<ED, keyof ED, Cxt>>, checkers: Array<Checker<ED, keyof ED, Cxt>>, watchers: Array<Watcher<ED, keyof ED, Cxt>>, timers: Array<Timer<ED, Cxt>>, startRoutines: Array<Routine<ED, Cxt>>, initialData: {
[T in keyof ED]?: Array<ED[T]['OpSchema']>;
}, actionDict: ActionDictOfEntityDict<ED>, authDeduceRelationMap: AuthDeduceRelationMap<ED>, saveFn: (key: string, data: any) => void, loadFn: (key: string) => any, selectFreeEntities?: (keyof ED)[], updateFreeDict?: {
}, actionDict: ActionDictOfEntityDict<ED>, authDeduceRelationMap: AuthDeduceRelationMap<ED>, saveFn: (key: string, data: any) => Promise<void>, loadFn: (key: string) => Promise<any>, selectFreeEntities?: (keyof ED)[], updateFreeDict?: {
[A in keyof ED]?: string[];
}): DebugStore<ED, Cxt>;

View File

@ -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

View File

@ -30,7 +30,6 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict, Cxt extends A
private refreshRecords;
private context?;
constructor(storageSchema: StorageSchema<ED>, aspectWrapper: AspectWrapper<ED, Cxt, AD>, frontendContextBuilder: () => (store: CacheStore<ED, FrontCxt>) => FrontCxt, checkers: Array<Checker<ED, keyof ED, FrontCxt | Cxt>>, getFullData: () => any, localStorage: LocalStorage, savedEntities?: (keyof ED)[], keepFreshPeriod?: number);
private rebuildRefreshRows;
/**
* cache中需要缓存的数据
*/

View File

@ -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]);
}
}
});
}

View File

@ -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<WebEnv | WechatMpEnv>;
getEnv(): Promise<WebEnv | WechatMpEnv | NativeEnv>;
}

View File

@ -2,12 +2,12 @@ import { Feature } from '../types/Feature';
export declare class LocalStorage extends Feature {
keys: Record<string, boolean>;
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<string, any>;
resetAll(data: Record<string, any>): void;
private setKey;
private unsetKey;
save(key: string, item: any): Promise<void>;
load(key: string): Promise<any>;
clear(): Promise<void>;
remove(key: string): Promise<void>;
loadAll(): Promise<Record<string, any>>;
resetAll(data: Record<string, any>): Promise<void>;
}

View File

@ -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]);
}
}
}

13
es/features/localStorage.native.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import { Feature } from '../types/Feature';
export declare class LocalStorage extends Feature {
keys: Record<string, boolean>;
constructor();
setKey(key: string): void;
unsetKey(key: string): void;
save(key: string, item: any): Promise<void>;
load(key: string): Promise<any>;
clear(): Promise<void>;
remove(key: string): Promise<void>;
loadAll(): Promise<Record<string, any>>;
resetAll(data: Record<string, any>): Promise<void>;
}

View File

@ -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);
}
}

View File

@ -17,6 +17,7 @@ export declare class Locales<ED extends EntityDict & BaseEntityDict, Cxt extends
private language;
private defaultLng;
private i18n;
private initializeLng;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD>, localStorage: LocalStorage, environment: Environment, defaultLng: string, makeBridgeUrlFn?: (url: string, headers?: Record<string, string>) => string);
private detectLanguange;
private resetDataset;

View File

@ -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', {

24
es/features/navigator.native.d.ts vendored Normal file
View File

@ -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<ED extends EntityDict & BaseEntityDict, T2 extends keyof ED>(options: {
url: string;
} & OakNavigateToParameters<ED, T2>, state?: Record<string, any>, disableNamespace?: boolean): Promise<void>;
redirectTo<ED extends EntityDict & BaseEntityDict, T2 extends keyof ED>(options: {
url: string;
} & OakNavigateToParameters<ED, T2>, state?: Record<string, any>, disableNamespace?: boolean): Promise<void>;
switchTab<ED extends EntityDict & BaseEntityDict, T2 extends keyof ED>(options: {
url: string;
} & OakNavigateToParameters<ED, T2>, state?: Record<string, any>, disableNamespace?: boolean): Promise<void>;
navigateBack(delta?: number): Promise<void>;
navigateBackOrRedirectTo<ED extends EntityDict & BaseEntityDict, T2 extends keyof ED>(options: {
url: string;
isTabBar?: boolean;
} & OakNavigateToParameters<ED, T2>, state?: Record<string, any>, disableNamespace?: boolean): void;
}

View File

@ -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');
}
}

View File

@ -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);

3
es/page.native.d.ts vendored
View File

@ -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<IsList extends boolean, ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>, FD extends Record<string, Feature>, FormedData extends Record<string, any>, TData extends Record<string, any> = {}, TProperty extends DataOption = {}, TMethod extends Record<string, Function> = {}>(option: OakComponentOption<IsList, ED, T, Cxt, FrontCxt, AD, FD, FormedData, TData, TProperty, TMethod>, features: BasicFeatures<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>> & FD): void;
export declare function createComponent<IsList extends boolean, ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>, FD extends Record<string, Feature>, FormedData extends Record<string, any>, TData extends Record<string, any> = {}, TProperty extends DataOption = {}, TMethod extends Record<string, Function> = {}>(option: OakComponentOption<IsList, ED, T, Cxt, FrontCxt, AD, FD, FormedData, TData, TProperty, TMethod>, features: BasicFeatures<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>> & FD): React.ComponentType<any>;

View File

@ -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;
}

6
es/page.react.d.ts vendored
View File

@ -31,9 +31,9 @@ export declare function createComponent<IsList extends boolean, ED extends Entit
unsubEvent(type: string, callback: Function): 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<void>;
load(key: string): Promise<any>;
clear(key?: string | undefined): Promise<void>;
setNotification(data: NotificationProps): void;
consumeNotification(): NotificationProps | undefined;
setMessage(data: MessageProps): Promise<void>;

View File

@ -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);

View File

@ -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';

6
es/types/Page.d.ts vendored
View File

@ -138,9 +138,9 @@ export type OakCommonComponentMethods<ED extends EntityDict & BaseEntityDict, T
unsubEvent: (type: string, callback: Function) => 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<void>;
load: (key: string) => Promise<any>;
clear: (key?: string) => Promise<void>;
setNotification: (data: NotificationProps) => void;
consumeNotification: () => NotificationProps | undefined;
setMessage: (data: MessageProps) => void;

2
es/utils/env/env.native.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
import { NativeEnv } from 'oak-domain/lib/types/Environment';
export declare function getEnv(): Promise<NativeEnv>;

9
es/utils/env/env.native.js vendored Normal file
View File

@ -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,
};
}

View File

@ -12,6 +12,9 @@ interface DebugStoreSelectOption extends TreeStoreSelectOption {
export declare class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends TreeStore<ED> implements AsyncRowStore<ED, Cxt> {
private executor;
private relationAuth;
private dataLoaded;
private dataLoadedLock;
private dataLoadedLockUnlocker;
constructor(storageSchema: StorageSchema<ED>, contextBuilder: (cxtString?: string) => (store: DebugStore<ED, Cxt>) => Promise<Cxt>, authDeduceRelationMap: AuthDeduceRelationMap<ED>, selectFreeEntities?: (keyof ED)[], updateFreeDict?: {
[A in keyof ED]?: string[];
});
@ -26,5 +29,13 @@ export declare class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt exte
count<T extends keyof ED, OP extends SelectOption>(entity: T, selection: Pick<ED[T]["Selection"], "filter" | "count">, context: Cxt, option: OP): Promise<number>;
registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>): void;
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>): void;
resetInitialData(initialData: {
[T in keyof ED]?: Array<ED[T]['OpSchema']>;
}, stat?: {
create: number;
update: number;
remove: number;
commit: number;
}): void;
}
export {};

View File

@ -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;

View File

@ -5,6 +5,6 @@ import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
export declare function clearMaterializedData(): void;
export declare function createDebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(storageSchema: StorageSchema<ED>, contextBuilder: (cxtString?: string) => (store: DebugStore<ED, Cxt>) => Promise<Cxt>, triggers: Array<Trigger<ED, keyof ED, Cxt>>, checkers: Array<Checker<ED, keyof ED, Cxt>>, watchers: Array<Watcher<ED, keyof ED, Cxt>>, timers: Array<Timer<ED, Cxt>>, startRoutines: Array<Routine<ED, Cxt>>, initialData: {
[T in keyof ED]?: Array<ED[T]['OpSchema']>;
}, actionDict: ActionDictOfEntityDict<ED>, authDeduceRelationMap: AuthDeduceRelationMap<ED>, saveFn: (key: string, data: any) => void, loadFn: (key: string) => any, selectFreeEntities?: (keyof ED)[], updateFreeDict?: {
}, actionDict: ActionDictOfEntityDict<ED>, authDeduceRelationMap: AuthDeduceRelationMap<ED>, saveFn: (key: string, data: any) => Promise<void>, loadFn: (key: string) => Promise<any>, selectFreeEntities?: (keyof ED)[], updateFreeDict?: {
[A in keyof ED]?: string[];
}): DebugStore<ED, Cxt>;

View File

@ -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

View File

@ -30,7 +30,6 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict, Cxt extends A
private refreshRecords;
private context?;
constructor(storageSchema: StorageSchema<ED>, aspectWrapper: AspectWrapper<ED, Cxt, AD>, frontendContextBuilder: () => (store: CacheStore<ED, FrontCxt>) => FrontCxt, checkers: Array<Checker<ED, keyof ED, FrontCxt | Cxt>>, getFullData: () => any, localStorage: LocalStorage, savedEntities?: (keyof ED)[], keepFreshPeriod?: number);
private rebuildRefreshRows;
/**
* cache中需要缓存的数据
*/

View File

@ -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]);
}
}
});
}

View File

@ -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<WebEnv | WechatMpEnv>;
getEnv(): Promise<WebEnv | WechatMpEnv | NativeEnv>;
}

View File

@ -2,12 +2,12 @@ import { Feature } from '../types/Feature';
export declare class LocalStorage extends Feature {
keys: Record<string, boolean>;
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<string, any>;
resetAll(data: Record<string, any>): void;
private setKey;
private unsetKey;
save(key: string, item: any): Promise<void>;
load(key: string): Promise<any>;
clear(): Promise<void>;
remove(key: string): Promise<void>;
loadAll(): Promise<Record<string, any>>;
resetAll(data: Record<string, any>): Promise<void>;
}

View File

@ -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]);
}
}
}

13
lib/features/localStorage.native.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import { Feature } from '../types/Feature';
export declare class LocalStorage extends Feature {
keys: Record<string, boolean>;
constructor();
setKey(key: string): void;
unsetKey(key: string): void;
save(key: string, item: any): Promise<void>;
load(key: string): Promise<any>;
clear(): Promise<void>;
remove(key: string): Promise<void>;
loadAll(): Promise<Record<string, any>>;
resetAll(data: Record<string, any>): Promise<void>;
}

View File

@ -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;

View File

@ -17,6 +17,7 @@ export declare class Locales<ED extends EntityDict & BaseEntityDict, Cxt extends
private language;
private defaultLng;
private i18n;
private initializeLng;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD>, localStorage: LocalStorage, environment: Environment, defaultLng: string, makeBridgeUrlFn?: (url: string, headers?: Record<string, string>) => string);
private detectLanguange;
private resetDataset;

View File

@ -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', {

24
lib/features/navigator.native.d.ts vendored Normal file
View File

@ -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<ED extends EntityDict & BaseEntityDict, T2 extends keyof ED>(options: {
url: string;
} & OakNavigateToParameters<ED, T2>, state?: Record<string, any>, disableNamespace?: boolean): Promise<void>;
redirectTo<ED extends EntityDict & BaseEntityDict, T2 extends keyof ED>(options: {
url: string;
} & OakNavigateToParameters<ED, T2>, state?: Record<string, any>, disableNamespace?: boolean): Promise<void>;
switchTab<ED extends EntityDict & BaseEntityDict, T2 extends keyof ED>(options: {
url: string;
} & OakNavigateToParameters<ED, T2>, state?: Record<string, any>, disableNamespace?: boolean): Promise<void>;
navigateBack(delta?: number): Promise<void>;
navigateBackOrRedirectTo<ED extends EntityDict & BaseEntityDict, T2 extends keyof ED>(options: {
url: string;
isTabBar?: boolean;
} & OakNavigateToParameters<ED, T2>, state?: Record<string, any>, disableNamespace?: boolean): void;
}

View File

@ -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;

View File

@ -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);

View File

@ -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<IsList extends boolean, ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>, FD extends Record<string, Feature>, FormedData extends Record<string, any>, TData extends Record<string, any> = {}, TProperty extends DataOption = {}, TMethod extends Record<string, Function> = {}>(option: OakComponentOption<IsList, ED, T, Cxt, FrontCxt, AD, FD, FormedData, TData, TProperty, TMethod>, features: BasicFeatures<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>> & FD): void;
export declare function createComponent<IsList extends boolean, ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>, FD extends Record<string, Feature>, FormedData extends Record<string, any>, TData extends Record<string, any> = {}, TProperty extends DataOption = {}, TMethod extends Record<string, Function> = {}>(option: OakComponentOption<IsList, ED, T, Cxt, FrontCxt, AD, FD, FormedData, TData, TProperty, TMethod>, features: BasicFeatures<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>> & FD): React.ComponentType<any>;

View File

@ -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;

6
lib/page.react.d.ts vendored
View File

@ -31,9 +31,9 @@ export declare function createComponent<IsList extends boolean, ED extends Entit
unsubEvent(type: string, callback: Function): 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<void>;
load(key: string): Promise<any>;
clear(key?: string | undefined): Promise<void>;
setNotification(data: NotificationProps): void;
consumeNotification(): NotificationProps | undefined;
setMessage(data: MessageProps): Promise<void>;

View File

@ -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);

View File

@ -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';

6
lib/types/Page.d.ts vendored
View File

@ -138,9 +138,9 @@ export type OakCommonComponentMethods<ED extends EntityDict & BaseEntityDict, T
unsubEvent: (type: string, callback: Function) => 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<void>;
load: (key: string) => Promise<any>;
clear: (key?: string) => Promise<void>;
setNotification: (data: NotificationProps) => void;
consumeNotification: () => NotificationProps | undefined;
setMessage: (data: MessageProps) => void;

2
lib/utils/env/env.native.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
import { NativeEnv } from 'oak-domain/lib/types/Environment';
export declare function getEnv(): Promise<NativeEnv>;

13
lib/utils/env/env.native.js vendored Normal file
View File

@ -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;

View File

@ -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",

View File

@ -18,6 +18,9 @@ interface DebugStoreSelectOption extends TreeStoreSelectOption {
export class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends TreeStore<ED> implements AsyncRowStore<ED, Cxt> {
private executor: TriggerExecutor<ED, Cxt>;
private relationAuth: RelationAuth<ED>;
private dataLoaded: boolean;
private dataLoadedLock: Promise<void>;
private dataLoadedLockUnlocker = () => undefined as void;
constructor(
storageSchema: StorageSchema<ED>,
@ -29,6 +32,12 @@ export class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends Asyn
}
) {
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);
}
@ -68,6 +77,9 @@ export class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends Asyn
context: Cxt,
option: OP
) {
if (!this.dataLoaded) {
await this.dataLoadedLock;
}
const autoCommit = !context.getCurrentTxnId();
let result;
if (autoCommit) {
@ -99,6 +111,9 @@ export class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends Asyn
context: Cxt,
option: OP
) {
if (!this.dataLoaded) {
await this.dataLoadedLock;
}
const autoCommit = !context.getCurrentTxnId();
if (autoCommit) {
await context.begin();
@ -135,6 +150,9 @@ export class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends Asyn
}
async count<T extends keyof ED, OP extends SelectOption>(entity: T, selection: Pick<ED[T]["Selection"], "filter" | "count">, context: Cxt, option: OP): Promise<number> {
if (!this.dataLoaded) {
await this.dataLoadedLock;
}
return super.countAsync(entity, selection, context, option);
}
@ -145,5 +163,18 @@ export class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends Asyn
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>) {
this.executor.registerChecker(checker);
}
resetInitialData(initialData: {
[T in keyof ED]?: Array<ED[T]['OpSchema']>;
}, stat?: {
create: number;
update: number;
remove: number;
commit: number;
}) {
super.resetInitialData(initialData, stat);
this.dataLoaded = true;
this.dataLoadedLockUnlocker();
}
}

View File

@ -23,10 +23,10 @@ async function initDataInStore<ED extends EntityDict & BaseEntityDict, Cxt exten
store.resetInitialData(initialData, stat);
}
function getMaterializedData(loadFn: (key: string) => any) {
async function getMaterializedData(loadFn: (key: string) => Promise<any>) {
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<void>) {
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<ED extends EntityDict & BaseEntityDict, Cxt ext
},
actionDict: ActionDictOfEntityDict<ED>,
authDeduceRelationMap: AuthDeduceRelationMap<ED>,
saveFn: (key: string, data: any) => void,
loadFn: (key: string) => any,
saveFn: (key: string, data: any) => Promise<void>,
loadFn: (key: string) => Promise<any>,
selectFreeEntities?: (keyof ED)[],
updateFreeDict?: {
[A in keyof ED]?: string[];
@ -234,31 +234,36 @@ export function createDebugStore<ED extends EntityDict & BaseEntityDict, Cxt ext
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) => {
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);

View File

@ -75,55 +75,52 @@ export class Cache<
);
this.getFullDataFn = getFullData;
// 现在这个init变成了异步行为不知道有没有影响。by Xc 20231126
this.initSavedLogic();
}
private rebuildRefreshRows<T extends keyof ED>(entity: T, projection: ED[T]['Selection']['data'], result: Awaited<ReturnType<AD['select']>>) {
const { data } = result;
const rows = [] as Partial<ED['T']['Schema']>[];
// 重新建立
}
/**
* 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<T extends keyof ED>(
entity: T,
selection: ED[T]['Selection'],

View File

@ -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<WebEnv | WechatMpEnv> {
async getEnv(): Promise<WebEnv | WechatMpEnv | NativeEnv> {
if (this.env) {
return this.env;
}

View File

@ -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<string, boolean>;
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<string, any> = {};
value.forEach(
([k, v]) => {
if (typeof v === 'string') {
result[k] = JSON.parse(v);
}
}
);
return result;
}
resetAll(data: Record<string, any>) {
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);
}
}

View File

@ -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<string, any> = {};
for (const k in this.keys) {
Object.assign(data, {
[k]: this.load(k),
[k]: await this.load(k),
});
}
return data;
}
resetAll(data: Record<string, any>) {
async resetAll(data: Record<string, any>) {
this.clear();
for (const k in data) {
this.save(k, data[k]);
await this.save(k, data[k]);
}
}
}

View File

@ -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<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncCo
private defaultLng: string;
private i18n: I18n;
private async initializeLng() {
const savedLng = await this.localStorage.load(LOCAL_STORAGE_KEYS.localeLng);
if (savedLng) {
this.language = savedLng;
}
else {
await this.detectLanguange();
}
}
constructor(
cache: Cache<ED, Cxt, FrontCxt, AD>,
localStorage: LocalStorage,
@ -34,14 +44,9 @@ export class Locales<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncCo
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,
@ -67,7 +72,7 @@ export class Locales<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncCo
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);
}
private resetDataset() {

View File

@ -0,0 +1,77 @@
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 class Navigator extends Feature {
private namespace = '';
setNamespace(namespace: string) {
this.namespace = namespace;
this.publish();
}
getLocation() {
throw new Error('unimplemented');
}
getNamespace() {
throw new Error('unimplemented');
}
async navigateTo<
ED extends EntityDict & BaseEntityDict,
T2 extends keyof ED
>(
options: { url: string } & OakNavigateToParameters<ED, T2>,
state?: Record<string, any>,
disableNamespace?: boolean
) {
throw new Error('unimplemented');
}
async redirectTo<
ED extends EntityDict & BaseEntityDict,
T2 extends keyof ED
>(
options: { url: string } & OakNavigateToParameters<ED, T2>,
state?: Record<string, any>,
disableNamespace?: boolean
) {
throw new Error('unimplemented');
}
async switchTab<
ED extends EntityDict & BaseEntityDict,
T2 extends keyof ED
>(
options: { url: string } & OakNavigateToParameters<ED, T2>,
state?: Record<string, any>,
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<string, any>,
disableNamespace?: boolean
) {
throw new Error('unimplemented');
}
}

View File

@ -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) {

View File

@ -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<any>;
}

View File

@ -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) {

View File

@ -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';

View File

@ -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<void>;
load: (key: string) => Promise<any>;
clear: (key?: string) => Promise<void>;
setNotification: (data: NotificationProps) => void;
consumeNotification: () => NotificationProps | undefined;
setMessage: (data: MessageProps) => void;

11
src/utils/env/env.native.ts vendored Normal file
View File

@ -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;
}

View File

@ -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",

View File

@ -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",

8
typings/react.d.ts vendored
View File

@ -1,12 +1,13 @@
/// <reference types="node" />
/// <reference types="react" />
/// <reference types="react-dom" />
/// <reference types="react-native" />
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<any>;
}
/**
*
*/