style: enable rxjs/finnish (#6276)
chore(infra): use finnish notation for observables do rename
This commit is contained in:
104 files changed
+797
-591
No files matched your search
@@ -98,6 +98,7 @@ const config = {
|
|||||||
'i',
|
'i',
|
||||||
'unused-imports',
|
'unused-imports',
|
||||||
'unicorn',
|
'unicorn',
|
||||||
|
'rxjs',
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'array-callback-return': 'error',
|
'array-callback-return': 'error',
|
||||||
@@ -202,6 +203,21 @@ const config = {
|
|||||||
'sonarjs/no-collection-size-mischeck': 'error',
|
'sonarjs/no-collection-size-mischeck': 'error',
|
||||||
'sonarjs/no-useless-catch': 'error',
|
'sonarjs/no-useless-catch': 'error',
|
||||||
'sonarjs/no-identical-functions': 'error',
|
'sonarjs/no-identical-functions': 'error',
|
||||||
|
'rxjs/finnish': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
functions: false,
|
||||||
|
methods: false,
|
||||||
|
strict: true,
|
||||||
|
types: {
|
||||||
|
'^LiveData$': true,
|
||||||
|
// some yjs classes are Observables, but they don't need to be in Finnish notation
|
||||||
|
'^Doc$': false, // yjs Doc
|
||||||
|
'^Awareness$': false, // yjs Awareness
|
||||||
|
'^UndoManager$': false, // yjs UndoManager
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -82,6 +82,7 @@
|
|||||||
"eslint-plugin-i": "^2.29.1",
|
"eslint-plugin-i": "^2.29.1",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-rxjs": "^5.0.3",
|
||||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||||
"eslint-plugin-sonarjs": "^0.24.0",
|
"eslint-plugin-sonarjs": "^0.24.0",
|
||||||
"eslint-plugin-unicorn": "^51.0.1",
|
"eslint-plugin-unicorn": "^51.0.1",
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ const metricCreators: MetricCreators = {
|
|||||||
gauge(meter: Meter, name: string, opts?: MetricOptions) {
|
gauge(meter: Meter, name: string, opts?: MetricOptions) {
|
||||||
let value: any;
|
let value: any;
|
||||||
let attrs: Attributes | undefined;
|
let attrs: Attributes | undefined;
|
||||||
const ob = meter.createObservableGauge(name, opts);
|
const ob$ = meter.createObservableGauge(name, opts);
|
||||||
|
|
||||||
ob.addCallback(result => {
|
ob$.addCallback(result => {
|
||||||
result.observe(value, attrs);
|
result.observe(value, attrs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ export async function buildShowcaseWorkspace(
|
|||||||
// perhaps put them into middleware?
|
// perhaps put them into middleware?
|
||||||
{
|
{
|
||||||
// the "Write, Draw, Plan all at Once." page should be set to edgeless mode
|
// the "Write, Draw, Plan all at Once." page should be set to edgeless mode
|
||||||
const edgelessPage1 = pageRecordList.records.value.find(
|
const edgelessPage1 = pageRecordList.records$.value.find(
|
||||||
p => p.title.value === 'Write, Draw, Plan all at Once.'
|
p => p.title$.value === 'Write, Draw, Plan all at Once.'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (edgelessPage1) {
|
if (edgelessPage1) {
|
||||||
@@ -122,8 +122,8 @@ export async function buildShowcaseWorkspace(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// should jump to "Write, Draw, Plan all at Once." by default
|
// should jump to "Write, Draw, Plan all at Once." by default
|
||||||
const defaultPage = pageRecordList.records.value.find(p =>
|
const defaultPage = pageRecordList.records$.value.find(p =>
|
||||||
p.title.value.startsWith('Write, Draw, Plan all at Once.')
|
p.title$.value.startsWith('Write, Draw, Plan all at Once.')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (defaultPage) {
|
if (defaultPage) {
|
||||||
|
|||||||
@@ -6,33 +6,33 @@ import { LiveData, PoisonedError } from '..';
|
|||||||
|
|
||||||
describe('livedata', () => {
|
describe('livedata', () => {
|
||||||
test('LiveData', async () => {
|
test('LiveData', async () => {
|
||||||
const livedata = new LiveData(0);
|
const livedata$ = new LiveData(0);
|
||||||
expect(livedata.value).toBe(0);
|
expect(livedata$.value).toBe(0);
|
||||||
livedata.next(1);
|
livedata$.next(1);
|
||||||
expect(livedata.value).toBe(1);
|
expect(livedata$.value).toBe(1);
|
||||||
let subscribed = 0;
|
let subscribed = 0;
|
||||||
livedata.subscribe(v => {
|
livedata$.subscribe(v => {
|
||||||
subscribed = v;
|
subscribed = v;
|
||||||
});
|
});
|
||||||
livedata.next(2);
|
livedata$.next(2);
|
||||||
expect(livedata.value).toBe(2);
|
expect(livedata$.value).toBe(2);
|
||||||
await vitest.waitFor(() => subscribed === 2);
|
await vitest.waitFor(() => subscribed === 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('from', async () => {
|
test('from', async () => {
|
||||||
{
|
{
|
||||||
const livedata = LiveData.from(of(1, 2, 3, 4), 0);
|
const livedata$ = LiveData.from(of(1, 2, 3, 4), 0);
|
||||||
expect(livedata.value).toBe(4);
|
expect(livedata$.value).toBe(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let subscriber: Subscriber<number> = null!;
|
let subscriber: Subscriber<number> = null!;
|
||||||
const observable = new Observable<number>(s => {
|
const observable$ = new Observable<number>(s => {
|
||||||
subscriber = s;
|
subscriber = s;
|
||||||
});
|
});
|
||||||
const livedata = LiveData.from(observable, 0);
|
const livedata$ = LiveData.from(observable$, 0);
|
||||||
let value = 0;
|
let value = 0;
|
||||||
livedata.subscribe(v => {
|
livedata$.subscribe(v => {
|
||||||
value = v;
|
value = v;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -46,16 +46,16 @@ describe('livedata', () => {
|
|||||||
{
|
{
|
||||||
let observableSubscribed = false;
|
let observableSubscribed = false;
|
||||||
let observableClosed = false;
|
let observableClosed = false;
|
||||||
const observable = new Observable(subscriber => {
|
const observable$ = new Observable(subscriber => {
|
||||||
observableSubscribed = true;
|
observableSubscribed = true;
|
||||||
subscriber.next(1);
|
subscriber.next(1);
|
||||||
return () => {
|
return () => {
|
||||||
observableClosed = true;
|
observableClosed = true;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const livedata = LiveData.from(observable, 0);
|
const livedata$ = LiveData.from(observable$, 0);
|
||||||
expect(observableSubscribed).toBe(false);
|
expect(observableSubscribed).toBe(false);
|
||||||
const subscription = livedata.subscribe(_ => {});
|
const subscription = livedata$.subscribe(_ => {});
|
||||||
expect(observableSubscribed).toBe(true);
|
expect(observableSubscribed).toBe(true);
|
||||||
expect(observableClosed).toBe(false);
|
expect(observableClosed).toBe(false);
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
@@ -64,17 +64,17 @@ describe('livedata', () => {
|
|||||||
|
|
||||||
{
|
{
|
||||||
let subscriber: Subscriber<number> = null!;
|
let subscriber: Subscriber<number> = null!;
|
||||||
const observable = new Observable<number>(s => {
|
const observable$ = new Observable<number>(s => {
|
||||||
subscriber = s;
|
subscriber = s;
|
||||||
});
|
});
|
||||||
const livedata = LiveData.from(observable, 0);
|
const livedata$ = LiveData.from(observable$, 0);
|
||||||
let value1 = 0;
|
let value1 = 0;
|
||||||
livedata.subscribe(v => {
|
livedata$.subscribe(v => {
|
||||||
value1 = v;
|
value1 = v;
|
||||||
});
|
});
|
||||||
|
|
||||||
let value2 = 0;
|
let value2 = 0;
|
||||||
livedata.subscribe(v => {
|
livedata$.subscribe(v => {
|
||||||
value2 = v;
|
value2 = v;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,17 +91,17 @@ describe('livedata', () => {
|
|||||||
{
|
{
|
||||||
let observableSubscribed = false;
|
let observableSubscribed = false;
|
||||||
let observableClosed = false;
|
let observableClosed = false;
|
||||||
const observable = new Observable(subscriber => {
|
const observable$ = new Observable(subscriber => {
|
||||||
observableSubscribed = true;
|
observableSubscribed = true;
|
||||||
subscriber.next(1);
|
subscriber.next(1);
|
||||||
return () => {
|
return () => {
|
||||||
observableClosed = true;
|
observableClosed = true;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const livedata = LiveData.from(observable, 0);
|
const livedata$ = LiveData.from(observable$, 0);
|
||||||
expect(observableSubscribed).toBe(false);
|
expect(observableSubscribed).toBe(false);
|
||||||
const subscription1 = livedata.subscribe(_ => {});
|
const subscription1 = livedata$.subscribe(_ => {});
|
||||||
const subscription2 = livedata.subscribe(_ => {});
|
const subscription2 = livedata$.subscribe(_ => {});
|
||||||
expect(observableSubscribed).toBe(true);
|
expect(observableSubscribed).toBe(true);
|
||||||
expect(observableClosed).toBe(false);
|
expect(observableClosed).toBe(false);
|
||||||
subscription1.unsubscribe();
|
subscription1.unsubscribe();
|
||||||
@@ -112,31 +112,31 @@ describe('livedata', () => {
|
|||||||
|
|
||||||
{
|
{
|
||||||
let observerCount = 0;
|
let observerCount = 0;
|
||||||
const observable = new Observable(_ => {
|
const observable$ = new Observable(_ => {
|
||||||
observerCount++;
|
observerCount++;
|
||||||
});
|
});
|
||||||
const livedata = LiveData.from(observable, 0);
|
const livedata$ = LiveData.from(observable$, 0);
|
||||||
livedata.subscribe(_ => {});
|
livedata$.subscribe(_ => {});
|
||||||
livedata.subscribe(_ => {});
|
livedata$.subscribe(_ => {});
|
||||||
expect(observerCount).toBe(1);
|
expect(observerCount).toBe(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let value = 0;
|
let value = 0;
|
||||||
const observable = new Observable<number>(subscriber => {
|
const observable$ = new Observable<number>(subscriber => {
|
||||||
subscriber.next(value);
|
subscriber.next(value);
|
||||||
});
|
});
|
||||||
const livedata = LiveData.from(observable, 0);
|
const livedata$ = LiveData.from(observable$, 0);
|
||||||
expect(livedata.value).toBe(0);
|
expect(livedata$.value).toBe(0);
|
||||||
value = 1;
|
value = 1;
|
||||||
expect(livedata.value).toBe(1);
|
expect(livedata$.value).toBe(1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('poisoned', () => {
|
test('poisoned', () => {
|
||||||
{
|
{
|
||||||
let subscriber: Subscriber<number> = null!;
|
let subscriber: Subscriber<number> = null!;
|
||||||
const livedata = LiveData.from<number>(
|
const livedata$ = LiveData.from<number>(
|
||||||
new Observable(sub => {
|
new Observable(sub => {
|
||||||
subscriber = sub;
|
subscriber = sub;
|
||||||
}),
|
}),
|
||||||
@@ -145,7 +145,7 @@ describe('livedata', () => {
|
|||||||
|
|
||||||
let value: number = 0;
|
let value: number = 0;
|
||||||
let error: any = null;
|
let error: any = null;
|
||||||
livedata.subscribe({
|
livedata$.subscribe({
|
||||||
next: v => {
|
next: v => {
|
||||||
value = v;
|
value = v;
|
||||||
},
|
},
|
||||||
@@ -161,11 +161,11 @@ describe('livedata', () => {
|
|||||||
subscriber.error('error');
|
subscriber.error('error');
|
||||||
expect(error).toBeInstanceOf(PoisonedError);
|
expect(error).toBeInstanceOf(PoisonedError);
|
||||||
|
|
||||||
expect(() => livedata.next(3)).toThrowError(PoisonedError);
|
expect(() => livedata$.next(3)).toThrowError(PoisonedError);
|
||||||
expect(() => livedata.value).toThrowError(PoisonedError);
|
expect(() => livedata$.value).toThrowError(PoisonedError);
|
||||||
|
|
||||||
let error2: any = null;
|
let error2: any = null;
|
||||||
livedata.subscribe({
|
livedata$.subscribe({
|
||||||
error: e => {
|
error: e => {
|
||||||
error2 = e;
|
error2 = e;
|
||||||
},
|
},
|
||||||
@@ -176,29 +176,29 @@ describe('livedata', () => {
|
|||||||
|
|
||||||
test('map', () => {
|
test('map', () => {
|
||||||
{
|
{
|
||||||
const livedata = new LiveData(0);
|
const livedata$ = new LiveData(0);
|
||||||
const mapped = livedata.map(v => v + 1);
|
const mapped$ = livedata$.map(v => v + 1);
|
||||||
expect(mapped.value).toBe(1);
|
expect(mapped$.value).toBe(1);
|
||||||
livedata.next(1);
|
livedata$.next(1);
|
||||||
expect(mapped.value).toBe(2);
|
expect(mapped$.value).toBe(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const livedata = new LiveData(0);
|
const livedata$ = new LiveData(0);
|
||||||
const mapped = livedata.map(v => v + 1);
|
const mapped$ = livedata$.map(v => v + 1);
|
||||||
let value = 0;
|
let value = 0;
|
||||||
mapped.subscribe(v => {
|
mapped$.subscribe(v => {
|
||||||
value = v;
|
value = v;
|
||||||
});
|
});
|
||||||
expect(value).toBe(1);
|
expect(value).toBe(1);
|
||||||
livedata.next(1);
|
livedata$.next(1);
|
||||||
expect(value).toBe(2);
|
expect(value).toBe(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let observableSubscribed = false;
|
let observableSubscribed = false;
|
||||||
let observableClosed = false;
|
let observableClosed = false;
|
||||||
const observable = new Observable<number>(subscriber => {
|
const observable$ = new Observable<number>(subscriber => {
|
||||||
observableSubscribed = true;
|
observableSubscribed = true;
|
||||||
subscriber.next(1);
|
subscriber.next(1);
|
||||||
return () => {
|
return () => {
|
||||||
@@ -206,11 +206,11 @@ describe('livedata', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const livedata = LiveData.from(observable, 0);
|
const livedata$ = LiveData.from(observable$, 0);
|
||||||
const mapped = livedata.map(v => v + 1);
|
const mapped$ = livedata$.map(v => v + 1);
|
||||||
|
|
||||||
expect(observableSubscribed).toBe(false);
|
expect(observableSubscribed).toBe(false);
|
||||||
const subscription = mapped.subscribe(_ => {});
|
const subscription = mapped$.subscribe(_ => {});
|
||||||
expect(observableSubscribed).toBe(true);
|
expect(observableSubscribed).toBe(true);
|
||||||
expect(observableClosed).toBe(false);
|
expect(observableClosed).toBe(false);
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
@@ -219,9 +219,9 @@ describe('livedata', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('interop with rxjs', () => {
|
test('interop with rxjs', () => {
|
||||||
const ob = combineLatest([new LiveData(1)]);
|
const ob$ = combineLatest([new LiveData(1)]);
|
||||||
let value = 0;
|
let value = 0;
|
||||||
ob.subscribe(v => {
|
ob$.subscribe(v => {
|
||||||
value = v[0];
|
value = v[0];
|
||||||
});
|
});
|
||||||
expect(value).toBe(1);
|
expect(value).toBe(1);
|
||||||
@@ -229,69 +229,69 @@ describe('livedata', () => {
|
|||||||
|
|
||||||
test('flat', () => {
|
test('flat', () => {
|
||||||
{
|
{
|
||||||
const wrapped = new LiveData(new LiveData(0));
|
const wrapped$ = new LiveData(new LiveData(0));
|
||||||
const flatten = wrapped.flat();
|
const flatten$ = wrapped$.flat();
|
||||||
expect(flatten.value).toBe(0);
|
expect(flatten$.value).toBe(0);
|
||||||
|
|
||||||
wrapped.next(new LiveData(1));
|
wrapped$.next(new LiveData(1));
|
||||||
expect(flatten.value).toBe(1);
|
expect(flatten$.value).toBe(1);
|
||||||
|
|
||||||
wrapped.next(LiveData.from(of(2, 3), 0));
|
wrapped$.next(LiveData.from(of(2, 3), 0));
|
||||||
expect(flatten.value).toBe(3);
|
expect(flatten$.value).toBe(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const wrapped = new LiveData(
|
const wrapped$ = new LiveData(
|
||||||
new LiveData([
|
new LiveData([
|
||||||
new LiveData(new LiveData(1)),
|
new LiveData(new LiveData(1)),
|
||||||
new LiveData(new LiveData(2)),
|
new LiveData(new LiveData(2)),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
const flatten = wrapped.flat();
|
const flatten$ = wrapped$.flat();
|
||||||
expect(flatten.value).toStrictEqual([1, 2]);
|
expect(flatten$.value).toStrictEqual([1, 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const wrapped = new LiveData([new LiveData(0), new LiveData(1)]);
|
const wrapped$ = new LiveData([new LiveData(0), new LiveData(1)]);
|
||||||
const flatten = wrapped.flat();
|
const flatten$ = wrapped$.flat();
|
||||||
|
|
||||||
expect(flatten.value).toEqual([0, 1]);
|
expect(flatten$.value).toEqual([0, 1]);
|
||||||
|
|
||||||
const inner = new LiveData(2);
|
const inner$ = new LiveData(2);
|
||||||
wrapped.next([inner, new LiveData(3)]);
|
wrapped$.next([inner$, new LiveData(3)]);
|
||||||
expect(flatten.value).toEqual([2, 3]);
|
expect(flatten$.value).toEqual([2, 3]);
|
||||||
inner.next(4);
|
inner$.next(4);
|
||||||
expect(flatten.value).toEqual([4, 3]);
|
expect(flatten$.value).toEqual([4, 3]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('computed', () => {
|
test('computed', () => {
|
||||||
{
|
{
|
||||||
const a = new LiveData(1);
|
const a$ = new LiveData(1);
|
||||||
const b = LiveData.computed(get => get(a) + 1);
|
const b$ = LiveData.computed(get => get(a$) + 1);
|
||||||
expect(b.value).toBe(2);
|
expect(b$.value).toBe(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const a = new LiveData('v1');
|
const a$ = new LiveData('v1');
|
||||||
const v1 = new LiveData(100);
|
const v1$ = new LiveData(100);
|
||||||
const v2 = new LiveData(200);
|
const v2$ = new LiveData(200);
|
||||||
|
|
||||||
const v = LiveData.computed(get => {
|
const v$ = LiveData.computed(get => {
|
||||||
return get(a) === 'v1' ? get(v1) : get(v2);
|
return get(a$) === 'v1' ? get(v1$) : get(v2$);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(v.value).toBe(100);
|
expect(v$.value).toBe(100);
|
||||||
|
|
||||||
a.next('v2');
|
a$.next('v2');
|
||||||
expect(v.value).toBe(200);
|
expect(v$.value).toBe(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let watched = false;
|
let watched = false;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let subscriber: Subscriber<number> = null!;
|
let subscriber: Subscriber<number> = null!;
|
||||||
const a = LiveData.from<number>(
|
const a$ = LiveData.from<number>(
|
||||||
new Observable(sub => {
|
new Observable(sub => {
|
||||||
count++;
|
count++;
|
||||||
watched = true;
|
watched = true;
|
||||||
@@ -303,16 +303,16 @@ describe('livedata', () => {
|
|||||||
}),
|
}),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
const b = LiveData.computed(get => get(a) + 1);
|
const b$ = LiveData.computed(get => get(a$) + 1);
|
||||||
|
|
||||||
expect(watched).toBe(false);
|
expect(watched).toBe(false);
|
||||||
expect(count).toBe(0);
|
expect(count).toBe(0);
|
||||||
|
|
||||||
const subscription = b.subscribe(_ => {});
|
const subscription = b$.subscribe(_ => {});
|
||||||
expect(watched).toBe(true);
|
expect(watched).toBe(true);
|
||||||
expect(count).toBe(1);
|
expect(count).toBe(1);
|
||||||
subscriber.next(2);
|
subscriber.next(2);
|
||||||
expect(b.value).toBe(3);
|
expect(b$.value).toBe(3);
|
||||||
|
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
expect(watched).toBe(false);
|
expect(watched).toBe(false);
|
||||||
@@ -320,11 +320,11 @@ describe('livedata', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let c = null! as LiveData<number>;
|
let c$ = null! as LiveData<number>;
|
||||||
const b = LiveData.computed(get => get(c) + 1);
|
const b$ = LiveData.computed(get => get(c$) + 1);
|
||||||
c = LiveData.computed(get => get(b) + 1);
|
c$ = LiveData.computed(get => get(b$) + 1);
|
||||||
|
|
||||||
expect(() => b.value).toThrowError(PoisonedError);
|
expect(() => b$.value).toThrowError(PoisonedError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -10,11 +10,11 @@ import { LiveData, useLiveData } from '..';
|
|||||||
|
|
||||||
describe('livedata', () => {
|
describe('livedata', () => {
|
||||||
test('react', () => {
|
test('react', () => {
|
||||||
const livedata = new LiveData(0);
|
const livedata$ = new LiveData(0);
|
||||||
const Component = () => {
|
const Component = () => {
|
||||||
const renderCount = useRef(0);
|
const renderCount = useRef(0);
|
||||||
renderCount.current++;
|
renderCount.current++;
|
||||||
const value = useLiveData(livedata);
|
const value = useLiveData(livedata$);
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
{renderCount.current}:{value}
|
{renderCount.current}:{value}
|
||||||
@@ -23,7 +23,7 @@ describe('livedata', () => {
|
|||||||
};
|
};
|
||||||
const { rerender } = render(<Component />);
|
const { rerender } = render(<Component />);
|
||||||
expect(screen.getByRole('main').innerText).toBe('1:0');
|
expect(screen.getByRole('main').innerText).toBe('1:0');
|
||||||
livedata.next(1);
|
livedata$.next(1);
|
||||||
rerender(<Component />);
|
rerender(<Component />);
|
||||||
expect(screen.getByRole('main').innerText).toBe('3:1');
|
expect(screen.getByRole('main').innerText).toBe('3:1');
|
||||||
});
|
});
|
||||||
@@ -31,7 +31,7 @@ describe('livedata', () => {
|
|||||||
test('lifecycle', async () => {
|
test('lifecycle', async () => {
|
||||||
let observableSubscribed = false;
|
let observableSubscribed = false;
|
||||||
let observableClosed = false;
|
let observableClosed = false;
|
||||||
const observable = new Observable<number>(subscriber => {
|
const observable$ = new Observable<number>(subscriber => {
|
||||||
observableSubscribed = true;
|
observableSubscribed = true;
|
||||||
subscriber.next(1);
|
subscriber.next(1);
|
||||||
console.log(1);
|
console.log(1);
|
||||||
@@ -40,9 +40,9 @@ describe('livedata', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const livedata = LiveData.from(observable, 0);
|
const livedata$ = LiveData.from(observable$, 0);
|
||||||
const Component1 = () => {
|
const Component1 = () => {
|
||||||
const value = useLiveData(livedata);
|
const value = useLiveData(livedata$);
|
||||||
return <main>{value}</main>;
|
return <main>{value}</main>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
skip,
|
skip,
|
||||||
type Subscription,
|
type Subscription,
|
||||||
switchMap,
|
switchMap,
|
||||||
|
type TeardownLogic,
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { BehaviorSubject, Subject } from 'rxjs';
|
import { BehaviorSubject, Subject } from 'rxjs';
|
||||||
|
|
||||||
@@ -78,20 +79,23 @@ const logger = new DebugLogger('livedata');
|
|||||||
* @see {@link https://rxjs.dev/api/index/class/BehaviorSubject}
|
* @see {@link https://rxjs.dev/api/index/class/BehaviorSubject}
|
||||||
* @see {@link https://developer.android.com/topic/libraries/architecture/livedata}
|
* @see {@link https://developer.android.com/topic/libraries/architecture/livedata}
|
||||||
*/
|
*/
|
||||||
export class LiveData<T = unknown> implements InteropObservable<T> {
|
export class LiveData<T = unknown>
|
||||||
|
extends Observable<T>
|
||||||
|
implements InteropObservable<T>
|
||||||
|
{
|
||||||
static from<T>(
|
static from<T>(
|
||||||
upstream:
|
upstream$:
|
||||||
| Observable<T>
|
| Observable<T>
|
||||||
| InteropObservable<T>
|
| InteropObservable<T>
|
||||||
| ((stream: Observable<LiveDataOperation>) => Observable<T>),
|
| ((stream: Observable<LiveDataOperation>) => Observable<T>),
|
||||||
initialValue: T
|
initialValue: T
|
||||||
): LiveData<T> {
|
): LiveData<T> {
|
||||||
const data = new LiveData(
|
const data$ = new LiveData(
|
||||||
initialValue,
|
initialValue,
|
||||||
typeof upstream === 'function'
|
typeof upstream$ === 'function'
|
||||||
? upstream
|
? upstream$
|
||||||
: stream =>
|
: stream$ =>
|
||||||
stream.pipe(
|
stream$.pipe(
|
||||||
filter(
|
filter(
|
||||||
(op): op is Exclude<LiveDataOperation, 'set'> => op !== 'set'
|
(op): op is Exclude<LiveDataOperation, 'set'> => op !== 'set'
|
||||||
),
|
),
|
||||||
@@ -121,7 +125,7 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap(op => {
|
switchMap(op => {
|
||||||
if (op === 'watch') {
|
if (op === 'watch') {
|
||||||
return upstream;
|
return upstream$;
|
||||||
} else {
|
} else {
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}
|
}
|
||||||
@@ -129,7 +133,7 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return data;
|
return data$;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GLOBAL_COMPUTED_RECURSIVE_COUNT = 0;
|
private static GLOBAL_COMPUTED_RECURSIVE_COUNT = 0;
|
||||||
@@ -155,11 +159,11 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
new Observable(subscribe => {
|
new Observable(subscribe => {
|
||||||
const execute = (next: () => void) => {
|
const execute = (next: () => void) => {
|
||||||
const subscriptions: Subscription[] = [];
|
const subscriptions: Subscription[] = [];
|
||||||
const getfn = <L>(data: LiveData<L>) => {
|
const getfn = <L>(data$: LiveData<L>) => {
|
||||||
let value = null as L;
|
let value = null as L;
|
||||||
let first = true;
|
let first = true;
|
||||||
subscriptions.push(
|
subscriptions.push(
|
||||||
data.subscribe({
|
data$.subscribe({
|
||||||
error(err) {
|
error(err) {
|
||||||
subscribe.error(err);
|
subscribe.error(err);
|
||||||
},
|
},
|
||||||
@@ -212,8 +216,8 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly raw: BehaviorSubject<T>;
|
private readonly raw$: BehaviorSubject<T>;
|
||||||
private readonly ops = new Subject<LiveDataOperation>();
|
private readonly ops$ = new Subject<LiveDataOperation>();
|
||||||
private readonly upstreamSubscription: Subscription | undefined;
|
private readonly upstreamSubscription: Subscription | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,14 +236,15 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
| ((upstream: Observable<LiveDataOperation>) => Observable<T>)
|
| ((upstream: Observable<LiveDataOperation>) => Observable<T>)
|
||||||
| undefined = undefined
|
| undefined = undefined
|
||||||
) {
|
) {
|
||||||
this.raw = new BehaviorSubject(initialValue);
|
super();
|
||||||
|
this.raw$ = new BehaviorSubject(initialValue);
|
||||||
if (upstream) {
|
if (upstream) {
|
||||||
this.upstreamSubscription = upstream(this.ops).subscribe({
|
this.upstreamSubscription = upstream(this.ops$).subscribe({
|
||||||
next: v => {
|
next: v => {
|
||||||
this.raw.next(v);
|
this.raw$.next(v);
|
||||||
},
|
},
|
||||||
complete: () => {
|
complete: () => {
|
||||||
if (!this.raw.closed) {
|
if (!this.raw$.closed) {
|
||||||
logger.error('livedata upstream unexpected complete');
|
logger.error('livedata upstream unexpected complete');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -247,7 +252,7 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
logger.error('uncatched error in livedata', err);
|
logger.error('uncatched error in livedata', err);
|
||||||
this.isPoisoned = true;
|
this.isPoisoned = true;
|
||||||
this.poisonedError = new PoisonedError(err);
|
this.poisonedError = new PoisonedError(err);
|
||||||
this.raw.error(this.poisonedError);
|
this.raw$.error(this.poisonedError);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -257,16 +262,16 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
if (this.isPoisoned) {
|
if (this.isPoisoned) {
|
||||||
throw this.poisonedError;
|
throw this.poisonedError;
|
||||||
}
|
}
|
||||||
this.ops.next('get');
|
this.ops$.next('get');
|
||||||
return this.raw.value;
|
return this.raw$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(v: T) {
|
setValue(v: T) {
|
||||||
if (this.isPoisoned) {
|
if (this.isPoisoned) {
|
||||||
throw this.poisonedError;
|
throw this.poisonedError;
|
||||||
}
|
}
|
||||||
this.raw.next(v);
|
this.raw$.next(v);
|
||||||
this.ops.next('set');
|
this.ops$.next('set');
|
||||||
}
|
}
|
||||||
|
|
||||||
get value(): T {
|
get value(): T {
|
||||||
@@ -284,66 +289,81 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
this.setValue(v);
|
this.setValue(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(
|
override subscribe(
|
||||||
observer?: Partial<Observer<T>> | ((value: T) => void) | undefined
|
observerOrNext?: Partial<Observer<T>> | ((value: T) => void)
|
||||||
|
): Subscription;
|
||||||
|
override subscribe(
|
||||||
|
next?: ((value: T) => void) | null,
|
||||||
|
error?: ((error: any) => void) | null,
|
||||||
|
complete?: (() => void) | null
|
||||||
|
): Subscription;
|
||||||
|
override subscribe(
|
||||||
|
observerOrNext?: Partial<Observer<T>> | ((value: T) => void) | null,
|
||||||
|
error?: ((error: any) => void) | null,
|
||||||
|
complete?: (() => void) | null
|
||||||
): Subscription {
|
): Subscription {
|
||||||
this.ops.next('watch');
|
this.ops$.next('watch');
|
||||||
const subscription = this.raw.subscribe(observer);
|
const subscription = this.raw$.subscribe(
|
||||||
|
observerOrNext as any,
|
||||||
|
error,
|
||||||
|
complete
|
||||||
|
);
|
||||||
subscription.add(() => {
|
subscription.add(() => {
|
||||||
this.ops.next('unwatch');
|
this.ops$.next('unwatch');
|
||||||
});
|
});
|
||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
map<R>(mapper: (v: T) => R) {
|
map<R>(mapper: (v: T) => R): LiveData<R> {
|
||||||
const sub = LiveData.from(
|
const sub$ = LiveData.from(
|
||||||
new Observable<R>(subscriber =>
|
new Observable<R>(subscriber =>
|
||||||
this.subscribe({
|
this.subscribe({
|
||||||
next: v => {
|
next: v => {
|
||||||
subscriber.next(mapper(v));
|
subscriber.next(mapper(v));
|
||||||
},
|
},
|
||||||
complete: () => {
|
complete: () => {
|
||||||
sub.complete();
|
sub$.complete();
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
undefined as R // is safe
|
undefined as R // is safe
|
||||||
);
|
);
|
||||||
|
|
||||||
return sub;
|
return sub$;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line rxjs/finnish
|
||||||
asObservable(): Observable<T> {
|
asObservable(): Observable<T> {
|
||||||
return new Observable<T>(subscriber => {
|
return new Observable<T>(subscriber => {
|
||||||
return this.subscribe(subscriber);
|
return this.subscribe(subscriber);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pipe(): Observable<T>;
|
override pipe(): Observable<T>;
|
||||||
pipe<A>(op1: OperatorFunction<T, A>): Observable<A>;
|
override pipe<A>(op1: OperatorFunction<T, A>): Observable<A>;
|
||||||
pipe<A, B>(
|
override pipe<A, B>(
|
||||||
op1: OperatorFunction<T, A>,
|
op1: OperatorFunction<T, A>,
|
||||||
op2: OperatorFunction<A, B>
|
op2: OperatorFunction<A, B>
|
||||||
): Observable<B>;
|
): Observable<B>;
|
||||||
pipe<A, B, C>(
|
override pipe<A, B, C>(
|
||||||
op1: OperatorFunction<T, A>,
|
op1: OperatorFunction<T, A>,
|
||||||
op2: OperatorFunction<A, B>,
|
op2: OperatorFunction<A, B>,
|
||||||
op3: OperatorFunction<B, C>
|
op3: OperatorFunction<B, C>
|
||||||
): Observable<C>;
|
): Observable<C>;
|
||||||
pipe<A, B, C, D>(
|
override pipe<A, B, C, D>(
|
||||||
op1: OperatorFunction<T, A>,
|
op1: OperatorFunction<T, A>,
|
||||||
op2: OperatorFunction<A, B>,
|
op2: OperatorFunction<A, B>,
|
||||||
op3: OperatorFunction<B, C>,
|
op3: OperatorFunction<B, C>,
|
||||||
op4: OperatorFunction<C, D>
|
op4: OperatorFunction<C, D>
|
||||||
): Observable<D>;
|
): Observable<D>;
|
||||||
pipe<A, B, C, D, E>(
|
override pipe<A, B, C, D, E>(
|
||||||
op1: OperatorFunction<T, A>,
|
op1: OperatorFunction<T, A>,
|
||||||
op2: OperatorFunction<A, B>,
|
op2: OperatorFunction<A, B>,
|
||||||
op3: OperatorFunction<B, C>,
|
op3: OperatorFunction<B, C>,
|
||||||
op4: OperatorFunction<C, D>,
|
op4: OperatorFunction<C, D>,
|
||||||
op5: OperatorFunction<D, E>
|
op5: OperatorFunction<D, E>
|
||||||
): Observable<E>;
|
): Observable<E>;
|
||||||
pipe<A, B, C, D, E, F>(
|
override pipe<A, B, C, D, E, F>(
|
||||||
op1: OperatorFunction<T, A>,
|
op1: OperatorFunction<T, A>,
|
||||||
op2: OperatorFunction<A, B>,
|
op2: OperatorFunction<A, B>,
|
||||||
op3: OperatorFunction<B, C>,
|
op3: OperatorFunction<B, C>,
|
||||||
@@ -351,23 +371,23 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
op5: OperatorFunction<D, E>,
|
op5: OperatorFunction<D, E>,
|
||||||
op6: OperatorFunction<E, F>
|
op6: OperatorFunction<E, F>
|
||||||
): Observable<F>;
|
): Observable<F>;
|
||||||
pipe(...args: any[]) {
|
override pipe(...args: any[]) {
|
||||||
return new Observable(subscriber => {
|
return new Observable(subscriber => {
|
||||||
this.ops.next('watch');
|
this.ops$.next('watch');
|
||||||
// eslint-disable-next-line prefer-spread
|
// eslint-disable-next-line prefer-spread
|
||||||
const subscription = this.raw.pipe
|
const subscription = this.raw$.pipe
|
||||||
.apply(this.raw, args as any)
|
.apply(this.raw$, args as any)
|
||||||
.subscribe(subscriber);
|
.subscribe(subscriber);
|
||||||
subscription.add(() => {
|
subscription.add(() => {
|
||||||
this.ops.next('unwatch');
|
this.ops$.next('unwatch');
|
||||||
});
|
});
|
||||||
return subscription;
|
return subscription;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
complete() {
|
complete() {
|
||||||
this.ops.complete();
|
this.ops$.complete();
|
||||||
this.raw.complete();
|
this.raw$.complete();
|
||||||
this.upstreamSubscription?.unsubscribe();
|
this.upstreamSubscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,12 +431,12 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
if (this.isPoisoned) {
|
if (this.isPoisoned) {
|
||||||
throw this.poisonedError;
|
throw this.poisonedError;
|
||||||
}
|
}
|
||||||
this.ops.next('watch');
|
this.ops$.next('watch');
|
||||||
const subscription = this.raw
|
const subscription = this.raw$
|
||||||
.pipe(distinctUntilChanged(), skip(1))
|
.pipe(distinctUntilChanged(), skip(1))
|
||||||
.subscribe(cb);
|
.subscribe(cb);
|
||||||
subscription.add(() => {
|
subscription.add(() => {
|
||||||
this.ops.next('unwatch');
|
this.ops$.next('unwatch');
|
||||||
});
|
});
|
||||||
return () => subscription.unsubscribe();
|
return () => subscription.unsubscribe();
|
||||||
};
|
};
|
||||||
@@ -425,13 +445,17 @@ export class LiveData<T = unknown> implements InteropObservable<T> {
|
|||||||
if (this.isPoisoned) {
|
if (this.isPoisoned) {
|
||||||
throw this.poisonedError;
|
throw this.poisonedError;
|
||||||
}
|
}
|
||||||
this.ops.next('watch');
|
this.ops$.next('watch');
|
||||||
setImmediate(() => {
|
setImmediate(() => {
|
||||||
this.ops.next('unwatch');
|
this.ops$.next('unwatch');
|
||||||
});
|
});
|
||||||
return this.raw.value;
|
return this.raw$.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected _subscribe(): TeardownLogic {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
[Symbol.observable || '@@observable']() {
|
[Symbol.observable || '@@observable']() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ export function useLiveData<Input extends LiveData<any> | null | undefined>(
|
|||||||
/**
|
/**
|
||||||
* subscribe LiveData and return the value. If the value is nullish, will suspends until the value is not nullish.
|
* subscribe LiveData and return the value. If the value is nullish, will suspends until the value is not nullish.
|
||||||
*/
|
*/
|
||||||
export function useEnsureLiveData<T>(liveData: LiveData<T>): NonNullable<T> {
|
export function useEnsureLiveData<T>(liveData$: LiveData<T>): NonNullable<T> {
|
||||||
const data = useLiveData(liveData);
|
const data = useLiveData(liveData$);
|
||||||
|
|
||||||
if (data === null || data === undefined) {
|
if (data === null || data === undefined) {
|
||||||
return use(
|
return use(
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const subscription = liveData.subscribe({
|
const subscription = liveData$.subscribe({
|
||||||
next(value) {
|
next(value) {
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
resolve(value);
|
resolve(value);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export class PageManager {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
open(pageId: string) {
|
open(pageId: string) {
|
||||||
const pageRecord = this.pageRecordList.record(pageId).value;
|
const pageRecord = this.pageRecordList.record$(pageId).value;
|
||||||
if (!pageRecord) {
|
if (!pageRecord) {
|
||||||
throw new Error('Page record not found');
|
throw new Error('Page record not found');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ export class Doc {
|
|||||||
return this.record.id;
|
return this.record.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly mete = this.record.meta;
|
readonly mete$ = this.record.meta$;
|
||||||
readonly mode = this.record.mode;
|
readonly mode$ = this.record.mode$;
|
||||||
readonly title = this.record.title;
|
readonly title$ = this.record.title$;
|
||||||
|
|
||||||
setMode(mode: PageMode) {
|
setMode(mode: PageMode) {
|
||||||
this.record.setMode(mode);
|
this.record.setMode(mode);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class PageRecordList {
|
|||||||
private readonly localState: WorkspaceLocalState
|
private readonly localState: WorkspaceLocalState
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public readonly records = LiveData.from<PageRecord[]>(
|
public readonly records$ = LiveData.from<PageRecord[]>(
|
||||||
new Observable<string[]>(subscriber => {
|
new Observable<string[]>(subscriber => {
|
||||||
const emit = () => {
|
const emit = () => {
|
||||||
subscriber.next(
|
subscriber.next(
|
||||||
@@ -35,11 +35,11 @@ export class PageRecordList {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
public readonly isReady = this.workspace.engine.rootDocState.map(
|
public readonly isReady$ = this.workspace.engine.rootDocState$.map(
|
||||||
state => !state.syncing
|
state => !state.syncing
|
||||||
);
|
);
|
||||||
|
|
||||||
public record(id: string) {
|
public record$(id: string) {
|
||||||
return this.records.map(record => record.find(record => record.id === id));
|
return this.records$.map(record => record.find(record => record.id === id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ export class PageRecord {
|
|||||||
private readonly localState: WorkspaceLocalState
|
private readonly localState: WorkspaceLocalState
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
meta = LiveData.from<DocMeta>(
|
meta$ = LiveData.from<DocMeta>(
|
||||||
new Observable(subscriber => {
|
new Observable(subscriber => {
|
||||||
const emit = () => {
|
const emit = () => {
|
||||||
const meta = this.workspace.docCollection.meta.docMetas.find(
|
const meta = this.workspace.docCollection.meta.docMetas.find(
|
||||||
@@ -45,7 +45,7 @@ export class PageRecord {
|
|||||||
this.workspace.docCollection.setDocMeta(this.id, meta);
|
this.workspace.docCollection.setDocMeta(this.id, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
mode: LiveData<PageMode> = LiveData.from(
|
mode$: LiveData<PageMode> = LiveData.from(
|
||||||
this.localState.watch<PageMode>(`page:${this.id}:mode`),
|
this.localState.watch<PageMode>(`page:${this.id}:mode`),
|
||||||
'page'
|
'page'
|
||||||
).map(mode => (mode === 'edgeless' ? 'edgeless' : 'page'));
|
).map(mode => (mode === 'edgeless' ? 'edgeless' : 'page'));
|
||||||
@@ -55,9 +55,9 @@ export class PageRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleMode() {
|
toggleMode() {
|
||||||
this.setMode(this.mode.value === 'edgeless' ? 'page' : 'edgeless');
|
this.setMode(this.mode$.value === 'edgeless' ? 'page' : 'edgeless');
|
||||||
return this.mode.value;
|
return this.mode$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
title = this.meta.map(meta => meta.title);
|
title$ = this.meta$.map(meta => meta.title);
|
||||||
}
|
}
|
||||||
@@ -40,12 +40,12 @@ export class MemoryMemento implements Memento {
|
|||||||
private readonly data = new Map<string, LiveData<any>>();
|
private readonly data = new Map<string, LiveData<any>>();
|
||||||
|
|
||||||
private getLiveData(key: string): LiveData<any> {
|
private getLiveData(key: string): LiveData<any> {
|
||||||
let data = this.data.get(key);
|
let data$ = this.data.get(key);
|
||||||
if (!data) {
|
if (!data$) {
|
||||||
data = new LiveData<any>(null);
|
data$ = new LiveData<any>(null);
|
||||||
this.data.set(key, data);
|
this.data.set(key, data$);
|
||||||
}
|
}
|
||||||
return data;
|
return data$;
|
||||||
}
|
}
|
||||||
|
|
||||||
get<T>(key: string): T | null {
|
get<T>(key: string): T | null {
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ describe('Workspace System', () => {
|
|||||||
const provider = services.provider();
|
const provider = services.provider();
|
||||||
const workspaceManager = provider.get(WorkspaceManager);
|
const workspaceManager = provider.get(WorkspaceManager);
|
||||||
const workspaceListService = provider.get(WorkspaceListService);
|
const workspaceListService = provider.get(WorkspaceListService);
|
||||||
expect(workspaceListService.workspaceList.value.length).toBe(0);
|
expect(workspaceListService.workspaceList$.value.length).toBe(0);
|
||||||
|
|
||||||
const { workspace } = workspaceManager.open(
|
const { workspace } = workspaceManager.open(
|
||||||
await workspaceManager.createWorkspace(WorkspaceFlavour.LOCAL)
|
await workspaceManager.createWorkspace(WorkspaceFlavour.LOCAL)
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(workspaceListService.workspaceList.value.length).toBe(1);
|
expect(workspaceListService.workspaceList$.value.length).toBe(1);
|
||||||
|
|
||||||
const page = workspace.docCollection.createDoc({
|
const page = workspace.docCollection.createDoc({
|
||||||
id: 'page0',
|
id: 'page0',
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ export class DocEngine {
|
|||||||
|
|
||||||
storage: DocStorageInner;
|
storage: DocStorageInner;
|
||||||
|
|
||||||
engineState = LiveData.computed(get => {
|
engineState$ = LiveData.computed(get => {
|
||||||
const localState = get(this.localPart.engineState);
|
const localState = get(this.localPart.engineState$);
|
||||||
if (this.remotePart) {
|
if (this.remotePart) {
|
||||||
const remoteState = get(this.remotePart?.engineState);
|
const remoteState = get(this.remotePart?.engineState$);
|
||||||
return {
|
return {
|
||||||
total: remoteState.total,
|
total: remoteState.total,
|
||||||
syncing: remoteState.syncing,
|
syncing: remoteState.syncing,
|
||||||
@@ -53,12 +53,12 @@ export class DocEngine {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
docState(docId: string) {
|
docState$(docId: string) {
|
||||||
const localState = this.localPart.docState(docId);
|
const localState$ = this.localPart.docState$(docId);
|
||||||
const remoteState = this.remotePart?.docState(docId);
|
const remoteState$ = this.remotePart?.docState$(docId);
|
||||||
return LiveData.computed(get => {
|
return LiveData.computed(get => {
|
||||||
const local = get(localState);
|
const local = get(localState$);
|
||||||
const remote = remoteState ? get(remoteState) : null;
|
const remote = remoteState$ ? get(remoteState$) : null;
|
||||||
return {
|
return {
|
||||||
ready: local.ready,
|
ready: local.ready,
|
||||||
saving: local.syncing,
|
saving: local.syncing,
|
||||||
@@ -134,7 +134,7 @@ export class DocEngine {
|
|||||||
*/
|
*/
|
||||||
waitForSaved() {
|
waitForSaved() {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
this.engineState
|
this.engineState$
|
||||||
.pipe(map(state => state.saving === 0))
|
.pipe(map(state => state.saving === 0))
|
||||||
.subscribe(saved => {
|
.subscribe(saved => {
|
||||||
if (saved) {
|
if (saved) {
|
||||||
@@ -150,7 +150,7 @@ export class DocEngine {
|
|||||||
*/
|
*/
|
||||||
waitForSynced() {
|
waitForSynced() {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
this.engineState
|
this.engineState$
|
||||||
.pipe(map(state => state.syncing === 0 && state.saving === 0))
|
.pipe(map(state => state.syncing === 0 && state.saving === 0))
|
||||||
.subscribe(synced => {
|
.subscribe(synced => {
|
||||||
if (synced) {
|
if (synced) {
|
||||||
@@ -175,7 +175,7 @@ export class DocEngine {
|
|||||||
*/
|
*/
|
||||||
waitForReady(docId: string) {
|
waitForReady(docId: string) {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
this.docState(docId)
|
this.docState$(docId)
|
||||||
.pipe(map(state => state.ready))
|
.pipe(map(state => state.ready))
|
||||||
.subscribe(ready => {
|
.subscribe(ready => {
|
||||||
if (ready) {
|
if (ready) {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export interface LocalDocState {
|
|||||||
*/
|
*/
|
||||||
export class DocEngineLocalPart {
|
export class DocEngineLocalPart {
|
||||||
private readonly prioritySettings = new Map<string, number>();
|
private readonly prioritySettings = new Map<string, number>();
|
||||||
private readonly statusUpdatedSubject = new Subject<string>();
|
private readonly statusUpdatedSubject$ = new Subject<string>();
|
||||||
|
|
||||||
private readonly status = {
|
private readonly status = {
|
||||||
docs: new Map<string, YDoc>(),
|
docs: new Map<string, YDoc>(),
|
||||||
@@ -59,7 +59,7 @@ export class DocEngineLocalPart {
|
|||||||
currentJob: null as { docId: string; jobs: Job[] } | null,
|
currentJob: null as { docId: string; jobs: Job[] } | null,
|
||||||
};
|
};
|
||||||
|
|
||||||
engineState = LiveData.from<LocalEngineState>(
|
engineState$ = LiveData.from<LocalEngineState>(
|
||||||
new Observable(subscribe => {
|
new Observable(subscribe => {
|
||||||
const next = () => {
|
const next = () => {
|
||||||
subscribe.next({
|
subscribe.next({
|
||||||
@@ -68,14 +68,14 @@ export class DocEngineLocalPart {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
next();
|
next();
|
||||||
return this.statusUpdatedSubject.subscribe(() => {
|
return this.statusUpdatedSubject$.subscribe(() => {
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
{ syncing: 0, total: 0 }
|
{ syncing: 0, total: 0 }
|
||||||
);
|
);
|
||||||
|
|
||||||
docState(docId: string) {
|
docState$(docId: string) {
|
||||||
return LiveData.from<LocalDocState>(
|
return LiveData.from<LocalDocState>(
|
||||||
new Observable(subscribe => {
|
new Observable(subscribe => {
|
||||||
const next = () => {
|
const next = () => {
|
||||||
@@ -87,7 +87,7 @@ export class DocEngineLocalPart {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
next();
|
next();
|
||||||
return this.statusUpdatedSubject.subscribe(updatedId => {
|
return this.statusUpdatedSubject$.subscribe(updatedId => {
|
||||||
if (updatedId === docId) next();
|
if (updatedId === docId) next();
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
@@ -120,7 +120,7 @@ export class DocEngineLocalPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.status.currentJob = { docId, jobs };
|
this.status.currentJob = { docId, jobs };
|
||||||
this.statusUpdatedSubject.next(docId);
|
this.statusUpdatedSubject$.next(docId);
|
||||||
|
|
||||||
const { apply, load, save } = groupBy(jobs, job => job.type) as {
|
const { apply, load, save } = groupBy(jobs, job => job.type) as {
|
||||||
[key in Job['type']]?: Job[];
|
[key in Job['type']]?: Job[];
|
||||||
@@ -139,7 +139,7 @@ export class DocEngineLocalPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.status.currentJob = null;
|
this.status.currentJob = null;
|
||||||
this.statusUpdatedSubject.next(docId);
|
this.statusUpdatedSubject$.next(docId);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
dispose();
|
dispose();
|
||||||
@@ -161,7 +161,7 @@ export class DocEngineLocalPart {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.status.docs.set(doc.guid, doc);
|
this.status.docs.set(doc.guid, doc);
|
||||||
this.statusUpdatedSubject.next(doc.guid);
|
this.statusUpdatedSubject$.next(doc.guid);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ export class DocEngineLocalPart {
|
|||||||
doc.on('update', this.handleDocUpdate);
|
doc.on('update', this.handleDocUpdate);
|
||||||
|
|
||||||
this.status.connectedDocs.add(job.docId);
|
this.status.connectedDocs.add(job.docId);
|
||||||
this.statusUpdatedSubject.next(job.docId);
|
this.statusUpdatedSubject$.next(job.docId);
|
||||||
|
|
||||||
const docData = await this.storage.loadDocFromLocal(job.docId, signal);
|
const docData = await this.storage.loadDocFromLocal(job.docId, signal);
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ export class DocEngineLocalPart {
|
|||||||
|
|
||||||
this.applyUpdate(job.docId, docData);
|
this.applyUpdate(job.docId, docData);
|
||||||
this.status.readyDocs.add(job.docId);
|
this.status.readyDocs.add(job.docId);
|
||||||
this.statusUpdatedSubject.next(job.docId);
|
this.statusUpdatedSubject$.next(job.docId);
|
||||||
},
|
},
|
||||||
save: async (
|
save: async (
|
||||||
docId: string,
|
docId: string,
|
||||||
@@ -292,7 +292,7 @@ export class DocEngineLocalPart {
|
|||||||
const existingJobs = this.status.jobMap.get(job.docId) ?? [];
|
const existingJobs = this.status.jobMap.get(job.docId) ?? [];
|
||||||
existingJobs.push(job);
|
existingJobs.push(job);
|
||||||
this.status.jobMap.set(job.docId, existingJobs);
|
this.status.jobMap.set(job.docId, existingJobs);
|
||||||
this.statusUpdatedSubject.next(job.docId);
|
this.statusUpdatedSubject$.next(job.docId);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPriority(docId: string, priority: number) {
|
setPriority(docId: string, priority: number) {
|
||||||
|
|||||||
@@ -81,9 +81,9 @@ export class DocEngineRemotePart {
|
|||||||
retrying: false,
|
retrying: false,
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
};
|
};
|
||||||
private readonly statusUpdatedSubject = new Subject<string | true>();
|
private readonly statusUpdatedSubject$ = new Subject<string | true>();
|
||||||
|
|
||||||
engineState = LiveData.from<RemoteEngineState>(
|
engineState$ = LiveData.from<RemoteEngineState>(
|
||||||
new Observable(subscribe => {
|
new Observable(subscribe => {
|
||||||
const next = () => {
|
const next = () => {
|
||||||
if (!this.status.syncing) {
|
if (!this.status.syncing) {
|
||||||
@@ -103,7 +103,7 @@ export class DocEngineRemotePart {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
next();
|
next();
|
||||||
return this.statusUpdatedSubject.subscribe(() => {
|
return this.statusUpdatedSubject$.subscribe(() => {
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
@@ -115,7 +115,7 @@ export class DocEngineRemotePart {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
docState(docId: string) {
|
docState$(docId: string) {
|
||||||
return LiveData.from<RemoteDocState>(
|
return LiveData.from<RemoteDocState>(
|
||||||
new Observable(subscribe => {
|
new Observable(subscribe => {
|
||||||
const next = () => {
|
const next = () => {
|
||||||
@@ -126,7 +126,7 @@ export class DocEngineRemotePart {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
next();
|
next();
|
||||||
return this.statusUpdatedSubject.subscribe(updatedId => {
|
return this.statusUpdatedSubject$.subscribe(updatedId => {
|
||||||
if (updatedId === true || updatedId === docId) next();
|
if (updatedId === true || updatedId === docId) next();
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
@@ -152,7 +152,7 @@ export class DocEngineRemotePart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.status.connectedDocs.add(docId);
|
this.status.connectedDocs.add(docId);
|
||||||
this.statusUpdatedSubject.next(docId);
|
this.statusUpdatedSubject$.next(docId);
|
||||||
},
|
},
|
||||||
push: async (
|
push: async (
|
||||||
docId: string,
|
docId: string,
|
||||||
@@ -321,7 +321,7 @@ export class DocEngineRemotePart {
|
|||||||
addDoc: (docId: string) => {
|
addDoc: (docId: string) => {
|
||||||
if (!this.status.docs.has(docId)) {
|
if (!this.status.docs.has(docId)) {
|
||||||
this.status.docs.add(docId);
|
this.status.docs.add(docId);
|
||||||
this.statusUpdatedSubject.next(docId);
|
this.statusUpdatedSubject$.next(docId);
|
||||||
this.schedule({
|
this.schedule({
|
||||||
type: 'connect',
|
type: 'connect',
|
||||||
docId,
|
docId,
|
||||||
@@ -359,7 +359,7 @@ export class DocEngineRemotePart {
|
|||||||
logger.error('Remote sync error, retry in 5s', err);
|
logger.error('Remote sync error, retry in 5s', err);
|
||||||
this.status.errorMessage =
|
this.status.errorMessage =
|
||||||
err instanceof Error ? err.message : `${err}`;
|
err instanceof Error ? err.message : `${err}`;
|
||||||
this.statusUpdatedSubject.next(true);
|
this.statusUpdatedSubject$.next(true);
|
||||||
} finally {
|
} finally {
|
||||||
this.status = {
|
this.status = {
|
||||||
docs: this.status.docs,
|
docs: this.status.docs,
|
||||||
@@ -371,7 +371,7 @@ export class DocEngineRemotePart {
|
|||||||
retrying: true,
|
retrying: true,
|
||||||
errorMessage: this.status.errorMessage,
|
errorMessage: this.status.errorMessage,
|
||||||
};
|
};
|
||||||
this.statusUpdatedSubject.next(true);
|
this.statusUpdatedSubject$.next(true);
|
||||||
}
|
}
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
new Promise<void>(resolve => {
|
new Promise<void>(resolve => {
|
||||||
@@ -420,7 +420,7 @@ export class DocEngineRemotePart {
|
|||||||
|
|
||||||
logger.info('Remote sync started');
|
logger.info('Remote sync started');
|
||||||
this.status.syncing = true;
|
this.status.syncing = true;
|
||||||
this.statusUpdatedSubject.next(true);
|
this.statusUpdatedSubject$.next(true);
|
||||||
|
|
||||||
this.server.onInterrupted(reason => {
|
this.server.onInterrupted(reason => {
|
||||||
abort.abort(reason);
|
abort.abort(reason);
|
||||||
@@ -471,7 +471,7 @@ export class DocEngineRemotePart {
|
|||||||
const jobs = this.status.jobMap.get(docId);
|
const jobs = this.status.jobMap.get(docId);
|
||||||
if (!jobs || jobs.length === 0) {
|
if (!jobs || jobs.length === 0) {
|
||||||
this.status.jobMap.delete(docId);
|
this.status.jobMap.delete(docId);
|
||||||
this.statusUpdatedSubject.next(docId);
|
this.statusUpdatedSubject$.next(docId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,7 +535,7 @@ export class DocEngineRemotePart {
|
|||||||
const existingJobs = this.status.jobMap.get(job.docId) ?? [];
|
const existingJobs = this.status.jobMap.get(job.docId) ?? [];
|
||||||
existingJobs.push(job);
|
existingJobs.push(job);
|
||||||
this.status.jobMap.set(job.docId, existingJobs);
|
this.status.jobMap.set(job.docId, existingJobs);
|
||||||
this.statusUpdatedSubject.next(job.docId);
|
this.statusUpdatedSubject$.next(job.docId);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPriority(docId: string, priority: number) {
|
setPriority(docId: string, priority: number) {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export class WorkspaceEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canGracefulStop() {
|
canGracefulStop() {
|
||||||
return this.doc.engineState.value.saving === 0;
|
return this.doc.engineState$.value.saving === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForGracefulStop(abort?: AbortSignal) {
|
async waitForGracefulStop(abort?: AbortSignal) {
|
||||||
@@ -67,9 +67,9 @@ export class WorkspaceEngine {
|
|||||||
this.blob.stop();
|
this.blob.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
docEngineState = this.doc.engineState;
|
docEngineState$ = this.doc.engineState$;
|
||||||
|
|
||||||
rootDocState = this.doc.docState(this.yDoc.guid);
|
rootDocState$ = this.doc.docState$(this.yDoc.guid);
|
||||||
|
|
||||||
waitForSynced() {
|
waitForSynced() {
|
||||||
return this.doc.waitForSynced();
|
return this.doc.waitForSynced();
|
||||||
|
|||||||
@@ -86,18 +86,18 @@ export class WorkspaceListService {
|
|||||||
WorkspaceInformation
|
WorkspaceInformation
|
||||||
>();
|
>();
|
||||||
|
|
||||||
status = new LiveData<WorkspaceListStatus>({
|
status$ = new LiveData<WorkspaceListStatus>({
|
||||||
loading: true,
|
loading: true,
|
||||||
workspaceList: [],
|
workspaceList: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
setStatus(status: WorkspaceListStatus) {
|
setStatus(status: WorkspaceListStatus) {
|
||||||
this.status.next(status);
|
this.status$.next(status);
|
||||||
// update cache
|
// update cache
|
||||||
writeWorkspaceListCache(this.cache, status.workspaceList);
|
writeWorkspaceListCache(this.cache, status.workspaceList);
|
||||||
}
|
}
|
||||||
|
|
||||||
workspaceList = this.status.map(x => x.workspaceList);
|
workspaceList$ = this.status$.map(x => x.workspaceList);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly providers: WorkspaceListProvider[],
|
private readonly providers: WorkspaceListProvider[],
|
||||||
@@ -106,8 +106,8 @@ export class WorkspaceListService {
|
|||||||
// initialize workspace list from cache
|
// initialize workspace list from cache
|
||||||
const cached = readWorkspaceListCache(cache);
|
const cached = readWorkspaceListCache(cache);
|
||||||
const workspaceList = cached;
|
const workspaceList = cached;
|
||||||
this.status.next({
|
this.status$.next({
|
||||||
...this.status.value,
|
...this.status$.value,
|
||||||
workspaceList,
|
workspaceList,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ export class WorkspaceListService {
|
|||||||
}
|
}
|
||||||
const metadata = await provider.create(initial);
|
const metadata = await provider.create(initial);
|
||||||
// update workspace list
|
// update workspace list
|
||||||
this.setStatus(this.addWorkspace(this.status.value, metadata));
|
this.setStatus(this.addWorkspace(this.status$.value, metadata));
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ export class WorkspaceListService {
|
|||||||
await provider.delete(workspaceMetadata.id);
|
await provider.delete(workspaceMetadata.id);
|
||||||
|
|
||||||
// delete workspace from list
|
// delete workspace from list
|
||||||
this.setStatus(this.deleteWorkspace(this.status.value, workspaceMetadata));
|
this.setStatus(this.deleteWorkspace(this.status$.value, workspaceMetadata));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,7 +201,7 @@ export class WorkspaceListService {
|
|||||||
added?: WorkspaceMetadata[];
|
added?: WorkspaceMetadata[];
|
||||||
deleted?: WorkspaceMetadata[];
|
deleted?: WorkspaceMetadata[];
|
||||||
}) {
|
}) {
|
||||||
let status = this.status.value;
|
let status = this.status$.value;
|
||||||
|
|
||||||
for (const added of changed.added ?? []) {
|
for (const added of changed.added ?? []) {
|
||||||
status = this.addWorkspace(status, added);
|
status = this.addWorkspace(status, added);
|
||||||
@@ -239,7 +239,7 @@ export class WorkspaceListService {
|
|||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.setStatus({
|
this.setStatus({
|
||||||
...this.status.value,
|
...this.status$.value,
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -250,7 +250,7 @@ export class WorkspaceListService {
|
|||||||
this.providers.map(async provider => {
|
this.providers.map(async provider => {
|
||||||
try {
|
try {
|
||||||
const list = await provider.getList();
|
const list = await provider.getList();
|
||||||
const oldList = this.workspaceList.value.filter(
|
const oldList = this.workspaceList$.value.filter(
|
||||||
w => w.flavour === provider.name
|
w => w.flavour === provider.name
|
||||||
);
|
);
|
||||||
this.handleWorkspaceChange({
|
this.handleWorkspaceChange({
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ export const DumpInfo = (_props: DumpInfoProps) => {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const workspaceList = useService(WorkspaceListService);
|
const workspaceList = useService(WorkspaceListService);
|
||||||
const currentWorkspace = useLiveData(
|
const currentWorkspace = useLiveData(
|
||||||
useService(CurrentWorkspaceService).currentWorkspace
|
useService(CurrentWorkspaceService).currentWorkspace$
|
||||||
);
|
);
|
||||||
const path = location.pathname;
|
const path = location.pathname;
|
||||||
const query = useParams();
|
const query = useParams();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { CurrentWorkspaceService } from '../../../modules/workspace/current-work
|
|||||||
const SyncAwarenessInnerLoggedIn = () => {
|
const SyncAwarenessInnerLoggedIn = () => {
|
||||||
const { user } = useSession();
|
const { user } = useSession();
|
||||||
const currentWorkspace = useLiveData(
|
const currentWorkspace = useLiveData(
|
||||||
useService(CurrentWorkspaceService).currentWorkspace
|
useService(CurrentWorkspaceService).currentWorkspace$
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ const PageHistoryManager = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const page = useService(Doc);
|
const page = useService(Doc);
|
||||||
const [mode, setMode] = useState<PageMode>(page.mode.value);
|
const [mode, setMode] = useState<PageMode>(page.mode$.value);
|
||||||
|
|
||||||
const title = useDocCollectionPageTitle(docCollection, pageId);
|
const title = useDocCollectionPageTitle(docCollection, pageId);
|
||||||
|
|
||||||
|
|||||||
+15
-19
@@ -42,8 +42,8 @@ const InlineTagsList = ({
|
|||||||
children,
|
children,
|
||||||
}: PropsWithChildren<InlineTagsListProps>) => {
|
}: PropsWithChildren<InlineTagsListProps>) => {
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const tags = useLiveData(tagService.tags);
|
const tags = useLiveData(tagService.tags$);
|
||||||
const tagIds = useLiveData(tagService.tagIdsByPageId(pageId));
|
const tagIds = useLiveData(tagService.tagIdsByPageId$(pageId));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.inlineTagsContainer} data-testid="inline-tags-list">
|
<div className={styles.inlineTagsContainer} data-testid="inline-tags-list">
|
||||||
@@ -83,9 +83,9 @@ export const EditTagMenu = ({
|
|||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const legacyProperties = useService(WorkspaceLegacyProperties);
|
const legacyProperties = useService(WorkspaceLegacyProperties);
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const tag = useLiveData(tagService.tagByTagId(tagId));
|
const tag = useLiveData(tagService.tagByTagId$(tagId));
|
||||||
const tagColor = useLiveData(tag?.color);
|
const tagColor = useLiveData(tag?.color$);
|
||||||
const tagValue = useLiveData(tag?.value);
|
const tagValue = useLiveData(tag?.value$);
|
||||||
const navigate = useNavigateHelper();
|
const navigate = useNavigateHelper();
|
||||||
|
|
||||||
const menuProps = useMemo(() => {
|
const menuProps = useMemo(() => {
|
||||||
@@ -135,11 +135,11 @@ export const EditTagMenu = ({
|
|||||||
options.push('-');
|
options.push('-');
|
||||||
|
|
||||||
options.push(
|
options.push(
|
||||||
tagColors.map(([name, color]) => {
|
tagColors.map(([name, color], i) => {
|
||||||
return {
|
return {
|
||||||
text: name,
|
text: name,
|
||||||
icon: (
|
icon: (
|
||||||
<div className={styles.tagColorIconWrapper}>
|
<div key={i} className={styles.tagColorIconWrapper}>
|
||||||
<div
|
<div
|
||||||
className={styles.tagColorIcon}
|
className={styles.tagColorIcon}
|
||||||
style={{
|
style={{
|
||||||
@@ -181,8 +181,8 @@ export const EditTagMenu = ({
|
|||||||
export const TagsEditor = ({ pageId, readonly }: TagsEditorProps) => {
|
export const TagsEditor = ({ pageId, readonly }: TagsEditorProps) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const tags = useLiveData(tagService.tags);
|
const tags = useLiveData(tagService.tags$);
|
||||||
const tagIds = useLiveData(tagService.tagIdsByPageId(pageId));
|
const tagIds = useLiveData(tagService.tagIdsByPageId$(pageId));
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
|
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
|
||||||
@@ -203,15 +203,11 @@ export const TagsEditor = ({ pageId, readonly }: TagsEditorProps) => {
|
|||||||
[setOpen, setSelectedTagIds]
|
[setOpen, setSelectedTagIds]
|
||||||
);
|
);
|
||||||
|
|
||||||
const exactMatch = useLiveData(tagService.tagByTagValue(inputValue));
|
const exactMatch = useLiveData(tagService.tagByTagValue$(inputValue));
|
||||||
|
|
||||||
const filteredLiveData = useMemo(() => {
|
const filteredTags = useLiveData(
|
||||||
if (inputValue) {
|
inputValue ? tagService.filterTagsByName$(inputValue) : tagService.tags$
|
||||||
return tagService.filterTagsByName(inputValue);
|
);
|
||||||
}
|
|
||||||
return tagService.tags;
|
|
||||||
}, [inputValue, tagService]);
|
|
||||||
const filteredTags = useLiveData(filteredLiveData);
|
|
||||||
|
|
||||||
const onInputChange = useCallback(
|
const onInputChange = useCallback(
|
||||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
@@ -295,7 +291,7 @@ export const TagsEditor = ({ pageId, readonly }: TagsEditorProps) => {
|
|||||||
className={styles.tagSelectorItem}
|
className={styles.tagSelectorItem}
|
||||||
data-testid="tag-selector-item"
|
data-testid="tag-selector-item"
|
||||||
data-tag-id={tag.id}
|
data-tag-id={tag.id}
|
||||||
data-tag-value={tag.value}
|
data-tag-value={tag.value$}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onAddTag(tag.id);
|
onAddTag(tag.id);
|
||||||
}}
|
}}
|
||||||
@@ -351,7 +347,7 @@ export const TagsInlineEditor = ({
|
|||||||
className,
|
className,
|
||||||
}: TagsInlineEditorProps) => {
|
}: TagsInlineEditorProps) => {
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const tagIds = useLiveData(tagService.tagIdsByPageId(pageId));
|
const tagIds = useLiveData(tagService.tagIdsByPageId$(pageId));
|
||||||
const empty = !tagIds || tagIds.length === 0;
|
const empty = !tagIds || tagIds.length === 0;
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
|
|||||||
+1
-1
@@ -195,7 +195,7 @@ export const WorkspaceList = ({
|
|||||||
activeSubTab: WorkspaceSubTab;
|
activeSubTab: WorkspaceSubTab;
|
||||||
}) => {
|
}) => {
|
||||||
const workspaces = useLiveData(
|
const workspaces = useLiveData(
|
||||||
useService(WorkspaceManager).list.workspaceList
|
useService(WorkspaceManager).list.workspaceList$
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ export const DeleteLeaveWorkspace = ({
|
|||||||
const setSettingModal = useSetAtom(openSettingModalAtom);
|
const setSettingModal = useSetAtom(openSettingModalAtom);
|
||||||
|
|
||||||
const workspaceManager = useService(WorkspaceManager);
|
const workspaceManager = useService(WorkspaceManager);
|
||||||
const workspaceList = useLiveData(workspaceManager.list.workspaceList);
|
const workspaceList = useLiveData(workspaceManager.list.workspaceList$);
|
||||||
const currentWorkspace = useService(Workspace);
|
const currentWorkspace = useService(Workspace);
|
||||||
const pushNotification = useSetAtom(pushNotificationAtom);
|
const pushNotification = useSetAtom(pushNotificationAtom);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ export const ProfilePanel = ({ isOwner, workspace }: ProfilePanelProps) => {
|
|||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const pushNotification = useSetAtom(pushNotificationAtom);
|
const pushNotification = useSetAtom(pushNotificationAtom);
|
||||||
|
|
||||||
const workspaceIsReady = useLiveData(workspace?.engine.rootDocState)?.ready;
|
const workspaceIsReady = useLiveData(workspace?.engine.rootDocState$)?.ready;
|
||||||
|
|
||||||
const [avatarBlob, setAvatarBlob] = useState<string | null>(null);
|
const [avatarBlob, setAvatarBlob] = useState<string | null>(null);
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ export const ShareExport = ({
|
|||||||
urlType: 'workspace',
|
urlType: 'workspace',
|
||||||
});
|
});
|
||||||
const exportHandler = useExportPage(currentPage);
|
const exportHandler = useExportPage(currentPage);
|
||||||
const currentMode = useLiveData(page.mode);
|
const currentMode = useLiveData(page.mode$);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
+1
-1
@@ -66,7 +66,7 @@ export const AffineSharePage = (props: ShareMenuProps) => {
|
|||||||
disableShare,
|
disableShare,
|
||||||
} = useIsSharedPage(workspaceId, currentPage.id);
|
} = useIsSharedPage(workspaceId, currentPage.id);
|
||||||
|
|
||||||
const currentPageMode = useLiveData(page.mode);
|
const currentPageMode = useLiveData(page.mode$);
|
||||||
|
|
||||||
const defaultMode = useMemo(() => {
|
const defaultMode = useMemo(() => {
|
||||||
if (isSharedPage) {
|
if (isSharedPage) {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const PageHeaderMenuButton = ({
|
|||||||
meta => meta.id === pageId
|
meta => meta.id === pageId
|
||||||
);
|
);
|
||||||
const page = useService(Doc);
|
const page = useService(Doc);
|
||||||
const currentMode = useLiveData(page.mode);
|
const currentMode = useLiveData(page.mode$);
|
||||||
|
|
||||||
const { favorite, toggleFavorite } = useFavorite(pageId);
|
const { favorite, toggleFavorite } = useFavorite(pageId);
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export const EditorModeSwitch = ({
|
|||||||
const trash = pageMeta?.trash ?? false;
|
const trash = pageMeta?.trash ?? false;
|
||||||
const page = useService(Doc);
|
const page = useService(Doc);
|
||||||
|
|
||||||
const currentMode = useLiveData(page.mode);
|
const currentMode = useLiveData(page.mode$);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (trash || isPublic) {
|
if (trash || isPublic) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const usePageHelper = (docCollection: DocCollection) => {
|
|||||||
|
|
||||||
const isPreferredEdgeless = useCallback(
|
const isPreferredEdgeless = useCallback(
|
||||||
(pageId: string) =>
|
(pageId: string) =>
|
||||||
pageRecordList.record(pageId).value?.mode.value === 'edgeless',
|
pageRecordList.record$(pageId).value?.mode$.value === 'edgeless',
|
||||||
[pageRecordList]
|
[pageRecordList]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ export const usePageHelper = (docCollection: DocCollection) => {
|
|||||||
(mode?: 'page' | 'edgeless') => {
|
(mode?: 'page' | 'edgeless') => {
|
||||||
const page = createDoc();
|
const page = createDoc();
|
||||||
initEmptyPage(page);
|
initEmptyPage(page);
|
||||||
pageRecordList.record(page.id).value?.setMode(mode || 'page');
|
pageRecordList.record$(page.id).value?.setMode(mode || 'page');
|
||||||
openPage(docCollection.id, page.id);
|
openPage(docCollection.id, page.id);
|
||||||
return page;
|
return page;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({
|
|||||||
isPublic,
|
isPublic,
|
||||||
publishMode,
|
publishMode,
|
||||||
}: PageDetailEditorProps & { page: BlockSuiteDoc }) {
|
}: PageDetailEditorProps & { page: BlockSuiteDoc }) {
|
||||||
const currentMode = useLiveData(useService(Doc).mode);
|
const currentMode = useLiveData(useService(Doc).mode$);
|
||||||
const mode = useMemo(() => {
|
const mode = useMemo(() => {
|
||||||
const shareMode = publishMode || currentMode;
|
const shareMode = publishMode || currentMode;
|
||||||
|
|
||||||
|
|||||||
@@ -94,8 +94,8 @@ export const TagPageListHeader = ({
|
|||||||
tag: Tag;
|
tag: Tag;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const tagColor = useLiveData(tag.color);
|
const tagColor = useLiveData(tag.color$);
|
||||||
const tagTitle = useLiveData(tag.value);
|
const tagTitle = useLiveData(tag.value$);
|
||||||
|
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const { jumpToTags, jumpToCollection } = useNavigateHelper();
|
const { jumpToTags, jumpToCollection } = useNavigateHelper();
|
||||||
@@ -183,13 +183,9 @@ export const SwitchTag = ({ onClick }: SwitchTagProps) => {
|
|||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const filteredLiveData = useMemo(() => {
|
const filteredTags = useLiveData(
|
||||||
if (inputValue) {
|
inputValue ? tagService.filterTagsByName$(inputValue) : tagService.tags$
|
||||||
return tagService.filterTagsByName(inputValue);
|
);
|
||||||
}
|
|
||||||
return tagService.tags;
|
|
||||||
}, [inputValue, tagService]);
|
|
||||||
const filteredTags = useLiveData(filteredLiveData);
|
|
||||||
|
|
||||||
const onInputChange = useCallback(
|
const onInputChange = useCallback(
|
||||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
@@ -238,8 +234,8 @@ export const SwitchTag = ({ onClick }: SwitchTagProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TagLink = ({ tag, onClick }: { tag: Tag; onClick: () => void }) => {
|
const TagLink = ({ tag, onClick }: { tag: Tag; onClick: () => void }) => {
|
||||||
const tagColor = useLiveData(tag.color);
|
const tagColor = useLiveData(tag.color$);
|
||||||
const tagTitle = useLiveData(tag.value);
|
const tagTitle = useLiveData(tag.value$);
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={tag.id}
|
key={tag.id}
|
||||||
|
|||||||
@@ -69,8 +69,7 @@ const PageSelectionCell = ({
|
|||||||
|
|
||||||
export const PageTagsCell = ({ pageId }: Pick<PageListItemProps, 'pageId'>) => {
|
export const PageTagsCell = ({ pageId }: Pick<PageListItemProps, 'pageId'>) => {
|
||||||
const tagsService = useService(TagService);
|
const tagsService = useService(TagService);
|
||||||
const tagsLiveData = tagsService.tagsByPageId(pageId);
|
const tags = useLiveData(tagsService.tagsByPageId$(pageId));
|
||||||
const tags = useLiveData(tagsLiveData);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="page-list-item-tags" className={styles.tagsCell}>
|
<div data-testid="page-list-item-tags" className={styles.tagsCell}>
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ export const TagItem = ({
|
|||||||
style,
|
style,
|
||||||
maxWidth,
|
maxWidth,
|
||||||
}: TagItemProps) => {
|
}: TagItemProps) => {
|
||||||
const value = useLiveData(tag?.value);
|
const value = useLiveData(tag?.value$);
|
||||||
const color = useLiveData(tag?.color);
|
const color = useLiveData(tag?.color$);
|
||||||
const handleRemove: MouseEventHandler = useCallback(
|
const handleRemove: MouseEventHandler = useCallback(
|
||||||
e => {
|
e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -112,13 +112,13 @@ const TagItemNormal = ({
|
|||||||
return maxItems ? tags.slice(0, maxItems) : tags;
|
return maxItems ? tags.slice(0, maxItems) : tags;
|
||||||
}, [maxItems, tags]);
|
}, [maxItems, tags]);
|
||||||
|
|
||||||
const tagsOrderedLiveData = useMemo(() => {
|
const tagsOrdered = useLiveData(
|
||||||
return LiveData.computed(get =>
|
useMemo(() => {
|
||||||
[...nTags].sort((a, b) => get(a.value).length - get(b.value).length)
|
return LiveData.computed(get =>
|
||||||
);
|
[...nTags].sort((a, b) => get(a.value$).length - get(b.value$).length)
|
||||||
}, [nTags]);
|
);
|
||||||
|
}, [nTags])
|
||||||
const tagsOrdered = useLiveData(tagsOrderedLiveData);
|
);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -275,11 +275,11 @@ function tagIdToTagOption(
|
|||||||
|
|
||||||
const PageTitle = ({ id }: { id: string }) => {
|
const PageTitle = ({ id }: { id: string }) => {
|
||||||
const page = useLiveData(
|
const page = useLiveData(
|
||||||
useService(PageRecordList).records.map(record => {
|
useService(PageRecordList).records$.map(record => {
|
||||||
return record.find(p => p.id === id);
|
return record.find(p => p.id === id);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
const title = useLiveData(page?.title);
|
const title = useLiveData(page?.title$);
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
return title || t['Untitled']();
|
return title || t['Untitled']();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ export const CreateOrEditTag = ({
|
|||||||
tagMeta?: TagMeta;
|
tagMeta?: TagMeta;
|
||||||
}) => {
|
}) => {
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const tagOptions = useLiveData(tagService.tagMetas);
|
const tagOptions = useLiveData(tagService.tagMetas$);
|
||||||
const tag = useLiveData(tagService.tagByTagId(tagMeta?.id));
|
const tag = useLiveData(tagService.tagByTagId$(tagMeta?.id));
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ export const pageToCommand = (
|
|||||||
subTitle?: string,
|
subTitle?: string,
|
||||||
blockId?: string
|
blockId?: string
|
||||||
): CMDKCommand => {
|
): CMDKCommand => {
|
||||||
const pageMode = workspace.services.get(PageRecordList).record(page.id).value
|
const pageMode = workspace.services.get(PageRecordList).record$(page.id).value
|
||||||
?.mode.value;
|
?.mode$.value;
|
||||||
|
|
||||||
const title = getPageTitle(page.id) || t['Untitled']();
|
const title = getPageTitle(page.id) || t['Untitled']();
|
||||||
const commandLabel = {
|
const commandLabel = {
|
||||||
@@ -320,7 +320,7 @@ export const collectionToCommand = (
|
|||||||
export const useCollectionsCommands = () => {
|
export const useCollectionsCommands = () => {
|
||||||
// todo: considering collections for searching pages
|
// todo: considering collections for searching pages
|
||||||
const collectionService = useService(CollectionService);
|
const collectionService = useService(CollectionService);
|
||||||
const collections = useLiveData(collectionService.collections);
|
const collections = useLiveData(collectionService.collections$);
|
||||||
const query = useAtomValue(cmdkQueryAtom);
|
const query = useAtomValue(cmdkQueryAtom);
|
||||||
const navigationHelper = useNavigateHelper();
|
const navigationHelper = useNavigateHelper();
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
@@ -356,7 +356,7 @@ export const useCMDKCommandGroups = () => {
|
|||||||
const collectionCommands = useCollectionsCommands();
|
const collectionCommands = useCollectionsCommands();
|
||||||
|
|
||||||
const currentPage = useServiceOptional(Doc);
|
const currentPage = useServiceOptional(Doc);
|
||||||
const currentPageMode = useLiveData(currentPage?.mode);
|
const currentPageMode = useLiveData(currentPage?.mode$);
|
||||||
const affineCommands = useMemo(() => {
|
const affineCommands = useMemo(() => {
|
||||||
return getAllCommand({
|
return getAllCommand({
|
||||||
pageMode: currentPageMode,
|
pageMode: currentPageMode,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const showList = environment.isDesktop ? DESKTOP_SHOW_LIST : DEFAULT_SHOW_LIST;
|
|||||||
export const HelpIsland = () => {
|
export const HelpIsland = () => {
|
||||||
const page = useServiceOptional(Doc);
|
const page = useServiceOptional(Doc);
|
||||||
const pageId = page?.id;
|
const pageId = page?.id;
|
||||||
const mode = useLiveData(page?.mode);
|
const mode = useLiveData(page?.mode$);
|
||||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||||
const [spread, setShowSpread] = useState(false);
|
const [spread, setShowSpread] = useState(false);
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import * as styles from './styles.css';
|
|||||||
|
|
||||||
export const TrashPageFooter = ({ pageId }: { pageId: string }) => {
|
export const TrashPageFooter = ({ pageId }: { pageId: string }) => {
|
||||||
const workspace = useLiveData(
|
const workspace = useLiveData(
|
||||||
useService(CurrentWorkspaceService).currentWorkspace
|
useService(CurrentWorkspaceService).currentWorkspace$
|
||||||
);
|
);
|
||||||
assertExists(workspace);
|
assertExists(workspace);
|
||||||
const docCollection = workspace.docCollection;
|
const docCollection = workspace.docCollection;
|
||||||
|
|||||||
+2
-2
@@ -89,7 +89,7 @@ const CollectionRenderer = ({
|
|||||||
};
|
};
|
||||||
return filterPage(collection, pageData);
|
return filterPage(collection, pageData);
|
||||||
});
|
});
|
||||||
const location = useLiveData(useService(Workbench).location);
|
const location = useLiveData(useService(Workbench).location$);
|
||||||
const currentPath = location.pathname;
|
const currentPath = location.pathname;
|
||||||
const path = `/collection/${collection.id}`;
|
const path = `/collection/${collection.id}`;
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ export const CollectionsList = ({
|
|||||||
onCreate,
|
onCreate,
|
||||||
}: CollectionsListProps) => {
|
}: CollectionsListProps) => {
|
||||||
const metas = useBlockSuiteDocMeta(workspace);
|
const metas = useBlockSuiteDocMeta(workspace);
|
||||||
const collections = useLiveData(useService(CollectionService).collections);
|
const collections = useLiveData(useService(CollectionService).collections$);
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
if (collections.length === 0) {
|
if (collections.length === 0) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
+2
-2
@@ -36,8 +36,8 @@ export const Page = ({
|
|||||||
|
|
||||||
const pageId = page.id;
|
const pageId = page.id;
|
||||||
const active = params.pageId === pageId;
|
const active = params.pageId === pageId;
|
||||||
const pageRecord = useLiveData(useService(PageRecordList).record(pageId));
|
const pageRecord = useLiveData(useService(PageRecordList).record$(pageId));
|
||||||
const pageMode = useLiveData(pageRecord?.mode);
|
const pageMode = useLiveData(pageRecord?.mode$);
|
||||||
const dragItemId = getDragItemId('collectionPage', pageId);
|
const dragItemId = getDragItemId('collectionPage', pageId);
|
||||||
|
|
||||||
const icon = useMemo(() => {
|
const icon = useMemo(() => {
|
||||||
|
|||||||
+2
-2
@@ -27,8 +27,8 @@ export const ReferencePage = ({
|
|||||||
const params = useParams();
|
const params = useParams();
|
||||||
const active = params.pageId === pageId;
|
const active = params.pageId === pageId;
|
||||||
|
|
||||||
const pageRecord = useLiveData(useService(PageRecordList).record(pageId));
|
const pageRecord = useLiveData(useService(PageRecordList).record$(pageId));
|
||||||
const pageMode = useLiveData(pageRecord?.mode);
|
const pageMode = useLiveData(pageRecord?.mode$);
|
||||||
const icon = useMemo(() => {
|
const icon = useMemo(() => {
|
||||||
return pageMode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
return pageMode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
||||||
}, [pageMode]);
|
}, [pageMode]);
|
||||||
|
|||||||
+2
-2
@@ -29,8 +29,8 @@ export const FavouritePage = ({
|
|||||||
const params = useParams();
|
const params = useParams();
|
||||||
const active = params.pageId === pageId;
|
const active = params.pageId === pageId;
|
||||||
const dragItemId = getDragItemId('favouritePage', pageId);
|
const dragItemId = getDragItemId('favouritePage', pageId);
|
||||||
const pageRecord = useLiveData(useService(PageRecordList).record(pageId));
|
const pageRecord = useLiveData(useService(PageRecordList).record$(pageId));
|
||||||
const pageMode = useLiveData(pageRecord?.mode);
|
const pageMode = useLiveData(pageRecord?.mode$);
|
||||||
|
|
||||||
const icon = useMemo(() => {
|
const icon = useMemo(() => {
|
||||||
return pageMode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
return pageMode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
||||||
|
|||||||
+1
-1
@@ -121,7 +121,7 @@ const UserWithWorkspaceListInner = ({
|
|||||||
}, [onEventEnd, setOpenCreateWorkspaceModal]);
|
}, [onEventEnd, setOpenCreateWorkspaceModal]);
|
||||||
|
|
||||||
const workspaceManager = useService(WorkspaceManager);
|
const workspaceManager = useService(WorkspaceManager);
|
||||||
const workspaces = useLiveData(workspaceManager.list.workspaceList);
|
const workspaces = useLiveData(workspaceManager.list.workspaceList$);
|
||||||
|
|
||||||
// revalidate workspace list when mounted
|
// revalidate workspace list when mounted
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
+2
-2
@@ -105,7 +105,7 @@ export const AFFiNEWorkspaceList = ({
|
|||||||
onEventEnd?: () => void;
|
onEventEnd?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const workspaces = useLiveData(
|
const workspaces = useLiveData(
|
||||||
useService(WorkspaceManager).list.workspaceList
|
useService(WorkspaceManager).list.workspaceList$
|
||||||
);
|
);
|
||||||
|
|
||||||
const setOpenCreateWorkspaceModal = useSetAtom(openCreateWorkspaceModalAtom);
|
const setOpenCreateWorkspaceModal = useSetAtom(openCreateWorkspaceModalAtom);
|
||||||
@@ -113,7 +113,7 @@ export const AFFiNEWorkspaceList = ({
|
|||||||
const { jumpToSubPath } = useNavigateHelper();
|
const { jumpToSubPath } = useNavigateHelper();
|
||||||
|
|
||||||
const currentWorkspace = useLiveData(
|
const currentWorkspace = useLiveData(
|
||||||
useService(CurrentWorkspaceService).currentWorkspace
|
useService(CurrentWorkspaceService).currentWorkspace$
|
||||||
);
|
);
|
||||||
|
|
||||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export const RootAppSidebar = ({
|
|||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
const docCollection = currentWorkspace.docCollection;
|
const docCollection = currentWorkspace.docCollection;
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const currentPath = useLiveData(useService(Workbench).location).pathname;
|
const currentPath = useLiveData(useService(Workbench).location$).pathname;
|
||||||
|
|
||||||
const onClickNewPage = useAsyncCallback(async () => {
|
const onClickNewPage = useAsyncCallback(async () => {
|
||||||
const page = createPage();
|
const page = createPage();
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export function useBlockSuiteMetaHelper(docCollection: DocCollection) {
|
|||||||
|
|
||||||
const duplicate = useAsyncCallback(
|
const duplicate = useAsyncCallback(
|
||||||
async (pageId: string, openPageAfterDuplication: boolean = true) => {
|
async (pageId: string, openPageAfterDuplication: boolean = true) => {
|
||||||
const currentPageMode = pageRecordList.record(pageId).value?.mode.value;
|
const currentPageMode = pageRecordList.record$(pageId).value?.mode$.value;
|
||||||
const currentPageMeta = getDocMeta(pageId);
|
const currentPageMeta = getDocMeta(pageId);
|
||||||
const newPage = createDoc();
|
const newPage = createDoc();
|
||||||
const currentPage = docCollection.getDoc(pageId);
|
const currentPage = docCollection.getDoc(pageId);
|
||||||
@@ -137,7 +137,7 @@ export function useBlockSuiteMetaHelper(docCollection: DocCollection) {
|
|||||||
currentPageMeta.title.replace(lastDigitRegex, '') + `(${newNumber})`;
|
currentPageMeta.title.replace(lastDigitRegex, '') + `(${newNumber})`;
|
||||||
|
|
||||||
pageRecordList
|
pageRecordList
|
||||||
.record(newPage.id)
|
.record$(newPage.id)
|
||||||
.value?.setMode(currentPageMode || 'page');
|
.value?.setMode(currentPageMode || 'page');
|
||||||
setDocTitle(newPage.id, newPageTitle);
|
setDocTitle(newPage.id, newPageTitle);
|
||||||
openPageAfterDuplication && openPage(docCollection.id, newPage.id);
|
openPageAfterDuplication && openPage(docCollection.id, newPage.id);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useMemo } from 'react';
|
|||||||
export function useDocEngineStatus() {
|
export function useDocEngineStatus() {
|
||||||
const workspace = useService(Workspace);
|
const workspace = useService(Workspace);
|
||||||
|
|
||||||
const engineState = useLiveData(workspace.engine.docEngineState);
|
const engineState = useLiveData(workspace.engine.docEngineState$);
|
||||||
|
|
||||||
const progress =
|
const progress =
|
||||||
(engineState.total - engineState.syncing) / engineState.total;
|
(engineState.total - engineState.syncing) / engineState.total;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { useTrashModalHelper } from './use-trash-modal-helper';
|
|||||||
export function useRegisterBlocksuiteEditorCommands() {
|
export function useRegisterBlocksuiteEditorCommands() {
|
||||||
const page = useService(Doc);
|
const page = useService(Doc);
|
||||||
const pageId = page.id;
|
const pageId = page.id;
|
||||||
const mode = useLiveData(page.mode);
|
const mode = useLiveData(page.mode$);
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const workspace = useService(Workspace);
|
const workspace = useService(Workspace);
|
||||||
const docCollection = workspace.docCollection;
|
const docCollection = workspace.docCollection;
|
||||||
|
|||||||
@@ -56,14 +56,14 @@ export const QuickSearch = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const workbench = useService(Workbench);
|
const workbench = useService(Workbench);
|
||||||
const currentPath = useLiveData(workbench.location.map(l => l.pathname));
|
const currentPath = useLiveData(workbench.location$.map(l => l.pathname));
|
||||||
const pageRecordList = useService(PageRecordList);
|
const pageRecordList = useService(PageRecordList);
|
||||||
const currentPathId = matchPath('/:pageId', currentPath)?.params.pageId;
|
const currentPathId = matchPath('/:pageId', currentPath)?.params.pageId;
|
||||||
// TODO: getting pageid from route is fragile, get current page from context
|
// TODO: getting pageid from route is fragile, get current page from context
|
||||||
const currentPage = useLiveData(
|
const currentPage = useLiveData(
|
||||||
currentPathId ? pageRecordList.record(currentPathId) : null
|
currentPathId ? pageRecordList.record$(currentPathId) : null
|
||||||
);
|
);
|
||||||
const pageMeta = useLiveData(currentPage?.meta);
|
const pageMeta = useLiveData(currentPage?.meta$);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CMDKQuickSearchModal
|
<CMDKQuickSearchModal
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class CollectionService {
|
|||||||
return this.setting.get(COLLECTIONS_TRASH_KEY) as YArray<DeletedCollection>;
|
return this.setting.get(COLLECTIONS_TRASH_KEY) as YArray<DeletedCollection>;
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly collections = LiveData.from(
|
readonly collections$ = LiveData.from(
|
||||||
new Observable<Collection[]>(subscriber => {
|
new Observable<Collection[]>(subscriber => {
|
||||||
subscriber.next(this.collectionsYArray?.toArray() ?? []);
|
subscriber.next(this.collectionsYArray?.toArray() ?? []);
|
||||||
const fn = () => {
|
const fn = () => {
|
||||||
@@ -46,7 +46,7 @@ export class CollectionService {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
readonly collectionsTrash = LiveData.from(
|
readonly collectionsTrash$ = LiveData.from(
|
||||||
new Observable<DeletedCollection[]>(subscriber => {
|
new Observable<DeletedCollection[]>(subscriber => {
|
||||||
subscriber.next(this.collectionsTrashYArray?.toArray() ?? []);
|
subscriber.next(this.collectionsTrashYArray?.toArray() ?? []);
|
||||||
const fn = () => {
|
const fn = () => {
|
||||||
@@ -148,7 +148,7 @@ export class CollectionService {
|
|||||||
deletePagesFromCollections(ids: string[]) {
|
deletePagesFromCollections(ids: string[]) {
|
||||||
const idSet = new Set(ids);
|
const idSet = new Set(ids);
|
||||||
this.doc.transact(() => {
|
this.doc.transact(() => {
|
||||||
this.collections.value.forEach(collection => {
|
this.collections$.value.forEach(collection => {
|
||||||
this.deletePagesFromCollection(collection, idSet);
|
this.deletePagesFromCollection(collection, idSet);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const GlobalScopeProvider: React.FC<
|
|||||||
});
|
});
|
||||||
|
|
||||||
const workspaceProvider = useLiveData(
|
const workspaceProvider = useLiveData(
|
||||||
currentWorkspaceService.currentWorkspace
|
currentWorkspaceService.currentWorkspace$
|
||||||
)?.services;
|
)?.services;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ const PageItem = ({
|
|||||||
className,
|
className,
|
||||||
...attrs
|
...attrs
|
||||||
}: PageItemProps) => {
|
}: PageItemProps) => {
|
||||||
const title = useLiveData(pageRecord.title);
|
const title = useLiveData(pageRecord.title$);
|
||||||
const mode = useLiveData(pageRecord.mode);
|
const mode = useLiveData(pageRecord.mode$);
|
||||||
const workspace = useService(Workspace);
|
const workspace = useService(Workspace);
|
||||||
const { isJournal } = useJournalInfoHelper(
|
const { isJournal } = useJournalInfoHelper(
|
||||||
workspace.docCollection,
|
workspace.docCollection,
|
||||||
@@ -171,7 +171,7 @@ const sortPagesByDate = (
|
|||||||
return [...pages].sort((a, b) => {
|
return [...pages].sort((a, b) => {
|
||||||
return (
|
return (
|
||||||
(order === 'asc' ? 1 : -1) *
|
(order === 'asc' ? 1 : -1) *
|
||||||
dayjs(b.meta.value[field]).diff(dayjs(a.meta.value[field]))
|
dayjs(b.meta$.value[field]).diff(dayjs(a.meta$.value[field]))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -193,7 +193,7 @@ const JournalDailyCountBlock = ({ date }: JournalBlockProps) => {
|
|||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const [activeItem, setActiveItem] = useState<NavItemName>('createdToday');
|
const [activeItem, setActiveItem] = useState<NavItemName>('createdToday');
|
||||||
const pageRecordList = useService(PageRecordList);
|
const pageRecordList = useService(PageRecordList);
|
||||||
const pageRecords = useLiveData(pageRecordList.records);
|
const pageRecords = useLiveData(pageRecordList.records$);
|
||||||
|
|
||||||
const navigateHelper = useNavigateHelper();
|
const navigateHelper = useNavigateHelper();
|
||||||
|
|
||||||
@@ -201,10 +201,10 @@ const JournalDailyCountBlock = ({ date }: JournalBlockProps) => {
|
|||||||
(field: 'createDate' | 'updatedDate') => {
|
(field: 'createDate' | 'updatedDate') => {
|
||||||
return sortPagesByDate(
|
return sortPagesByDate(
|
||||||
pageRecords.filter(pageRecord => {
|
pageRecords.filter(pageRecord => {
|
||||||
if (pageRecord.meta.value.trash) return false;
|
if (pageRecord.meta$.value.trash) return false;
|
||||||
return (
|
return (
|
||||||
pageRecord.meta.value[field] &&
|
pageRecord.meta$.value[field] &&
|
||||||
dayjs(pageRecord.meta.value[field]).isSame(date, 'day')
|
dayjs(pageRecord.meta$.value[field]).isSame(date, 'day')
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
field
|
field
|
||||||
@@ -321,7 +321,7 @@ const ConflictList = ({
|
|||||||
setTrashModal({
|
setTrashModal({
|
||||||
open: true,
|
open: true,
|
||||||
pageIds: [pageRecord.id],
|
pageIds: [pageRecord.id],
|
||||||
pageTitles: [pageRecord.meta.value.title],
|
pageTitles: [pageRecord.meta$.value.title],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setTrashModal]
|
[setTrashModal]
|
||||||
@@ -363,7 +363,7 @@ const JournalConflictBlock = ({ date }: JournalBlockProps) => {
|
|||||||
const pageRecordList = useService(PageRecordList);
|
const pageRecordList = useService(PageRecordList);
|
||||||
const journalHelper = useJournalHelper(workspace.docCollection);
|
const journalHelper = useJournalHelper(workspace.docCollection);
|
||||||
const docs = journalHelper.getJournalsByDate(date.format('YYYY-MM-DD'));
|
const docs = journalHelper.getJournalsByDate(date.format('YYYY-MM-DD'));
|
||||||
const pageRecords = useLiveData(pageRecordList.records).filter(v => {
|
const pageRecords = useLiveData(pageRecordList.records$).filter(v => {
|
||||||
return docs.some(doc => doc.id === v.id);
|
return docs.some(doc => doc.id === v.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import type { Workbench } from '../../workbench';
|
|||||||
export class Navigator {
|
export class Navigator {
|
||||||
constructor(private readonly workbench: Workbench) {}
|
constructor(private readonly workbench: Workbench) {}
|
||||||
|
|
||||||
private readonly history = this.workbench.activeView.map(
|
private readonly history$ = this.workbench.activeView$.map(
|
||||||
view => view.history
|
view => view.history
|
||||||
);
|
);
|
||||||
|
|
||||||
private readonly location = LiveData.from(
|
private readonly location$ = LiveData.from(
|
||||||
this.history.pipe(
|
this.history$.pipe(
|
||||||
switchMap(
|
switchMap(
|
||||||
history =>
|
history =>
|
||||||
new Observable<{ index: number; entries: Location[] }>(subscriber => {
|
new Observable<{ index: number; entries: Location[] }>(subscriber => {
|
||||||
@@ -29,11 +29,11 @@ export class Navigator {
|
|||||||
{ index: 0, entries: [] }
|
{ index: 0, entries: [] }
|
||||||
);
|
);
|
||||||
|
|
||||||
readonly backable = this.location.map(
|
readonly backable$ = this.location$.map(
|
||||||
({ index, entries }) => index > 0 && entries.length > 1
|
({ index, entries }) => index > 0 && entries.length > 1
|
||||||
);
|
);
|
||||||
|
|
||||||
readonly forwardable = this.location.map(
|
readonly forwardable$ = this.location$.map(
|
||||||
({ index, entries }) => index < entries.length - 1
|
({ index, entries }) => index < entries.length - 1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ export class Navigator {
|
|||||||
if (!environment.isDesktop) {
|
if (!environment.isDesktop) {
|
||||||
window.history.back();
|
window.history.back();
|
||||||
} else {
|
} else {
|
||||||
this.history.value.back();
|
this.history$.value.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export class Navigator {
|
|||||||
if (!environment.isDesktop) {
|
if (!environment.isDesktop) {
|
||||||
window.history.forward();
|
window.history.forward();
|
||||||
} else {
|
} else {
|
||||||
this.history.value.forward();
|
this.history$.value.forward();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,8 +33,8 @@ export const NavigationButtons = () => {
|
|||||||
|
|
||||||
const navigator = useService(Navigator);
|
const navigator = useService(Navigator);
|
||||||
|
|
||||||
const backable = useLiveData(navigator.backable);
|
const backable = useLiveData(navigator.backable$);
|
||||||
const forwardable = useLiveData(navigator.forwardable);
|
const forwardable = useLiveData(navigator.forwardable$);
|
||||||
|
|
||||||
const handleBack = useCallback(() => {
|
const handleBack = useCallback(() => {
|
||||||
navigator.back();
|
navigator.back();
|
||||||
|
|||||||
@@ -7,22 +7,22 @@ const RIGHT_SIDEBAR_KEY = 'app:settings:rightsidebar';
|
|||||||
|
|
||||||
export class RightSidebar {
|
export class RightSidebar {
|
||||||
constructor(private readonly globalState: GlobalState) {}
|
constructor(private readonly globalState: GlobalState) {}
|
||||||
readonly isOpen = LiveData.from(
|
readonly isOpen$ = LiveData.from(
|
||||||
this.globalState.watch<boolean>(RIGHT_SIDEBAR_KEY),
|
this.globalState.watch<boolean>(RIGHT_SIDEBAR_KEY),
|
||||||
false
|
false
|
||||||
).map(Boolean);
|
).map(Boolean);
|
||||||
readonly views = new LiveData<RightSidebarView[]>([]);
|
readonly views$ = new LiveData<RightSidebarView[]>([]);
|
||||||
readonly front = this.views.map(
|
readonly front$ = this.views$.map(
|
||||||
stack => stack[0] as RightSidebarView | undefined
|
stack => stack[0] as RightSidebarView | undefined
|
||||||
);
|
);
|
||||||
readonly hasViews = this.views.map(stack => stack.length > 0);
|
readonly hasViews$ = this.views$.map(stack => stack.length > 0);
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
this._set(true);
|
this._set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
this._set(!this.isOpen.value);
|
this._set(!this.isOpen$.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
@@ -37,15 +37,15 @@ export class RightSidebar {
|
|||||||
* @private use `RightSidebarViewIsland` instead
|
* @private use `RightSidebarViewIsland` instead
|
||||||
*/
|
*/
|
||||||
_append(view: RightSidebarView) {
|
_append(view: RightSidebarView) {
|
||||||
this.views.next([...this.views.value, view]);
|
this.views$.next([...this.views$.value, view]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private use `RightSidebarViewIsland` instead
|
* @private use `RightSidebarViewIsland` instead
|
||||||
*/
|
*/
|
||||||
_moveToFront(view: RightSidebarView) {
|
_moveToFront(view: RightSidebarView) {
|
||||||
if (this.views.value.includes(view)) {
|
if (this.views$.value.includes(view)) {
|
||||||
this.views.next([view, ...this.views.value.filter(v => v !== view)]);
|
this.views$.next([view, ...this.views$.value.filter(v => v !== view)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +53,6 @@ export class RightSidebar {
|
|||||||
* @private use `RightSidebarViewIsland` instead
|
* @private use `RightSidebarViewIsland` instead
|
||||||
*/
|
*/
|
||||||
_remove(view: RightSidebarView) {
|
_remove(view: RightSidebarView) {
|
||||||
this.views.next(this.views.value.filter(v => v !== view));
|
this.views$.next(this.views$.value.filter(v => v !== view));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,8 +19,8 @@ export const RightSidebarContainer = () => {
|
|||||||
const [resizing, setResizing] = useState(false);
|
const [resizing, setResizing] = useState(false);
|
||||||
const rightSidebar = useService(RightSidebar);
|
const rightSidebar = useService(RightSidebar);
|
||||||
|
|
||||||
const frontView = useLiveData(rightSidebar.front);
|
const frontView = useLiveData(rightSidebar.front$);
|
||||||
const open = useLiveData(rightSidebar.isOpen) && frontView !== undefined;
|
const open = useLiveData(rightSidebar.isOpen$) && frontView !== undefined;
|
||||||
const [floating, setFloating] = useState(false);
|
const [floating, setFloating] = useState(false);
|
||||||
const appSidebarOpened = useAtomValue(appSidebarOpenAtom);
|
const appSidebarOpened = useAtomValue(appSidebarOpenAtom);
|
||||||
|
|
||||||
|
|||||||
@@ -10,24 +10,24 @@ export class Tag {
|
|||||||
private readonly pageRecordList: PageRecordList
|
private readonly pageRecordList: PageRecordList
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private readonly tagOption = this.properties.tagOptions$.map(
|
private readonly tagOption$ = this.properties.tagOptions$.map(
|
||||||
tags => tags.find(tag => tag.id === this.id) as TagSchema
|
tags => tags.find(tag => tag.id === this.id) as TagSchema
|
||||||
);
|
);
|
||||||
|
|
||||||
value = this.tagOption.map(tag => tag?.value || '');
|
value$ = this.tagOption$.map(tag => tag?.value || '');
|
||||||
|
|
||||||
color = this.tagOption.map(tag => tag?.color || '');
|
color$ = this.tagOption$.map(tag => tag?.color || '');
|
||||||
|
|
||||||
createDate = this.tagOption.map(tag => tag?.createDate || Date.now());
|
createDate$ = this.tagOption$.map(tag => tag?.createDate || Date.now());
|
||||||
|
|
||||||
updateDate = this.tagOption.map(tag => tag?.updateDate || Date.now());
|
updateDate$ = this.tagOption$.map(tag => tag?.updateDate || Date.now());
|
||||||
|
|
||||||
rename(value: string) {
|
rename(value: string) {
|
||||||
this.properties.updateTagOption(this.id, {
|
this.properties.updateTagOption(this.id, {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
value,
|
value,
|
||||||
color: this.color.value,
|
color: this.color$.value,
|
||||||
createDate: this.createDate.value,
|
createDate: this.createDate$.value,
|
||||||
updateDate: Date.now(),
|
updateDate: Date.now(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -35,37 +35,37 @@ export class Tag {
|
|||||||
changeColor(color: string) {
|
changeColor(color: string) {
|
||||||
this.properties.updateTagOption(this.id, {
|
this.properties.updateTagOption(this.id, {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
value: this.value.value,
|
value: this.value$.value,
|
||||||
color,
|
color,
|
||||||
createDate: this.createDate.value,
|
createDate: this.createDate$.value,
|
||||||
updateDate: Date.now(),
|
updateDate: Date.now(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tag(pageId: string) {
|
tag(pageId: string) {
|
||||||
const pageRecord = this.pageRecordList.record(pageId).value;
|
const pageRecord = this.pageRecordList.record$(pageId).value;
|
||||||
if (!pageRecord) {
|
if (!pageRecord) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pageRecord?.setMeta({
|
pageRecord?.setMeta({
|
||||||
tags: [...pageRecord.meta.value.tags, this.id],
|
tags: [...pageRecord.meta$.value.tags, this.id],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
untag(pageId: string) {
|
untag(pageId: string) {
|
||||||
const pageRecord = this.pageRecordList.record(pageId).value;
|
const pageRecord = this.pageRecordList.record$(pageId).value;
|
||||||
if (!pageRecord) {
|
if (!pageRecord) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pageRecord?.setMeta({
|
pageRecord?.setMeta({
|
||||||
tags: pageRecord.meta.value.tags.filter(tagId => tagId !== this.id),
|
tags: pageRecord.meta$.value.tags.filter(tagId => tagId !== this.id),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly pageIds = LiveData.computed(get => {
|
readonly pageIds$ = LiveData.computed(get => {
|
||||||
const pages = get(this.pageRecordList.records);
|
const pages = get(this.pageRecordList.records$);
|
||||||
return pages
|
return pages
|
||||||
.filter(page => get(page.meta).tags.includes(this.id))
|
.filter(page => get(page.meta$).tags.includes(this.id))
|
||||||
.map(page => page.id);
|
.map(page => page.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ export class TagService {
|
|||||||
private readonly pageRecordList: PageRecordList
|
private readonly pageRecordList: PageRecordList
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
readonly tags = this.properties.tagOptions$.map(tags =>
|
readonly tags$ = this.properties.tagOptions$.map(tags =>
|
||||||
tags.map(tag => new Tag(tag.id, this.properties, this.pageRecordList))
|
tags.map(tag => new Tag(tag.id, this.properties, this.pageRecordList))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -34,33 +34,33 @@ export class TagService {
|
|||||||
this.properties.removeTagOption(tagId);
|
this.properties.removeTagOption(tagId);
|
||||||
}
|
}
|
||||||
|
|
||||||
tagsByPageId(pageId: string) {
|
tagsByPageId$(pageId: string) {
|
||||||
return LiveData.computed(get => {
|
return LiveData.computed(get => {
|
||||||
const pageRecord = get(this.pageRecordList.record(pageId));
|
const pageRecord = get(this.pageRecordList.record$(pageId));
|
||||||
if (!pageRecord) return [];
|
if (!pageRecord) return [];
|
||||||
const tagIds = get(pageRecord.meta).tags;
|
const tagIds = get(pageRecord.meta$).tags;
|
||||||
|
|
||||||
return get(this.tags).filter(tag => tagIds.includes(tag.id));
|
return get(this.tags$).filter(tag => tagIds.includes(tag.id));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tagIdsByPageId(pageId: string) {
|
tagIdsByPageId$(pageId: string) {
|
||||||
return this.tagsByPageId(pageId).map(tags => tags.map(tag => tag.id));
|
return this.tagsByPageId$(pageId).map(tags => tags.map(tag => tag.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
tagByTagId(tagId?: string) {
|
tagByTagId$(tagId?: string) {
|
||||||
return this.tags.map(tags => tags.find(tag => tag.id === tagId));
|
return this.tags$.map(tags => tags.find(tag => tag.id === tagId));
|
||||||
}
|
}
|
||||||
|
|
||||||
tagMetas = LiveData.computed(get => {
|
tagMetas$ = LiveData.computed(get => {
|
||||||
return get(this.tags).map(tag => {
|
return get(this.tags$).map(tag => {
|
||||||
return {
|
return {
|
||||||
id: tag.id,
|
id: tag.id,
|
||||||
title: get(tag.value),
|
title: get(tag.value$),
|
||||||
color: get(tag.color),
|
color: get(tag.color$),
|
||||||
pageCount: get(tag.pageIds).length,
|
pageCount: get(tag.pageIds$).length,
|
||||||
createDate: get(tag.createDate),
|
createDate: get(tag.createDate$),
|
||||||
updatedDate: get(tag.updateDate),
|
updatedDate: get(tag.updateDate$),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -71,15 +71,17 @@ export class TagService {
|
|||||||
return trimmedValue.includes(trimmedQuery);
|
return trimmedValue.includes(trimmedQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
filterTagsByName(name: string) {
|
filterTagsByName$(name: string) {
|
||||||
return LiveData.computed(get => {
|
return LiveData.computed(get => {
|
||||||
return get(this.tags).filter(tag => this.filterFn(get(tag.value), name));
|
return get(this.tags$).filter(tag =>
|
||||||
|
this.filterFn(get(tag.value$), name)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tagByTagValue(value: string) {
|
tagByTagValue$(value: string) {
|
||||||
return LiveData.computed(get => {
|
return LiveData.computed(get => {
|
||||||
return get(this.tags).find(tag => this.filterFn(get(tag.value), value));
|
return get(this.tags$).find(tag => this.filterFn(get(tag.value$), value));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,11 +17,11 @@ export const DeleteTagConfirmModal = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const tags = useLiveData(tagService.tags);
|
const tags = useLiveData(tagService.tags$);
|
||||||
const selectedTags = useMemo(() => {
|
const selectedTags = useMemo(() => {
|
||||||
return tags.filter(tag => selectedTagIds.includes(tag.id));
|
return tags.filter(tag => selectedTagIds.includes(tag.id));
|
||||||
}, [selectedTagIds, tags]);
|
}, [selectedTagIds, tags]);
|
||||||
const tagName = useLiveData(selectedTags[0]?.value || '');
|
const tagName = useLiveData(selectedTags[0]?.value$ || '');
|
||||||
|
|
||||||
const handleDelete = useCallback(() => {
|
const handleDelete = useCallback(() => {
|
||||||
selectedTagIds.forEach(tagId => {
|
selectedTagIds.forEach(tagId => {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class View {
|
|||||||
initialIndex: 0,
|
initialIndex: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
location = LiveData.from<Location>(
|
location$ = LiveData.from<Location>(
|
||||||
new Observable(subscriber => {
|
new Observable(subscriber => {
|
||||||
subscriber.next(this.history.location);
|
subscriber.next(this.history.location);
|
||||||
return this.history.listen(update => {
|
return this.history.listen(update => {
|
||||||
@@ -31,7 +31,7 @@ export class View {
|
|||||||
this.history.location
|
this.history.location
|
||||||
);
|
);
|
||||||
|
|
||||||
entries = LiveData.from<Location[]>(
|
entries$ = LiveData.from<Location[]>(
|
||||||
new Observable(subscriber => {
|
new Observable(subscriber => {
|
||||||
subscriber.next(this.history.entries);
|
subscriber.next(this.history.entries);
|
||||||
return this.history.listen(() => {
|
return this.history.listen(() => {
|
||||||
@@ -41,7 +41,7 @@ export class View {
|
|||||||
this.history.entries
|
this.history.entries
|
||||||
);
|
);
|
||||||
|
|
||||||
size = new LiveData(100);
|
size$ = new LiveData(100);
|
||||||
|
|
||||||
header = createIsland();
|
header = createIsland();
|
||||||
body = createIsland();
|
body = createIsland();
|
||||||
@@ -59,6 +59,6 @@ export class View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setSize(size?: number) {
|
setSize(size?: number) {
|
||||||
this.size.next(size ?? 100);
|
this.size$.next(size ?? 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,32 +13,32 @@ interface WorkbenchOpenOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Workbench {
|
export class Workbench {
|
||||||
readonly views = new LiveData([new View()]);
|
readonly views$ = new LiveData([new View()]);
|
||||||
|
|
||||||
activeViewIndex = new LiveData(0);
|
activeViewIndex$ = new LiveData(0);
|
||||||
activeView = LiveData.from(
|
activeView$ = LiveData.from(
|
||||||
combineLatest([this.views, this.activeViewIndex]).pipe(
|
combineLatest([this.views$, this.activeViewIndex$]).pipe(
|
||||||
map(([views, index]) => views[index])
|
map(([views, index]) => views[index])
|
||||||
),
|
),
|
||||||
this.views.value[this.activeViewIndex.value]
|
this.views$.value[this.activeViewIndex$.value]
|
||||||
);
|
);
|
||||||
|
|
||||||
basename = new LiveData('/');
|
basename$ = new LiveData('/');
|
||||||
|
|
||||||
location = LiveData.from(
|
location$ = LiveData.from(
|
||||||
this.activeView.pipe(switchMap(view => view.location)),
|
this.activeView$.pipe(switchMap(view => view.location$)),
|
||||||
this.views.value[this.activeViewIndex.value].history.location
|
this.views$.value[this.activeViewIndex$.value].history.location
|
||||||
);
|
);
|
||||||
|
|
||||||
active(index: number) {
|
active(index: number) {
|
||||||
this.activeViewIndex.next(index);
|
this.activeViewIndex$.next(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
createView(at: WorkbenchPosition = 'beside', defaultLocation: To) {
|
createView(at: WorkbenchPosition = 'beside', defaultLocation: To) {
|
||||||
const view = new View(defaultLocation);
|
const view = new View(defaultLocation);
|
||||||
const newViews = [...this.views.value];
|
const newViews = [...this.views$.value];
|
||||||
newViews.splice(this.indexAt(at), 0, view);
|
newViews.splice(this.indexAt(at), 0, view);
|
||||||
this.views.next(newViews);
|
this.views$.next(newViews);
|
||||||
return newViews.indexOf(view);
|
return newViews.indexOf(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,33 +91,33 @@ export class Workbench {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewAt(positionIndex: WorkbenchPosition): View | undefined {
|
viewAt(positionIndex: WorkbenchPosition): View | undefined {
|
||||||
return this.views.value[this.indexAt(positionIndex)];
|
return this.views$.value[this.indexAt(positionIndex)];
|
||||||
}
|
}
|
||||||
|
|
||||||
close(view: View) {
|
close(view: View) {
|
||||||
if (this.views.value.length === 1) return;
|
if (this.views$.value.length === 1) return;
|
||||||
const index = this.views.value.indexOf(view);
|
const index = this.views$.value.indexOf(view);
|
||||||
if (index === -1) return;
|
if (index === -1) return;
|
||||||
const newViews = [...this.views.value];
|
const newViews = [...this.views$.value];
|
||||||
newViews.splice(index, 1);
|
newViews.splice(index, 1);
|
||||||
const activeViewIndex = this.activeViewIndex.value;
|
const activeViewIndex = this.activeViewIndex$.value;
|
||||||
if (activeViewIndex !== 0 && activeViewIndex >= index) {
|
if (activeViewIndex !== 0 && activeViewIndex >= index) {
|
||||||
this.active(activeViewIndex - 1);
|
this.active(activeViewIndex - 1);
|
||||||
}
|
}
|
||||||
this.views.next(newViews);
|
this.views$.next(newViews);
|
||||||
}
|
}
|
||||||
|
|
||||||
closeOthers(view: View) {
|
closeOthers(view: View) {
|
||||||
view.size.next(100);
|
view.size$.next(100);
|
||||||
this.views.next([view]);
|
this.views$.next([view]);
|
||||||
this.active(0);
|
this.active(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
moveView(from: number, to: number) {
|
moveView(from: number, to: number) {
|
||||||
const views = [...this.views.value];
|
const views = [...this.views$.value];
|
||||||
const [removed] = views.splice(from, 1);
|
const [removed] = views.splice(from, 1);
|
||||||
views.splice(to, 0, removed);
|
views.splice(to, 0, removed);
|
||||||
this.views.next(views);
|
this.views$.next(views);
|
||||||
this.active(to);
|
this.active(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,18 +128,18 @@ export class Workbench {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
resize(index: number, percent: number) {
|
resize(index: number, percent: number) {
|
||||||
const view = this.views.value[index];
|
const view = this.views$.value[index];
|
||||||
const nextView = this.views.value[index + 1];
|
const nextView = this.views$.value[index + 1];
|
||||||
if (!nextView) return;
|
if (!nextView) return;
|
||||||
|
|
||||||
const totalViewSize = this.views.value.reduce(
|
const totalViewSize = this.views$.value.reduce(
|
||||||
(sum, v) => sum + v.size.value,
|
(sum, v) => sum + v.size$.value,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
const percentOfTotal = totalViewSize * percent;
|
const percentOfTotal = totalViewSize * percent;
|
||||||
const newSize = Number((view.size.value + percentOfTotal).toFixed(4));
|
const newSize = Number((view.size$.value + percentOfTotal).toFixed(4));
|
||||||
const newNextSize = Number(
|
const newNextSize = Number(
|
||||||
(nextView.size.value - percentOfTotal).toFixed(4)
|
(nextView.size$.value - percentOfTotal).toFixed(4)
|
||||||
);
|
);
|
||||||
// TODO: better strategy to limit size
|
// TODO: better strategy to limit size
|
||||||
if (newSize / totalViewSize < 0.2 || newNextSize / totalViewSize < 0.2)
|
if (newSize / totalViewSize < 0.2 || newNextSize / totalViewSize < 0.2)
|
||||||
@@ -150,16 +150,16 @@ export class Workbench {
|
|||||||
|
|
||||||
private indexAt(positionIndex: WorkbenchPosition): number {
|
private indexAt(positionIndex: WorkbenchPosition): number {
|
||||||
if (positionIndex === 'active') {
|
if (positionIndex === 'active') {
|
||||||
return this.activeViewIndex.value;
|
return this.activeViewIndex$.value;
|
||||||
}
|
}
|
||||||
if (positionIndex === 'beside') {
|
if (positionIndex === 'beside') {
|
||||||
return this.activeViewIndex.value + 1;
|
return this.activeViewIndex$.value + 1;
|
||||||
}
|
}
|
||||||
if (positionIndex === 'head') {
|
if (positionIndex === 'head') {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (positionIndex === 'tail') {
|
if (positionIndex === 'tail') {
|
||||||
return this.views.value.length;
|
return this.views$.value.length;
|
||||||
}
|
}
|
||||||
return positionIndex;
|
return positionIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function useBindWorkbenchToBrowserRouter(
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const browserLocation = useLocation();
|
const browserLocation = useLocation();
|
||||||
|
|
||||||
const view = useLiveData(workbench.activeView);
|
const view = useLiveData(workbench.activeView$);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return view.history.listen(update => {
|
return view.history.listen(update => {
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ export function useBindWorkbenchToDesktopRouter(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
workbench.location.value.pathname === newLocation.pathname &&
|
workbench.location$.value.pathname === newLocation.pathname &&
|
||||||
workbench.location.value.search === newLocation.search &&
|
workbench.location$.value.search === newLocation.search &&
|
||||||
workbench.location.value.hash === newLocation.hash
|
workbench.location$.value.hash === newLocation.hash
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ export const RouteContainer = ({ route }: Props) => {
|
|||||||
const viewPosition = useViewPosition();
|
const viewPosition = useViewPosition();
|
||||||
const leftSidebarOpen = useAtomValue(appSidebarOpenAtom);
|
const leftSidebarOpen = useAtomValue(appSidebarOpenAtom);
|
||||||
const rightSidebar = useService(RightSidebar);
|
const rightSidebar = useService(RightSidebar);
|
||||||
const rightSidebarOpen = useLiveData(rightSidebar.isOpen);
|
const rightSidebarOpen = useLiveData(rightSidebar.isOpen$);
|
||||||
const rightSidebarHasViews = useLiveData(rightSidebar.hasViews);
|
const rightSidebarHasViews = useLiveData(rightSidebar.hasViews$);
|
||||||
const handleToggleRightSidebar = useCallback(() => {
|
const handleToggleRightSidebar = useCallback(() => {
|
||||||
rightSidebar.toggle();
|
rightSidebar.toggle();
|
||||||
}, [rightSidebar]);
|
}, [rightSidebar]);
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ export const SplitViewPanel = memo(function SplitViewPanel({
|
|||||||
}: SplitViewPanelProps) {
|
}: SplitViewPanelProps) {
|
||||||
const [indicatorPressed, setIndicatorPressed] = useState(false);
|
const [indicatorPressed, setIndicatorPressed] = useState(false);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const size = useLiveData(view.size);
|
const size = useLiveData(view.size$);
|
||||||
const workbench = useService(Workbench);
|
const workbench = useService(Workbench);
|
||||||
const activeView = useLiveData(workbench.activeView);
|
const activeView = useLiveData(workbench.activeView$);
|
||||||
const views = useLiveData(workbench.views);
|
const views = useLiveData(workbench.views$);
|
||||||
const isLast = views[views.length - 1] === view;
|
const isLast = views[views.length - 1] === view;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -116,7 +116,7 @@ export const SplitViewPanel = memo(function SplitViewPanel({
|
|||||||
const SplitViewMenu = ({ view }: { view: View }) => {
|
const SplitViewMenu = ({ view }: { view: View }) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const workbench = useService(Workbench);
|
const workbench = useService(Workbench);
|
||||||
const views = useLiveData(workbench.views);
|
const views = useLiveData(workbench.views$);
|
||||||
|
|
||||||
const viewIndex = views.findIndex(v => v === view);
|
const viewIndex = views.findIndex(v => v === view);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ import { useView } from './use-view';
|
|||||||
export function useIsActiveView() {
|
export function useIsActiveView() {
|
||||||
const workbench = useService(Workbench);
|
const workbench = useService(Workbench);
|
||||||
const currentView = useView();
|
const currentView = useView();
|
||||||
const activeView = useLiveData(workbench.activeView);
|
const activeView = useLiveData(workbench.activeView$);
|
||||||
return currentView === activeView;
|
return currentView === activeView;
|
||||||
}
|
}
|
||||||
@@ -10,11 +10,11 @@ export const useViewPosition = () => {
|
|||||||
const view = useView();
|
const view = useView();
|
||||||
|
|
||||||
const [position, setPosition] = useState(() =>
|
const [position, setPosition] = useState(() =>
|
||||||
calcPosition(view, workbench.views.value)
|
calcPosition(view, workbench.views$.value)
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = workbench.views.subscribe(views => {
|
const subscription = workbench.views$.subscribe(views => {
|
||||||
setPosition(calcPosition(view, views));
|
setPosition(calcPosition(view, views));
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const warpedRoutes = viewRoutes.map(({ path, lazy }) => {
|
|||||||
export const ViewRoot = ({ view }: { view: View }) => {
|
export const ViewRoot = ({ view }: { view: View }) => {
|
||||||
const viewRouter = useMemo(() => createMemoryRouter(warpedRoutes), []);
|
const viewRouter = useMemo(() => createMemoryRouter(warpedRoutes), []);
|
||||||
|
|
||||||
const location = useLiveData(view.location);
|
const location = useLiveData(view.location$);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
viewRouter.navigate(location).catch(err => {
|
viewRouter.navigate(location).catch(err => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const WorkbenchLink = ({
|
|||||||
>) => {
|
>) => {
|
||||||
const workbench = useService(Workbench);
|
const workbench = useService(Workbench);
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
const basename = useLiveData(workbench.basename);
|
const basename = useLiveData(workbench.basename$);
|
||||||
const handleClick = useCallback(
|
const handleClick = useCallback(
|
||||||
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const WorkbenchRoot = () => {
|
|||||||
// for debugging
|
// for debugging
|
||||||
(window as any).workbench = workbench;
|
(window as any).workbench = workbench;
|
||||||
|
|
||||||
const views = useLiveData(workbench.views);
|
const views = useLiveData(workbench.views$);
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const basename = location.pathname.match(/\/workspace\/[^/]+/g)?.[0] ?? '/';
|
const basename = location.pathname.match(/\/workspace\/[^/]+/g)?.[0] ?? '/';
|
||||||
@@ -40,8 +40,8 @@ export const WorkbenchRoot = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
workbench.basename.next(basename);
|
workbench.basename$.next(basename);
|
||||||
}, [basename, workbench.basename]);
|
}, [basename, workbench.basename$]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SplitView
|
<SplitView
|
||||||
|
|||||||
@@ -5,20 +5,20 @@ import { LiveData } from '@toeverything/infra/livedata';
|
|||||||
* service to manage current workspace
|
* service to manage current workspace
|
||||||
*/
|
*/
|
||||||
export class CurrentWorkspaceService {
|
export class CurrentWorkspaceService {
|
||||||
currentWorkspace = new LiveData<Workspace | null>(null);
|
currentWorkspace$ = new LiveData<Workspace | null>(null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* open workspace, current workspace will be set to the workspace
|
* open workspace, current workspace will be set to the workspace
|
||||||
* @param workspace
|
* @param workspace
|
||||||
*/
|
*/
|
||||||
openWorkspace(workspace: Workspace) {
|
openWorkspace(workspace: Workspace) {
|
||||||
this.currentWorkspace.next(workspace);
|
this.currentWorkspace$.next(workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* close current workspace, current workspace will be null
|
* close current workspace, current workspace will be null
|
||||||
*/
|
*/
|
||||||
closeWorkspace() {
|
closeWorkspace() {
|
||||||
this.currentWorkspace.next(null);
|
this.currentWorkspace$.next(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ export const Component = () => {
|
|||||||
const [navigating, setNavigating] = useState(false);
|
const [navigating, setNavigating] = useState(false);
|
||||||
const [creating, setCreating] = useState(false);
|
const [creating, setCreating] = useState(false);
|
||||||
|
|
||||||
const list = useLiveData(useService(WorkspaceListService).workspaceList);
|
const list = useLiveData(useService(WorkspaceListService).workspaceList$);
|
||||||
|
|
||||||
const { openPage } = useNavigateHelper();
|
const { openPage } = useNavigateHelper();
|
||||||
|
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ export const Component = () => {
|
|||||||
workspaceManager,
|
workspaceManager,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const pageTitle = useLiveData(page?.title);
|
const pageTitle = useLiveData(page?.title$);
|
||||||
|
|
||||||
usePageDocumentTitle(pageTitle);
|
usePageDocumentTitle(pageTitle);
|
||||||
const loginStatus = useCurrentLoginStatus();
|
const loginStatus = useCurrentLoginStatus();
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const AllCollection = () => {
|
|||||||
const [hideHeaderCreateNew, setHideHeaderCreateNew] = useState(true);
|
const [hideHeaderCreateNew, setHideHeaderCreateNew] = useState(true);
|
||||||
|
|
||||||
const collectionService = useService(CollectionService);
|
const collectionService = useService(CollectionService);
|
||||||
const collections = useLiveData(collectionService.collections);
|
const collections = useLiveData(collectionService.collections$);
|
||||||
const config = useAllPageListConfig();
|
const config = useAllPageListConfig();
|
||||||
|
|
||||||
const collectionMetas = useMemo(() => {
|
const collectionMetas = useMemo(() => {
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ const EmptyTagListHeader = () => {
|
|||||||
|
|
||||||
export const AllTag = () => {
|
export const AllTag = () => {
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const tags = useLiveData(tagService.tags);
|
const tags = useLiveData(tagService.tags$);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
|
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
|
||||||
|
|
||||||
const tagMetas: TagMeta[] = useLiveData(tagService.tagMetas);
|
const tagMetas: TagMeta[] = useLiveData(tagService.tagMetas$);
|
||||||
|
|
||||||
const handleCloseModal = useCallback(
|
const handleCloseModal = useCallback(
|
||||||
(open: boolean) => {
|
(open: boolean) => {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const CollectionDetail = ({
|
|||||||
export const Component = function CollectionPage() {
|
export const Component = function CollectionPage() {
|
||||||
const collectionService = useService(CollectionService);
|
const collectionService = useService(CollectionService);
|
||||||
|
|
||||||
const collections = useLiveData(collectionService.collections);
|
const collections = useLiveData(collectionService.collections$);
|
||||||
const navigate = useNavigateHelper();
|
const navigate = useNavigateHelper();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const workspace = useService(Workspace);
|
const workspace = useService(Workspace);
|
||||||
@@ -76,7 +76,7 @@ export const Component = function CollectionPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!collection) {
|
if (!collection) {
|
||||||
navigate.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
|
navigate.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
|
||||||
const collection = collectionService.collectionsTrash.value.find(
|
const collection = collectionService.collectionsTrash$.value.find(
|
||||||
v => v.collection.id === params.collectionId
|
v => v.collection.id === params.collectionId
|
||||||
);
|
);
|
||||||
let text = 'Collection does not exist';
|
let text = 'Collection does not exist';
|
||||||
@@ -94,7 +94,7 @@ export const Component = function CollectionPage() {
|
|||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
collection,
|
collection,
|
||||||
collectionService.collectionsTrash.value,
|
collectionService.collectionsTrash$.value,
|
||||||
navigate,
|
navigate,
|
||||||
params.collectionId,
|
params.collectionId,
|
||||||
pushNotification,
|
pushNotification,
|
||||||
|
|||||||
@@ -108,9 +108,9 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
|
|
||||||
const isInTrash = pageMeta?.trash;
|
const isInTrash = pageMeta?.trash;
|
||||||
|
|
||||||
const mode = useLiveData(page.mode);
|
const mode = useLiveData(page.mode$);
|
||||||
useRegisterBlocksuiteEditorCommands();
|
useRegisterBlocksuiteEditorCommands();
|
||||||
const title = useLiveData(page.title);
|
const title = useLiveData(page.title$);
|
||||||
usePageDocumentTitle(title);
|
usePageDocumentTitle(title);
|
||||||
|
|
||||||
const onLoad = useCallback(
|
const onLoad = useCallback(
|
||||||
@@ -156,13 +156,13 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
const disposable = new DisposableGroup();
|
const disposable = new DisposableGroup();
|
||||||
|
|
||||||
pageService.getEditorMode = (pageId: string) =>
|
pageService.getEditorMode = (pageId: string) =>
|
||||||
pageRecordList.record(pageId).value?.mode.value ?? 'page';
|
pageRecordList.record$(pageId).value?.mode$.value ?? 'page';
|
||||||
pageService.getDocUpdatedAt = (pageId: string) => {
|
pageService.getDocUpdatedAt = (pageId: string) => {
|
||||||
const linkedPage = pageRecordList.record(pageId).value;
|
const linkedPage = pageRecordList.record$(pageId).value;
|
||||||
if (!linkedPage) return new Date();
|
if (!linkedPage) return new Date();
|
||||||
|
|
||||||
const updatedDate = linkedPage.meta.value.updatedDate;
|
const updatedDate = linkedPage.meta$.value.updatedDate;
|
||||||
const createDate = linkedPage.meta.value.createDate;
|
const createDate = linkedPage.meta$.value.createDate;
|
||||||
return updatedDate ? new Date(updatedDate) : new Date(createDate);
|
return updatedDate ? new Date(updatedDate) : new Date(createDate);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -278,9 +278,9 @@ export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => {
|
|||||||
const currentWorkspace = useService(Workspace);
|
const currentWorkspace = useService(Workspace);
|
||||||
const pageRecordList = useService(PageRecordList);
|
const pageRecordList = useService(PageRecordList);
|
||||||
|
|
||||||
const pageListReady = useLiveData(pageRecordList.isReady);
|
const pageListReady = useLiveData(pageRecordList.isReady$);
|
||||||
|
|
||||||
const pageRecords = useLiveData(pageRecordList.records);
|
const pageRecords = useLiveData(pageRecordList.records$);
|
||||||
|
|
||||||
const pageRecord = useMemo(
|
const pageRecord = useMemo(
|
||||||
() => pageRecords.find(page => page.id === pageId),
|
() => pageRecords.find(page => page.id === pageId),
|
||||||
@@ -310,7 +310,7 @@ export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => {
|
|||||||
};
|
};
|
||||||
}, [currentWorkspace, pageId]);
|
}, [currentWorkspace, pageId]);
|
||||||
|
|
||||||
const jumpOnce = useLiveData(pageRecord?.meta.map(meta => meta.jumpOnce));
|
const jumpOnce = useLiveData(pageRecord?.meta$.map(meta => meta.jumpOnce));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (jumpOnce) {
|
if (jumpOnce) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const Component = (): ReactElement => {
|
|||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
|
||||||
const { workspaceList, loading: listLoading } = useLiveData(
|
const { workspaceList, loading: listLoading } = useLiveData(
|
||||||
useService(WorkspaceListService).status
|
useService(WorkspaceListService).status$
|
||||||
);
|
);
|
||||||
const workspaceManager = useService(WorkspaceManager);
|
const workspaceManager = useService(WorkspaceManager);
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ export const Component = (): ReactElement => {
|
|||||||
|
|
||||||
// avoid doing operation, before workspace is loaded
|
// avoid doing operation, before workspace is loaded
|
||||||
const isRootDocReady =
|
const isRootDocReady =
|
||||||
useLiveData(workspace?.engine.rootDocState)?.ready ?? false;
|
useLiveData(workspace?.engine.rootDocState$)?.ready ?? false;
|
||||||
|
|
||||||
// if listLoading is false, we can show 404 page, otherwise we should show loading page.
|
// if listLoading is false, we can show 404 page, otherwise we should show loading page.
|
||||||
if (listLoading === false && meta === undefined) {
|
if (listLoading === false && meta === undefined) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
ViewBodyIsland,
|
ViewBodyIsland,
|
||||||
ViewHeaderIsland,
|
ViewHeaderIsland,
|
||||||
} from '@affine/core/modules/workbench';
|
} from '@affine/core/modules/workbench';
|
||||||
import { LiveData, useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { Workspace } from '@toeverything/infra';
|
import { Workspace } from '@toeverything/infra';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
@@ -23,21 +23,9 @@ export const TagDetail = ({ tagId }: { tagId?: string }) => {
|
|||||||
const pageMetas = useBlockSuiteDocMeta(currentWorkspace.docCollection);
|
const pageMetas = useBlockSuiteDocMeta(currentWorkspace.docCollection);
|
||||||
|
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const currentTagLiveData = tagService.tagByTagId(tagId);
|
const currentTag = useLiveData(tagService.tagByTagId$(tagId));
|
||||||
const currentTag = useLiveData(currentTagLiveData);
|
|
||||||
|
|
||||||
const pageIdsLiveData = useMemo(
|
const pageIds = useLiveData(currentTag?.pageIds$);
|
||||||
() =>
|
|
||||||
LiveData.computed(get => {
|
|
||||||
const liveTag = get(currentTagLiveData);
|
|
||||||
if (liveTag?.pageIds) {
|
|
||||||
return get(liveTag.pageIds);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}),
|
|
||||||
[currentTagLiveData]
|
|
||||||
);
|
|
||||||
const pageIds = useLiveData(pageIdsLiveData);
|
|
||||||
|
|
||||||
const filteredPageMetas = useMemo(() => {
|
const filteredPageMetas = useMemo(() => {
|
||||||
const pageIdsSet = new Set(pageIds);
|
const pageIdsSet = new Set(pageIds);
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ export const AuthModal = (): ReactElement => {
|
|||||||
|
|
||||||
export function CurrentWorkspaceModals() {
|
export function CurrentWorkspaceModals() {
|
||||||
const currentWorkspace = useLiveData(
|
const currentWorkspace = useLiveData(
|
||||||
useService(CurrentWorkspaceService).currentWorkspace
|
useService(CurrentWorkspaceService).currentWorkspace$
|
||||||
);
|
);
|
||||||
const [openDisableCloudAlertModal, setOpenDisableCloudAlertModal] = useAtom(
|
const [openDisableCloudAlertModal, setOpenDisableCloudAlertModal] = useAtom(
|
||||||
openDisableCloudAlertModalAtom
|
openDisableCloudAlertModalAtom
|
||||||
@@ -227,10 +227,10 @@ export const SignOutConfirmModal = () => {
|
|||||||
const { openPage } = useNavigateHelper();
|
const { openPage } = useNavigateHelper();
|
||||||
const [open, setOpen] = useAtom(openSignOutModalAtom);
|
const [open, setOpen] = useAtom(openSignOutModalAtom);
|
||||||
const currentWorkspace = useLiveData(
|
const currentWorkspace = useLiveData(
|
||||||
useService(CurrentWorkspaceService).currentWorkspace
|
useService(CurrentWorkspaceService).currentWorkspace$
|
||||||
);
|
);
|
||||||
const workspaces = useLiveData(
|
const workspaces = useLiveData(
|
||||||
useService(WorkspaceManager).list.workspaceList
|
useService(WorkspaceManager).list.workspaceList$
|
||||||
);
|
);
|
||||||
|
|
||||||
const onConfirm = useAsyncCallback(async () => {
|
const onConfirm = useAsyncCallback(async () => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useEffect, useRef } from 'react';
|
|||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
export const createIsland = () => {
|
export const createIsland = () => {
|
||||||
const targetLiveData = new LiveData<HTMLDivElement | null>(null);
|
const targetLiveData$ = new LiveData<HTMLDivElement | null>(null);
|
||||||
let mounted = false;
|
let mounted = false;
|
||||||
let provided = false;
|
let provided = false;
|
||||||
return {
|
return {
|
||||||
@@ -14,16 +14,16 @@ export const createIsland = () => {
|
|||||||
throw new Error('Island should not be mounted more than once');
|
throw new Error('Island should not be mounted more than once');
|
||||||
}
|
}
|
||||||
mounted = true;
|
mounted = true;
|
||||||
targetLiveData.next(target.current);
|
targetLiveData$.next(target.current);
|
||||||
return () => {
|
return () => {
|
||||||
mounted = false;
|
mounted = false;
|
||||||
targetLiveData.next(null);
|
targetLiveData$.next(null);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
return <div {...other} ref={target}></div>;
|
return <div {...other} ref={target}></div>;
|
||||||
},
|
},
|
||||||
Provider: ({ children }: React.PropsWithChildren) => {
|
Provider: ({ children }: React.PropsWithChildren) => {
|
||||||
const target = useLiveData(targetLiveData);
|
const target = useLiveData(targetLiveData$);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (provided === true && process.env.NODE_ENV !== 'production') {
|
if (provided === true && process.env.NODE_ENV !== 'production') {
|
||||||
throw new Error('Island should not be provided more than once');
|
throw new Error('Island should not be provided more than once');
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ export const db$Map = new Map<string, Observable<WorkspaceSQLiteDB>>();
|
|||||||
const beforeQuit$ = defer(() => fromEvent(process, 'beforeExit'));
|
const beforeQuit$ = defer(() => fromEvent(process, 'beforeExit'));
|
||||||
|
|
||||||
// return a stream that emit a single event when the subject completes
|
// return a stream that emit a single event when the subject completes
|
||||||
function completed<T>(subject: Subject<T>) {
|
function completed<T>(subject$: Subject<T>) {
|
||||||
return new Observable(subscriber => {
|
return new Observable(subscriber => {
|
||||||
const sub = subject.subscribe({
|
const sub = subject$.subscribe({
|
||||||
complete: () => {
|
complete: () => {
|
||||||
subscriber.next();
|
subscriber.next();
|
||||||
subscriber.complete();
|
subscriber.complete();
|
||||||
@@ -50,7 +50,7 @@ function completed<T>(subject: Subject<T>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWorkspaceDB$(id: string) {
|
function getWorkspaceDB(id: string) {
|
||||||
if (!db$Map.has(id)) {
|
if (!db$Map.has(id)) {
|
||||||
db$Map.set(
|
db$Map.set(
|
||||||
id,
|
id,
|
||||||
@@ -103,7 +103,7 @@ function getWorkspaceDB$(id: string) {
|
|||||||
function startPollingSecondaryDB(db: WorkspaceSQLiteDB) {
|
function startPollingSecondaryDB(db: WorkspaceSQLiteDB) {
|
||||||
return merge(
|
return merge(
|
||||||
getWorkspaceMeta(db.workspaceId),
|
getWorkspaceMeta(db.workspaceId),
|
||||||
workspaceSubjects.meta.pipe(
|
workspaceSubjects.meta$.pipe(
|
||||||
map(({ meta }) => meta),
|
map(({ meta }) => meta),
|
||||||
filter(meta => meta.id === db.workspaceId)
|
filter(meta => meta.id === db.workspaceId)
|
||||||
)
|
)
|
||||||
@@ -141,5 +141,5 @@ function startPollingSecondaryDB(db: WorkspaceSQLiteDB) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ensureSQLiteDB(id: string) {
|
export function ensureSQLiteDB(id: string) {
|
||||||
return lastValueFrom(getWorkspaceDB$(id).pipe(take(1)));
|
return lastValueFrom(getWorkspaceDB(id).pipe(take(1)));
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ export const dbEvents = {
|
|||||||
docId?: string;
|
docId?: string;
|
||||||
}) => void
|
}) => void
|
||||||
) => {
|
) => {
|
||||||
const sub = dbSubjects.externalUpdate.subscribe(fn);
|
const sub = dbSubjects.externalUpdate$.subscribe(fn);
|
||||||
return () => {
|
return () => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
export const dbSubjects = {
|
export const dbSubjects = {
|
||||||
externalUpdate: new Subject<{
|
externalUpdate$: new Subject<{
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
update: Uint8Array;
|
update: Uint8Array;
|
||||||
docId?: string;
|
docId?: string;
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
|
|||||||
if (origin === 'renderer') {
|
if (origin === 'renderer') {
|
||||||
await this.addUpdateToSQLite(insertRows);
|
await this.addUpdateToSQLite(insertRows);
|
||||||
} else if (origin === 'external') {
|
} else if (origin === 'external') {
|
||||||
dbSubjects.externalUpdate.next({
|
dbSubjects.externalUpdate$.next({
|
||||||
workspaceId: this.workspaceId,
|
workspaceId: this.workspaceId,
|
||||||
update,
|
update,
|
||||||
docId,
|
docId,
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export async function storeWorkspaceMeta(
|
|||||||
...meta,
|
...meta,
|
||||||
};
|
};
|
||||||
await fs.writeJSON(metaPath, newMeta);
|
await fs.writeJSON(metaPath, newMeta);
|
||||||
workspaceSubjects.meta.next({
|
workspaceSubjects.meta$.next({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
meta: newMeta,
|
meta: newMeta,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const workspaceEvents = {
|
|||||||
onMetaChange: (
|
onMetaChange: (
|
||||||
fn: (meta: { workspaceId: string; meta: WorkspaceMeta }) => void
|
fn: (meta: { workspaceId: string; meta: WorkspaceMeta }) => void
|
||||||
) => {
|
) => {
|
||||||
const sub = workspaceSubjects.meta.subscribe(fn);
|
const sub = workspaceSubjects.meta$.subscribe(fn);
|
||||||
return () => {
|
return () => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ import { Subject } from 'rxjs';
|
|||||||
import type { WorkspaceMeta } from '../type';
|
import type { WorkspaceMeta } from '../type';
|
||||||
|
|
||||||
export const workspaceSubjects = {
|
export const workspaceSubjects = {
|
||||||
meta: new Subject<{ workspaceId: string; meta: WorkspaceMeta }>(),
|
meta$: new Subject<{ workspaceId: string; meta: WorkspaceMeta }>(),
|
||||||
};
|
};
|
||||||
@@ -26,7 +26,7 @@ export function createApplicationMenu() {
|
|||||||
label: `About ${app.getName()}`,
|
label: `About ${app.getName()}`,
|
||||||
click: async () => {
|
click: async () => {
|
||||||
await showMainWindow();
|
await showMainWindow();
|
||||||
applicationMenuSubjects.openAboutPageInSettingModal.next();
|
applicationMenuSubjects.openAboutPageInSettingModal$.next();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
@@ -52,7 +52,7 @@ export function createApplicationMenu() {
|
|||||||
click: async () => {
|
click: async () => {
|
||||||
await initAndShowMainWindow();
|
await initAndShowMainWindow();
|
||||||
// fixme: if the window is just created, the new page action will not be triggered
|
// fixme: if the window is just created, the new page action will not be triggered
|
||||||
applicationMenuSubjects.newPageAction.next();
|
applicationMenuSubjects.newPageAction$.next();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ export const applicationMenuEvents = {
|
|||||||
* File -> New Doc
|
* File -> New Doc
|
||||||
*/
|
*/
|
||||||
onNewPageAction: (fn: () => void) => {
|
onNewPageAction: (fn: () => void) => {
|
||||||
const sub = applicationMenuSubjects.newPageAction.subscribe(fn);
|
const sub = applicationMenuSubjects.newPageAction$.subscribe(fn);
|
||||||
return () => {
|
return () => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
openAboutPageInSettingModal: (fn: () => void) => {
|
openAboutPageInSettingModal: (fn: () => void) => {
|
||||||
const sub =
|
const sub =
|
||||||
applicationMenuSubjects.openAboutPageInSettingModal.subscribe(fn);
|
applicationMenuSubjects.openAboutPageInSettingModal$.subscribe(fn);
|
||||||
return () => {
|
return () => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
export const applicationMenuSubjects = {
|
export const applicationMenuSubjects = {
|
||||||
newPageAction: new Subject<void>(),
|
newPageAction$: new Subject<void>(),
|
||||||
openAboutPageInSettingModal: new Subject<void>(),
|
openAboutPageInSettingModal$: new Subject<void>(),
|
||||||
};
|
};
|
||||||
@@ -104,7 +104,7 @@ async function createWindow(additionalArguments: string[]) {
|
|||||||
logger.info('main window is ready to show');
|
logger.info('main window is ready to show');
|
||||||
|
|
||||||
if (browserWindow.isMaximized() || browserWindow.isFullScreen()) {
|
if (browserWindow.isMaximized() || browserWindow.isFullScreen()) {
|
||||||
uiSubjects.onMaximized.next(true);
|
uiSubjects.onMaximized$.next(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWebContentsResize().catch(logger.error);
|
handleWebContentsResize().catch(logger.error);
|
||||||
@@ -141,20 +141,20 @@ async function createWindow(additionalArguments: string[]) {
|
|||||||
browserWindow.setSize(size[0] + 1, size[1] + 1);
|
browserWindow.setSize(size[0] + 1, size[1] + 1);
|
||||||
browserWindow.setSize(size[0], size[1]);
|
browserWindow.setSize(size[0], size[1]);
|
||||||
});
|
});
|
||||||
uiSubjects.onMaximized.next(false);
|
uiSubjects.onMaximized$.next(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
browserWindow.on('maximize', () => {
|
browserWindow.on('maximize', () => {
|
||||||
uiSubjects.onMaximized.next(true);
|
uiSubjects.onMaximized$.next(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// full-screen == maximized in UI on windows
|
// full-screen == maximized in UI on windows
|
||||||
browserWindow.on('enter-full-screen', () => {
|
browserWindow.on('enter-full-screen', () => {
|
||||||
uiSubjects.onMaximized.next(true);
|
uiSubjects.onMaximized$.next(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
browserWindow.on('unmaximize', () => {
|
browserWindow.on('unmaximize', () => {
|
||||||
uiSubjects.onMaximized.next(false);
|
uiSubjects.onMaximized$.next(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,7 +172,7 @@ async function createWindow(additionalArguments: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// singleton
|
// singleton
|
||||||
let browserWindow$: Promise<BrowserWindow> | undefined;
|
let browserWindow: Promise<BrowserWindow> | undefined;
|
||||||
|
|
||||||
// a hidden window that prevents the app from quitting on MacOS
|
// a hidden window that prevents the app from quitting on MacOS
|
||||||
let hiddenMacWindow: BrowserWindow | undefined;
|
let hiddenMacWindow: BrowserWindow | undefined;
|
||||||
@@ -181,11 +181,11 @@ let hiddenMacWindow: BrowserWindow | undefined;
|
|||||||
* Init main BrowserWindow. Will create a new window if it's not created yet.
|
* Init main BrowserWindow. Will create a new window if it's not created yet.
|
||||||
*/
|
*/
|
||||||
export async function initAndShowMainWindow() {
|
export async function initAndShowMainWindow() {
|
||||||
if (!browserWindow$ || (await browserWindow$.then(w => w.isDestroyed()))) {
|
if (!browserWindow || (await browserWindow.then(w => w.isDestroyed()))) {
|
||||||
const additionalArguments = await getWindowAdditionalArguments();
|
const additionalArguments = await getWindowAdditionalArguments();
|
||||||
browserWindow$ = createWindow(additionalArguments);
|
browserWindow = createWindow(additionalArguments);
|
||||||
}
|
}
|
||||||
const mainWindow = await browserWindow$;
|
const mainWindow = await browserWindow;
|
||||||
|
|
||||||
if (IS_DEV) {
|
if (IS_DEV) {
|
||||||
// do not gain focus in dev mode
|
// do not gain focus in dev mode
|
||||||
@@ -209,8 +209,8 @@ export async function initAndShowMainWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getMainWindow() {
|
export async function getMainWindow() {
|
||||||
if (!browserWindow$) return;
|
if (!browserWindow) return;
|
||||||
const window = await browserWindow$;
|
const window = await browserWindow;
|
||||||
if (window.isDestroyed()) return;
|
if (window.isDestroyed()) return;
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
@@ -251,7 +251,7 @@ export async function setCookie(
|
|||||||
arg0: CookiesSetDetails | string,
|
arg0: CookiesSetDetails | string,
|
||||||
arg1?: string
|
arg1?: string
|
||||||
) {
|
) {
|
||||||
const window = await browserWindow$;
|
const window = await browserWindow;
|
||||||
if (!window) {
|
if (!window) {
|
||||||
// do nothing if window is not ready
|
// do nothing if window is not ready
|
||||||
return;
|
return;
|
||||||
@@ -271,7 +271,7 @@ export async function setCookie(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function removeCookie(url: string, name: string): Promise<void> {
|
export async function removeCookie(url: string, name: string): Promise<void> {
|
||||||
const window = await browserWindow$;
|
const window = await browserWindow;
|
||||||
if (!window) {
|
if (!window) {
|
||||||
// do nothing if window is not ready
|
// do nothing if window is not ready
|
||||||
return;
|
return;
|
||||||
@@ -280,7 +280,7 @@ export async function removeCookie(url: string, name: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getCookie(url?: string, name?: string) {
|
export async function getCookie(url?: string, name?: string) {
|
||||||
const window = await browserWindow$;
|
const window = await browserWindow;
|
||||||
if (!window) {
|
if (!window) {
|
||||||
// do nothing if window is not ready
|
// do nothing if window is not ready
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -96,23 +96,23 @@ async function createOnboardingWindow(additionalArguments: string[]) {
|
|||||||
return browserWindow;
|
return browserWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
let onBoardingWindow$: Promise<BrowserWindow> | undefined;
|
let onBoardingWindow: Promise<BrowserWindow> | undefined;
|
||||||
|
|
||||||
export async function getOrCreateOnboardingWindow() {
|
export async function getOrCreateOnboardingWindow() {
|
||||||
const additionalArguments = await getWindowAdditionalArguments();
|
const additionalArguments = await getWindowAdditionalArguments();
|
||||||
if (
|
if (
|
||||||
!onBoardingWindow$ ||
|
!onBoardingWindow ||
|
||||||
(await onBoardingWindow$.then(w => w.isDestroyed()))
|
(await onBoardingWindow.then(w => w.isDestroyed()))
|
||||||
) {
|
) {
|
||||||
onBoardingWindow$ = createOnboardingWindow(additionalArguments);
|
onBoardingWindow = createOnboardingWindow(additionalArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
return onBoardingWindow$;
|
return onBoardingWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOnboardingWindow() {
|
export async function getOnboardingWindow() {
|
||||||
if (!onBoardingWindow$) return;
|
if (!onBoardingWindow) return;
|
||||||
const window = await onBoardingWindow$;
|
const window = await onBoardingWindow;
|
||||||
if (window.isDestroyed()) return;
|
if (window.isDestroyed()) return;
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ import { uiSubjects } from './subject';
|
|||||||
*/
|
*/
|
||||||
export const uiEvents = {
|
export const uiEvents = {
|
||||||
onMaximized: (fn: (maximized: boolean) => void) => {
|
onMaximized: (fn: (maximized: boolean) => void) => {
|
||||||
const sub = uiSubjects.onMaximized.subscribe(fn);
|
const sub = uiSubjects.onMaximized$.subscribe(fn);
|
||||||
return () => {
|
return () => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
export const uiSubjects = {
|
export const uiSubjects = {
|
||||||
onMaximized: new Subject<boolean>(),
|
onMaximized$: new Subject<boolean>(),
|
||||||
};
|
};
|
||||||
@@ -67,7 +67,7 @@ export const downloadUpdate = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
downloading = true;
|
downloading = true;
|
||||||
updaterSubjects.downloadProgress.next(0);
|
updaterSubjects.downloadProgress$.next(0);
|
||||||
autoUpdater.downloadUpdate().catch(e => {
|
autoUpdater.downloadUpdate().catch(e => {
|
||||||
downloading = false;
|
downloading = false;
|
||||||
logger.error('Failed to download update', e);
|
logger.error('Failed to download update', e);
|
||||||
@@ -115,7 +115,7 @@ export const registerUpdater = async () => {
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updaterSubjects.updateAvailable.next({
|
updaterSubjects.updateAvailable$.next({
|
||||||
version: info.version,
|
version: info.version,
|
||||||
allowAutoUpdate,
|
allowAutoUpdate,
|
||||||
});
|
});
|
||||||
@@ -125,11 +125,11 @@ export const registerUpdater = async () => {
|
|||||||
});
|
});
|
||||||
autoUpdater.on('download-progress', e => {
|
autoUpdater.on('download-progress', e => {
|
||||||
logger.info(`Download progress: ${e.percent}`);
|
logger.info(`Download progress: ${e.percent}`);
|
||||||
updaterSubjects.downloadProgress.next(e.percent);
|
updaterSubjects.downloadProgress$.next(e.percent);
|
||||||
});
|
});
|
||||||
autoUpdater.on('update-downloaded', e => {
|
autoUpdater.on('update-downloaded', e => {
|
||||||
downloading = false;
|
downloading = false;
|
||||||
updaterSubjects.updateReady.next({
|
updaterSubjects.updateReady$.next({
|
||||||
version: e.version,
|
version: e.version,
|
||||||
allowAutoUpdate,
|
allowAutoUpdate,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,26 +9,26 @@ export interface UpdateMeta {
|
|||||||
|
|
||||||
export const updaterSubjects = {
|
export const updaterSubjects = {
|
||||||
// means it is ready for restart and install the new version
|
// means it is ready for restart and install the new version
|
||||||
updateAvailable: new Subject<UpdateMeta>(),
|
updateAvailable$: new Subject<UpdateMeta>(),
|
||||||
updateReady: new Subject<UpdateMeta>(),
|
updateReady$: new Subject<UpdateMeta>(),
|
||||||
downloadProgress: new BehaviorSubject<number>(0),
|
downloadProgress$: new BehaviorSubject<number>(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updaterEvents = {
|
export const updaterEvents = {
|
||||||
onUpdateAvailable: (fn: (versionMeta: UpdateMeta) => void) => {
|
onUpdateAvailable: (fn: (versionMeta: UpdateMeta) => void) => {
|
||||||
const sub = updaterSubjects.updateAvailable.subscribe(fn);
|
const sub = updaterSubjects.updateAvailable$.subscribe(fn);
|
||||||
return () => {
|
return () => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onUpdateReady: (fn: (versionMeta: UpdateMeta) => void) => {
|
onUpdateReady: (fn: (versionMeta: UpdateMeta) => void) => {
|
||||||
const sub = updaterSubjects.updateReady.subscribe(fn);
|
const sub = updaterSubjects.updateReady$.subscribe(fn);
|
||||||
return () => {
|
return () => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onDownloadProgress: (fn: (progress: number) => void) => {
|
onDownloadProgress: (fn: (progress: number) => void) => {
|
||||||
const sub = updaterSubjects.downloadProgress.subscribe(fn);
|
const sub = updaterSubjects.downloadProgress$.subscribe(fn);
|
||||||
return () => {
|
return () => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import type { BrowserWindow } from 'electron';
|
|
||||||
|
|
||||||
import type { LaunchStage } from './types';
|
|
||||||
|
|
||||||
export const windows$: Record<LaunchStage, Promise<BrowserWindow> | undefined> =
|
|
||||||
{
|
|
||||||
main: undefined,
|
|
||||||
onboarding: undefined,
|
|
||||||
};
|
|
||||||
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user