Skip to content

Commit

Permalink
feat(infra): livadata
Browse files Browse the repository at this point in the history
  • Loading branch information
EYHN committed Jan 30, 2024
1 parent 588b3bc commit d0d040d
Show file tree
Hide file tree
Showing 6 changed files with 597 additions and 0 deletions.
4 changes: 4 additions & 0 deletions packages/common/infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"./command": "./src/command/index.ts",
"./atom": "./src/atom/index.ts",
"./app-config-storage": "./src/app-config-storage.ts",
"./livedata": "./src/livedata/index.ts",
".": "./src/index.ts"
},
"dependencies": {
Expand All @@ -16,9 +17,11 @@
"@blocksuite/blocks": "0.12.0-nightly-202401290223-b6302df",
"@blocksuite/global": "0.12.0-nightly-202401290223-b6302df",
"@blocksuite/store": "0.12.0-nightly-202401290223-b6302df",
"foxact": "^0.2.20",
"jotai": "^2.5.1",
"jotai-effect": "^0.2.3",
"nanoid": "^5.0.3",
"react": "18.2.0",
"tinykeys": "^2.1.0",
"yjs": "^13.6.10",
"zod": "^3.22.4"
Expand All @@ -28,6 +31,7 @@
"@affine/templates": "workspace:*",
"@blocksuite/lit": "0.12.0-nightly-202401290223-b6302df",
"@blocksuite/presets": "0.12.0-nightly-202401290223-b6302df",
"@testing-library/react": "^14.0.0",
"async-call-rpc": "^6.3.1",
"react": "^18.2.0",
"rxjs": "^7.8.1",
Expand Down
188 changes: 188 additions & 0 deletions packages/common/infra/src/livedata/__tests__/livedata.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import type { Subscriber } from 'rxjs';
import { combineLatest, Observable, of } from 'rxjs';
import { describe, expect, test, vitest } from 'vitest';

import { LiveData } from '..';

describe('livedata', () => {
test('LiveData', async () => {
const livedata = new LiveData(0);
expect(livedata.value).toBe(0);
livedata.next(1);
expect(livedata.value).toBe(1);
let subscribed = 0;
livedata.subscribe(v => {
subscribed = v;
});
livedata.next(2);
expect(livedata.value).toBe(2);
await vitest.waitFor(() => subscribed === 2);
});

test('from', async () => {
{
const livedata = LiveData.from(of(1, 2, 3, 4), 0);
expect(livedata.value).toBe(4);
}

{
let subscriber: Subscriber<number> = null!;
const observable = new Observable<number>(s => {
subscriber = s;
});
const livedata = LiveData.from(observable, 0);
let value = 0;
livedata.subscribe(v => {
value = v;
});

expect(value).toBe(0);
subscriber.next(1);
expect(value).toBe(1);
subscriber.next(2);
expect(value).toBe(2);
}

{
let observableSubscribed = false;
let observableClosed = false;
const observable = new Observable(subscriber => {
observableSubscribed = true;
subscriber.next(1);
return () => {
observableClosed = true;
};
});
const livedata = LiveData.from(observable, 0);
expect(observableSubscribed).toBe(false);
const subscription = livedata.subscribe(_ => {});
expect(observableSubscribed).toBe(true);
expect(observableClosed).toBe(false);
subscription.unsubscribe();
expect(observableClosed).toBe(true);
}

{
let subscriber: Subscriber<number> = null!;
const observable = new Observable<number>(s => {
subscriber = s;
});
const livedata = LiveData.from(observable, 0);
let value1 = 0;
livedata.subscribe(v => {
value1 = v;
});

let value2 = 0;
livedata.subscribe(v => {
value2 = v;
});

expect(value1).toBe(0);
expect(value2).toBe(0);
subscriber.next(1);
expect(value1).toBe(1);
expect(value2).toBe(1);
subscriber.next(2);
expect(value1).toBe(2);
expect(value2).toBe(2);
}

{
let observableSubscribed = false;
let observableClosed = false;
const observable = new Observable(subscriber => {
observableSubscribed = true;
subscriber.next(1);
return () => {
observableClosed = true;
};
});
const livedata = LiveData.from(observable, 0);
expect(observableSubscribed).toBe(false);
const subscription1 = livedata.subscribe(_ => {});
const subscription2 = livedata.subscribe(_ => {});
expect(observableSubscribed).toBe(true);
expect(observableClosed).toBe(false);
subscription1.unsubscribe();
expect(observableClosed).toBe(false);
subscription2.unsubscribe();
expect(observableClosed).toBe(true);
}

{
let observerCount = 0;
const observable = new Observable(_ => {
observerCount++;
});
const livedata = LiveData.from(observable, 0);
livedata.subscribe(_ => {});
livedata.subscribe(_ => {});
expect(observerCount).toBe(1);
}

{
let value = 0;
const observable = new Observable<number>(subscriber => {
subscriber.next(value);
});
const livedata = LiveData.from(observable, 0);
expect(livedata.value).toBe(0);
value = 1;
expect(livedata.value).toBe(1);
}
});

test('map', () => {
{
const livedata = new LiveData(0);
const mapped = livedata.map(v => v + 1);
expect(mapped.value).toBe(1);
livedata.next(1);
expect(mapped.value).toBe(2);
}

{
const livedata = new LiveData(0);
const mapped = livedata.map(v => v + 1);
let value = 0;
mapped.subscribe(v => {
value = v;
});
expect(value).toBe(1);
livedata.next(1);
expect(value).toBe(2);
}

{
let observableSubscribed = false;
let observableClosed = false;
const observable = new Observable<number>(subscriber => {
observableSubscribed = true;
subscriber.next(1);
return () => {
observableClosed = true;
};
});

const livedata = LiveData.from(observable, 0);
const mapped = livedata.map(v => v + 1);

expect(observableSubscribed).toBe(false);
const subscription = mapped.subscribe(_ => {});
expect(observableSubscribed).toBe(true);
expect(observableClosed).toBe(false);
subscription.unsubscribe();
expect(observableClosed).toBe(true);
}
});

test('interop with rxjs', () => {
const ob = combineLatest([new LiveData(1)]);
let value = 0;
ob.subscribe(v => {
value = v[0];
});
expect(value).toBe(1);
});
});
60 changes: 60 additions & 0 deletions packages/common/infra/src/livedata/__tests__/react.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* @vitest-environment happy-dom
*/
import { render, screen } from '@testing-library/react';
import { useRef } from 'react';
import { Observable } from 'rxjs';
import { describe, expect, test, vi } from 'vitest';

import { LiveData, useLiveData } from '..';

describe('livedata', () => {
test('react', () => {
const livedata = new LiveData(0);
const Component = () => {
const renderCount = useRef(0);
renderCount.current++;
const value = useLiveData(livedata);
return (
<main>
{renderCount.current}:{value}
</main>
);
};
const { rerender } = render(<Component />);
expect(screen.getByRole('main').innerText).toBe('1:0');
livedata.next(1);
rerender(<Component />);
expect(screen.getByRole('main').innerText).toBe('3:1');
});

test('lifecycle', async () => {
let observableSubscribed = false;
let observableClosed = false;
const observable = new Observable<number>(subscriber => {
observableSubscribed = true;
subscriber.next(1);
console.log(1);
return () => {
observableClosed = true;
};
});

const livedata = LiveData.from(observable, 0);
const Component1 = () => {
const value = useLiveData(livedata);
return <main>{value}</main>;
};

expect(observableSubscribed).toBe(false);
const { rerender } = render(<Component1 />);
expect(observableSubscribed).toBe(true);

expect(observableClosed).toBe(false);
const Component2 = () => {
return <main></main>;
};
rerender(<Component2 />);
await vi.waitUntil(() => observableClosed);
});
});
Loading

0 comments on commit d0d040d

Please sign in to comment.