Skip to content

Commit

Permalink
add computed, escape slashes
Browse files Browse the repository at this point in the history
  • Loading branch information
himanshu-satija committed Sep 28, 2020
1 parent d8de476 commit 3f4a0ce
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 17 deletions.
8 changes: 7 additions & 1 deletion example/Todo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import history from '@syncstate/history';
import { useDoc } from '@syncstate/react';
import { useDoc, useComputed } from '@syncstate/react';
import { TodoItem } from './TodoItem';
import { useState, useEffect } from 'react';

Expand All @@ -18,6 +18,12 @@ export function TodoApp() {
dispatch(history.enable('/todos'));
}, []);

// const [todoLength] = useComputed('doc', getValue => {
// const todos = getValue('/todos');

// return todos.length;
// });

// console.log('todo app render');

const filteredTodos = todos
Expand Down
11 changes: 11 additions & 0 deletions example/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ const [test, setTest] = store.useDoc('/test');
setTest('kkkkkk');
// undoable(() => true);

const disposeCompute = store.computeDoc((getValue, change) => {
const todos = getValue('/todos');

// const [val, setVal] = store.useDoc("path/to/nested/data")
console.log('$$$computed todos.length', todos.length, 'change', change);
});

setTimeout(() => {
disposeCompute();
}, 5000);

console.log(store.getPatches('doc'));

const disposeObs = store.observe(
Expand Down
97 changes: 95 additions & 2 deletions src/DocStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { SyncStatePath } from './index';
import removeFirstElement from './utils/jsonPatchPathToImmerPath';
import jsonPatchPathToImmerPath from './utils/jsonPatchPathToImmerPath';
import getNextId from './utils/getNextId';
import { Watch, ComputeCallback } from 'types';
import { createPostObserveMiddleware } from './postObserveMiddleware';

type ReduxStore = Store<
CombinedState<{
Expand All @@ -31,6 +33,7 @@ export default class DocStore {
plugins: Array<any>;
private observers = new Map<number, Observer>();
private interceptors = new Map<number, Interceptor>();
private postObserveCallbacks: Array<() => void> = [];

constructor(
initialDoc: {},
Expand Down Expand Up @@ -97,6 +100,7 @@ by passing name in plugin configuration to createPlugin.
applyMiddleware(
createInterceptMiddleware(this.interceptors),
createObserveMiddleware(this.observers),
createPostObserveMiddleware(this.postObserveCallbacks),
...this.plugins.map(p => p.middleware)
)
)
Expand All @@ -117,6 +121,11 @@ by passing name in plugin configuration to createPlugin.
},
});
});

if (process.env.NODE_ENV !== 'production') {
// @ts-ignore
window['store'] = this;
}
}
getState = (subtree: string) => {
const subtreeState = this.reduxStore.getState()[subtree];
Expand Down Expand Up @@ -151,7 +160,7 @@ by passing name in plugin configuration to createPlugin.
observe = (
subtree: string,
path: string = '',
callback: any,
callback: (value: any, change: any) => void,
depth: number = 1
) => {
const observerId = getNextId();
Expand All @@ -162,15 +171,27 @@ by passing name in plugin configuration to createPlugin.
depth,
});

console.log(
'$$$$added observer with id ',
{
subtree,
path,
callback,
depth,
},
observerId
);

return () => {
this.observers.delete(observerId);
console.log('$$$$$removing observer with id ', observerId);
};
};

intercept = (
subtree: string,
path: string = '',
callback: any,
callback: (value: any, change: any) => any,
depth: number = 1
) => {
const interceptorId = getNextId();
Expand All @@ -186,7 +207,79 @@ by passing name in plugin configuration to createPlugin.
};
};

postObserve = (callback: () => void) => {
this.postObserveCallbacks.push(callback);
};

useSyncState = (subtree: string, path: string = '') =>
useSyncState(this, subtree, path);
useDoc = (path: string = '') => useSyncState(this, 'doc', path);

computeDoc = (computeCallback: ComputeCallback) => {
return this.compute('doc', computeCallback);
};

compute = (subtree: string, computeCallback: ComputeCallback) => {
let oldDispose: any;
const watch: Watch = (
watchPath: string,
depth: number = 1,
firstWatch: boolean = false
) => {
if (oldDispose) {
oldDispose();
}

if (!firstWatch) {
this.postObserve(() => {
// postObserve bcoz otherwise a new observer gets added to the end of the array when calling
// a previous observer leading to an infinite loop
console.log('$$$$compute observer 1');
const dispose = this.observe(
subtree,
watchPath,
(updatedValue, change) => {
oldDispose = dispose;
computeCallback(getValue, change);
},
depth
);
});
} else {
console.log('$$$$compute observer 2');
const dispose = this.observe(
subtree,
watchPath,
(updatedValue, change) => {
oldDispose = dispose;
computeCallback(getValue, change);
},
depth
);
}

// return dispose;
};

const getValue = (
path: string,
depth: number = 1,
firstRun: boolean = false
) => {
watch(path, depth, firstRun);
const [doc, setDoc] = this.useSyncState(subtree, path);
return doc;
};

computeCallback(
(path: string, depth: number = 1) => getValue(path, depth, true),
{}
);

return () => {
if (oldDispose) {
oldDispose();
}
};
};
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ export function createDocStore(initialDoc: {}, plugins?: Array<any>) {

export type SyncStatePath = Array<string | number>;
export { DocStore };
export * from './types';
2 changes: 1 addition & 1 deletion src/interceptMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import get from 'lodash.get';
export type Interceptor = {
subtree: string;
path: string;
callback: any;
callback: (value: any, change: any) => any;
depth: number;
};

Expand Down
4 changes: 2 additions & 2 deletions src/observeMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import get from 'lodash.get';
export type Observer = {
subtree: string;
path: string;
callback: any;
callback: (value: any, change: any) => void;
depth: number;
};

Expand All @@ -12,7 +12,7 @@ export const createObserveMiddleware = (observers: Map<number, Observer>) => {
const result = next(action);

if (action.type === 'PATCH') {
observers.forEach(observer => {
observers.forEach((observer, key) => {
const payloadPath = action.payload.patch.path;

if (observer.subtree !== action.payload.subtree) {
Expand Down
22 changes: 22 additions & 0 deletions src/postObserveMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import get from 'lodash.get';

export type Observer = {
subtree: string;
path: string;
callback: any;
depth: number;
};

export const createPostObserveMiddleware = (callbacks: Array<() => void>) => {
return (store: any) => (next: any) => (action: any) => {
const result = next(action);

callbacks.forEach((cb, index) => {
cb();
});

callbacks.length = 0;

return result;
};
};
18 changes: 9 additions & 9 deletions src/storeMethods/useSyncState.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { produceWithPatches } from 'immer';
import { Patch, produceWithPatches } from 'immer';
import DocStore from '../DocStore';
import { SyncStatePath } from '../index';
import jsonPatchPathToImmerPath from '../utils/jsonPatchPathToImmerPath';
Expand Down Expand Up @@ -33,11 +33,8 @@ export default function useSyncState(
// let parentPath = [...path];
const immerPath = jsonPatchPathToImmerPath(path);
const childKey = immerPath.pop();
path = immerPath.join('/');
stateAtPath = store.getStateAtPath(
subtree,
immerPathToJsonPatchPath(immerPath)
);
path = immerPathToJsonPatchPath(immerPath); // immerPath.join('/');
stateAtPath = store.getStateAtPath(subtree, path);
produceCallback = (draft: any) => {
if (childKey) {
draft[childKey] = callbackOrData;
Expand All @@ -54,12 +51,15 @@ export default function useSyncState(
);
// console.log(path, 'path');
// console.log(JSON.stringify(patches, null, 2), 'patches before');
patches = patches.map((p: any) => {
return { ...p, path: path + '/' + p.path.join('/') };
patches = patches.map((p: Patch) => {
return {
...p,
path: path + immerPathToJsonPatchPath(p.path),
};
});

inversePatches = inversePatches.map((p: any) => {
return { ...p, path: path + '/' + p.path.join('/') };
return { ...p, path: path + immerPathToJsonPatchPath(p.path) };
});
// console.log(JSON.stringify(patches, null, 2), 'patches');

Expand Down
10 changes: 10 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type Watch = (
path: string,
depth?: number,
firstWatch?: boolean
) => void;

export type ComputeCallback = (
getValue: (path: string, depth?: number, firstRun?: boolean) => any,
change: any
) => any;
3 changes: 3 additions & 0 deletions src/utils/escapeSlashes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function escapeSlashes(str: string | number) {
return String(str).replaceAll('/', '\\/');
}
3 changes: 2 additions & 1 deletion src/utils/immerPathToJsonPatchPath.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { SyncStatePath } from '../index';
import escapeSlashes from './escapeSlashes';

export default function immerPathToJsonPatchPath(path: SyncStatePath) {
if (path.length === 0) {
return '';
}
return '/' + path.join('/');
return '/' + path.map(p => escapeSlashes(p)).join('/');
}
7 changes: 6 additions & 1 deletion src/utils/jsonPatchPathToImmerPath.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import unescapeSlashes from './unescapeSlashes';

export default function jsonPatchPathToImmerPath(path: string) {
if (!path) {
return [];
}

path = path.replaceAll('\\/', ':::');

const immerPath = path.split('/');
immerPath.shift();
return immerPath;
return immerPath.map(p => p.replaceAll(':::', '/'));
}
3 changes: 3 additions & 0 deletions src/utils/unescapeSlashes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function unescapeSlashes(str: string | number) {
return String(str).replaceAll('\\/', '/');
}

0 comments on commit 3f4a0ce

Please sign in to comment.