增加了大量和native相关的代码
This commit is contained in:
parent
27633c7335
commit
d24fd56763
|
|
@ -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 {};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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中需要缓存的数据
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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', {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
import { NativeEnv } from 'oak-domain/lib/types/Environment';
|
||||
export declare function getEnv(): Promise<NativeEnv>;
|
||||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
@ -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 {};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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中需要缓存的数据
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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', {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
import { NativeEnv } from 'oak-domain/lib/types/Environment';
|
||||
export declare function getEnv(): Promise<NativeEnv>;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信标签申明
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue