Skip to content

Commit

Permalink
add extras to onSet, move persistence upgrading node activation to be…
Browse files Browse the repository at this point in the history
… run from persist.ts
  • Loading branch information
jmeistrich committed Oct 28, 2023
1 parent 96d4c8a commit efc3261
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 77 deletions.
3 changes: 3 additions & 0 deletions persist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ export const internal: {
} = {
observablePersistConfiguration,
};

import { persistActivateNode } from './src/persist/persistActivateNode';
persistActivateNode();
7 changes: 5 additions & 2 deletions src/ObservableObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import type {
UpdateFn,
RetryOptions,
ObservablePersistStateInternal,
OnSetExtra,
SubscribeOptions,
} from './observableInterfaces';
import { observe } from './observe';
import { onChange } from './onChange';
Expand Down Expand Up @@ -888,6 +890,7 @@ function createNodeActivationParams(node: NodeValue): ActivateProxyParams {
retry,
subscribe,
updateLastSync,
obs$: getProxy(node),
};
}

Expand Down Expand Up @@ -989,7 +992,7 @@ const activateNodeBase = (globalState.activateNode = function activateNodeBase(
) {
isSetting = true;
batch(
() => onSetFn(params),
() => onSetFn(params, { update } as OnSetExtra),
() => {
isSetting = false;
},
Expand Down Expand Up @@ -1017,7 +1020,7 @@ const activateNodeBase = (globalState.activateNode = function activateNodeBase(
};

if (subscriber) {
subscriber({ update, refresh });
subscriber({ update, refresh } as SubscribeOptions);
}

return { update };
Expand Down
14 changes: 11 additions & 3 deletions src/observableInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,9 +482,9 @@ interface BaseNodeValue {
activated?: boolean;
proxyFn2?: (key: string, params: ActivateParams) => any;
activationState?: {
onSetFn?: (value: ListenerParams<any>) => void;
onSetFn?: (value: ListenerParams<any>, extra: OnSetExtra) => void;
update?: UpdateFn;
subscriber?: (params: { update: UpdateFn; refresh: () => void }) => void;
subscriber?: (params: SubscribeOptions) => void;
retryOptions?: RetryOptions;
lastSync: { value?: number };
cacheOptions?: CacheOptions;
Expand Down Expand Up @@ -543,7 +543,8 @@ export interface CacheOptions<T = any> {
pluginLocal?: ClassConstructor<ObservablePersistLocal, T[]>;
}
export interface ActivateParams<T = any> {
onSet: (fn: (params: ListenerParams<T>) => void) => void;
obs$: Observable<T>;
onSet: (fn: (params: ListenerParams<T>, extra: OnSetExtra) => void) => void;
subscribe: (fn: (params: { update: UpdateFn; refresh: () => void }) => void) => void;
}
export interface ActivateProxyParams<T = any> extends ActivateParams {
Expand All @@ -557,3 +558,10 @@ export interface RetryOptions {
backoff?: 'constant' | 'exponential';
maxDelay?: number;
}
export interface OnSetExtra {
update: UpdateFn;
}
export interface SubscribeOptions {
update: UpdateFn;
refresh: () => void;
}
93 changes: 93 additions & 0 deletions src/persist/persistActivateNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type {
CacheOptions,
ListenerParams,
NodeValue,
ObservableOnChangeParams,
ObservablePersistRemoteFunctions,
ObservablePersistRemoteGetParams,
ObservablePersistRemoteSetParams,
ObservablePersistStateBase,
RetryOptions,
UpdateFn,
} from '@legendapp/state';
import { internal, isPromise } from '@legendapp/state';
import { onChangeRemote, persistObservable } from './persistObservable';
const { getProxy, globalState } = internal;

export function persistActivateNode() {
globalState.activateNode = function activateNodePersist(
node: NodeValue,
refresh: () => void,
wasPromise: boolean,
newValue: any,
) {
const { onSetFn, subscriber, lastSync, cacheOptions, retryOptions } = node.activationState!;

let onChange: UpdateFn | undefined = undefined;
const pluginRemote: ObservablePersistRemoteFunctions = {
get: async (params: ObservablePersistRemoteGetParams<any>) => {
onChange = params.onChange;
if (isPromise(newValue)) {
newValue = await newValue;
}
if (lastSync.value) {
params.dateModified = lastSync.value;
}
return newValue;
},
};
if (onSetFn) {
// TODO: Work out these types better
pluginRemote.set = (params: ObservablePersistRemoteSetParams<any>) => {
if (node.state?.isLoaded.get()) {
onSetFn(params as unknown as ListenerParams, {
update: onChange as UpdateFn,
updateLastSync: () => {
console.log('TODO updateLastSync');
},
applyRemoteChange: onChangeRemote,
});
}
};
}
if (subscriber) {
subscriber({
update: (params: ObservableOnChangeParams) => {
if (!onChange) {
// TODO: Make this message better
console.log('[legend-state] Cannot update immediately before the first return');
} else {
onChange(params);
}
},
refresh,
applyRemoteChange: onChangeRemote,
});
}
persistObservable(getProxy(node), {
pluginRemote,
...(cacheOptions || {}),
remote: {
retry: retryOptions,
},
});

return { update: onChange! };
};
}
declare module '@legendapp/state' {
interface ActivateParams<T> {
cache: (cacheOptions: CacheOptions<T> | (() => CacheOptions<T>)) => void;
updateLastSync: (lastSync: number) => void;
retry: (options: RetryOptions) => void;
}
interface OnSetExtra {
updateLastSync: (lastSync: number) => void;
applyRemoteChange: (fn: () => void) => void;
}
interface SubscribeOptions {
applyRemoteChange: (fn: () => void) => void;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ObservableState extends ObservablePersistStateBase {}
}
74 changes: 2 additions & 72 deletions src/persist/persistObservable.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import type {
CacheOptions,
Change,
ClassConstructor,
FieldTransforms,
ListenerParams,
NodeValue,
Observable,
ObservableObject,
ObservableOnChangeParams,
ObservablePersistLocal,
ObservablePersistRemoteClass,
ObservablePersistRemoteFunctions,
ObservablePersistRemoteGetParams,
ObservablePersistRemoteSetParams,
ObservablePersistState,
ObservablePersistStateBase,
ObservablePersistStateInternal,
ObservableReadable,
ObservableWriteable,
Expand All @@ -24,9 +19,7 @@ import type {
PersistOptionsRemote,
PersistTransform,
Primitive,
RetryOptions,
TypeAtPath,
UpdateFn,
WithPersistState,
} from '@legendapp/state';
import {
Expand All @@ -50,7 +43,7 @@ import {
import { observablePersistConfiguration } from './configureObservablePersistence';
import { invertFieldMap, transformObject, transformObjectWithPath, transformPath } from './fieldTransformer';
import { observablePersistRemoteFunctionsAdapter } from './observablePersistRemoteFunctionsAdapter';
const { getProxy, globalState } = internal;
const { globalState } = internal;

export const mapPersistences: WeakMap<
ClassConstructor<ObservablePersistLocal | ObservablePersistRemoteClass>,
Expand Down Expand Up @@ -88,7 +81,7 @@ function doInOrder<T>(arg1: T | Promise<T>, arg2: (value: T) => void): any {
return isPromise(arg1) ? arg1.then(arg2) : arg2(arg1);
}

function onChangeRemote(cb: () => void) {
export function onChangeRemote(cb: () => void) {
when(
() => !globalState.isLoadingRemote$.get(),
() => {
Expand Down Expand Up @@ -867,66 +860,3 @@ export function persistObservable<T extends WithoutState>(

return obs as any;
}

globalState.activateNode = function activateNodePersist(
node: NodeValue,
refresh: () => void,
wasPromise: boolean,
newValue: any,
) {
const { onSetFn, subscriber, lastSync, cacheOptions, retryOptions } = node.activationState!;

let onChange: UpdateFn | undefined = undefined;
const pluginRemote: ObservablePersistRemoteFunctions = {
get: async (params: ObservablePersistRemoteGetParams<any>) => {
onChange = params.onChange;
if (isPromise(newValue)) {
newValue = await newValue;
}
if (lastSync.value) {
params.dateModified = lastSync.value;
}
return newValue;
},
};
if (onSetFn) {
// TODO: Work out these types better
pluginRemote.set = (params: ObservablePersistRemoteSetParams<any>) => {
if (node.state?.isLoaded.get()) {
onSetFn(params as unknown as ListenerParams);
}
};
}
if (subscriber) {
subscriber({
update: (params: ObservableOnChangeParams) => {
if (!onChange) {
// TODO: Make this message better
console.log('[legend-state] Cannot update immediately before the first return');
} else {
onChange(params);
}
},
refresh,
});
}
persistObservable(getProxy(node), {
pluginRemote,
...(cacheOptions || {}),
remote: {
retry: retryOptions,
},
});

return { update: onChange! };
};

declare module '@legendapp/state' {
interface ActivateParams<T> {
cache: (cacheOptions: CacheOptions<T> | (() => CacheOptions<T>)) => void;
updateLastSync: (lastSync: number) => void;
retry: (options: RetryOptions) => void;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ObservableState extends ObservablePersistStateBase {}
}

0 comments on commit efc3261

Please sign in to comment.