From 5d305c36adc32aea9561efe088c55d86052dfc77 Mon Sep 17 00:00:00 2001 From: Jay Meistrich Date: Fri, 27 Oct 2023 16:25:43 +0100 Subject: [PATCH] add a refresh function to subscribe --- src/ObservableObject.ts | 12 +++++++----- src/globals.ts | 7 +------ src/observableInterfaces.ts | 8 ++++++-- src/persist/persistObservable.ts | 9 +++++---- tests/computedtests.ts | 22 ++++++++++++++++++++-- 5 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/ObservableObject.ts b/src/ObservableObject.ts index f10a8bdf6..f65fb620a 100644 --- a/src/ObservableObject.ts +++ b/src/ObservableObject.ts @@ -43,6 +43,7 @@ import type { TrackingType, UpdateFn, RetryOptions, + ObservablePersistStateInternal, } from './observableInterfaces'; import { observe } from './observe'; import { onChange } from './onChange'; @@ -891,6 +892,7 @@ function activateNodeFunction(node: NodeValue, lazyFn: () => void) { let curTarget$: ObservableObject; const activator = (isFunction(node) ? node : lazyFn) as (value: ActivateProxyParams) => any; let wasPromise: Promise | undefined; + const refresh = () => (node.state as ObservableObject).refreshNum.set((v) => v + 1); observe( () => { const params = createNodeActivationParams(node); @@ -927,9 +929,11 @@ function activateNodeFunction(node: NodeValue, lazyFn: () => void) { if (!node.activated) { node.activated = true; const activateNodeFn = wasPromise ? globalState.activateNode : activateNodeBase; - activateNodeFn(node, value).update!; + activateNodeFn(node, refresh, value); } + (node.state as ObservableObject).refreshNum.get(); + return value; }, ({ value }) => { @@ -953,7 +957,7 @@ function activateNodeFunction(node: NodeValue, lazyFn: () => void) { ); } -const activateNodeBase = (globalState.activateNode = function activateNodeBase(node: NodeValue): { update: UpdateFn } { +const activateNodeBase = (globalState.activateNode = function activateNodeBase(node: NodeValue, refresh: () => void) { const { onSetFn, subscriber } = node.activationState!; let isSetting = false; if (!node.state) { @@ -1001,8 +1005,6 @@ const activateNodeBase = (globalState.activateNode = function activateNodeBase(n }; if (subscriber) { - subscriber({ update }); + subscriber({ update, refresh }); } - - return { update }; }); diff --git a/src/globals.ts b/src/globals.ts index 75b1b4c94..a16d9d17f 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -24,12 +24,7 @@ export const globalState = { isLoadingLocal: false, isMerging: false, isLoadingRemote$: undefined as unknown as ObservablePrimitive, - activateNode: undefined as unknown as ( - node: NodeValue, - newValue: any, - ) => { - update?: UpdateFn; - }, + activateNode: undefined as unknown as (node: NodeValue, refresh: () => void, newValue: any) => void, }; export function isObservable(obs: any): obs is ObservableObject { diff --git a/src/observableInterfaces.ts b/src/observableInterfaces.ts index 5f91022b4..db92e5b03 100644 --- a/src/observableInterfaces.ts +++ b/src/observableInterfaces.ts @@ -327,6 +327,7 @@ export interface ObservablePersistStateBase { isEnabledLocal: boolean; isEnabledRemote: boolean; dateModified?: number; + syncCount?: number; clearLocal: () => Promise; sync: () => Promise; getPendingChanges: () => @@ -340,6 +341,9 @@ export interface ObservablePersistStateBase { | undefined; } export type ObservablePersistState = ObservableState & ObservablePersistStateBase; +export type ObservablePersistStateInternal = ObservablePersistState & { + refreshNum: number; +}; export interface WithPersistState { state?: ObservablePersistState; // TODOV3: remove this _state?: ObservablePersistState; @@ -480,7 +484,7 @@ interface BaseNodeValue { activationState?: { onSetFn?: (value: ListenerParams) => void; update?: UpdateFn; - subscriber?: (params: { update: UpdateFn }) => void; + subscriber?: (params: { update: UpdateFn; refresh: () => void }) => void; retryOptions?: RetryOptions; lastSync: { value?: number }; cacheOptions?: CacheOptions; @@ -540,7 +544,7 @@ export interface CacheOptions { } export interface ActivateParams { onSet: (fn: (params: ListenerParams) => void) => void; - subscribe: (fn: (params: { update: (props: ObservableOnChangeParams) => void }) => void) => void; + subscribe: (fn: (params: { update: UpdateFn; refresh: () => void }) => void) => void; } export interface ActivateProxyParams extends ActivateParams { proxy: (fn: (key: string, params: ActivateParams) => T | Promise) => void; diff --git a/src/persist/persistObservable.ts b/src/persist/persistObservable.ts index e3aabcf62..04716b279 100644 --- a/src/persist/persistObservable.ts +++ b/src/persist/persistObservable.ts @@ -15,6 +15,7 @@ import type { ObservablePersistRemoteSetParams, ObservablePersistState, ObservablePersistStateBase, + ObservablePersistStateInternal, ObservableReadable, ObservableWriteable, PersistMetadata, @@ -654,11 +655,12 @@ export function persistObservable( const localState: LocalState = {}; let sync: () => Promise; - const syncState = (node.state = observable({ + const syncState = (node.state = observable({ isLoadedLocal: false, isLoaded: false, isEnabledLocal: true, isEnabledRemote: true, + refreshNum: 0, clearLocal: undefined as unknown as () => Promise, sync: () => Promise.resolve(), getPendingChanges: () => localState.pendingChanges, @@ -866,7 +868,7 @@ export function persistObservable( return obs as any; } -globalState.activateNode = function activateNodePersist(node: NodeValue, newValue: any): { update?: UpdateFn } { +globalState.activateNode = function activateNodePersist(node: NodeValue, refresh: () => void, newValue: any) { const { onSetFn, subscriber, lastSync, cacheOptions, retryOptions } = node.activationState!; let onChange: UpdateFn | undefined = undefined; @@ -896,6 +898,7 @@ globalState.activateNode = function activateNodePersist(node: NodeValue, newValu onChange(params); } }, + refresh, }); } persistObservable(getProxy(node), { @@ -905,8 +908,6 @@ globalState.activateNode = function activateNodePersist(node: NodeValue, newValu retry: retryOptions, }, }); - - return { update: onChange }; }; declare module '@legendapp/state' { diff --git a/tests/computedtests.ts b/tests/computedtests.ts index 89e020d34..9988a17eb 100644 --- a/tests/computedtests.ts +++ b/tests/computedtests.ts @@ -1001,7 +1001,7 @@ export const run = (isPersist: boolean) => { }); }); describe('subscribing to computeds', () => { - test('basic subscription', async () => { + test('subscription with update', async () => { const obs = observable(({ subscribe }: ActivateParams) => { subscribe(({ update }) => { setTimeout(() => { @@ -1015,9 +1015,27 @@ export const run = (isPersist: boolean) => { expect(obs.get()).toEqual(undefined); await promiseTimeout(0); expect(obs.get()).toEqual('hi there'); - await promiseTimeout(5); + await promiseTimeout(10); expect(obs.get()).toEqual('hi there again'); }); + test('subscription with refresh', async () => { + let num = 0; + const obs = observable(({ subscribe }: ActivateParams) => { + subscribe(({ refresh }) => { + setTimeout(() => { + refresh(); + }, 5); + }); + return new Promise((resolve) => { + setTimeout(() => resolve('hi there ' + num++), 0); + }); + }); + expect(obs.get()).toEqual(undefined); + await promiseTimeout(0); + expect(obs.get()).toEqual('hi there 0'); + await promiseTimeout(10); + expect(obs.get()).toEqual('hi there 1'); + }); }); describe('loading', () => { test('isLoaded', async () => {