feat(infra): framework

This commit is contained in:
EYHN
2024-04-17 14:12:29 +08:00
parent ab17a05df3
commit 06fda3b62c
467 changed files with 9996 additions and 8697 deletions

View File

@@ -0,0 +1,68 @@
import { DebugLogger } from '@affine/debug';
import type { GetWorkspacePublicPagesQuery } from '@affine/graphql';
import type { GlobalCache, WorkspaceService } from '@toeverything/infra';
import {
backoffRetry,
catchErrorInto,
effect,
Entity,
fromPromise,
LiveData,
onComplete,
onStart,
} from '@toeverything/infra';
import { EMPTY, mergeMap, switchMap } from 'rxjs';
import { isBackendError, isNetworkError } from '../../cloud';
import type { ShareDocsStore } from '../stores/share-docs';
type ShareDocListType =
GetWorkspacePublicPagesQuery['workspace']['publicPages'];
export const logger = new DebugLogger('affine:share-doc-list');
export class ShareDocsList extends Entity {
list$ = LiveData.from(this.cache.watch<ShareDocListType>('share-docs'), []);
isLoading$ = new LiveData<boolean>(false);
error$ = new LiveData<any>(null);
constructor(
private readonly workspaceService: WorkspaceService,
private readonly store: ShareDocsStore,
private readonly cache: GlobalCache
) {
super();
}
revalidate = effect(
switchMap(() =>
fromPromise(signal =>
this.store.getWorkspacesShareDocs(
this.workspaceService.workspace.id,
signal
)
).pipe(
backoffRetry({
when: isNetworkError,
count: Infinity,
}),
backoffRetry({
when: isBackendError,
}),
mergeMap(list => {
this.cache.set('share-docs', list);
return EMPTY;
}),
catchErrorInto(this.error$, err =>
logger.error('revalidate share docs error', err)
),
onStart(() => {
this.isLoading$.next(true);
}),
onComplete(() => {
this.isLoading$.next(false);
})
)
)
);
}

View File

@@ -0,0 +1,92 @@
import type {
GetWorkspacePublicPageByIdQuery,
PublicPageMode,
} from '@affine/graphql';
import type { DocService, WorkspaceService } from '@toeverything/infra';
import {
backoffRetry,
catchErrorInto,
effect,
Entity,
fromPromise,
LiveData,
mapInto,
onComplete,
onStart,
} from '@toeverything/infra';
import { switchMap } from 'rxjs';
import { isBackendError, isNetworkError } from '../../cloud';
import type { ShareStore } from '../stores/share';
type ShareInfoType = GetWorkspacePublicPageByIdQuery['workspace']['publicPage'];
export class Share extends Entity {
info$ = new LiveData<ShareInfoType | undefined | null>(null);
isShared$ = this.info$.map(info =>
// null means not loaded yet, undefined means not shared
info !== null ? info !== undefined : null
);
sharedMode$ = this.info$.map(info => (info !== null ? info?.mode : null));
error$ = new LiveData<any>(null);
isRevalidating$ = new LiveData<boolean>(false);
constructor(
private readonly workspaceService: WorkspaceService,
private readonly docService: DocService,
private readonly store: ShareStore
) {
super();
}
revalidate = effect(
switchMap(() => {
return fromPromise(signal =>
this.store.getShareInfoByDocId(
this.workspaceService.workspace.id,
this.docService.doc.id,
signal
)
).pipe(
backoffRetry({
when: isNetworkError,
count: Infinity,
}),
backoffRetry({
when: isBackendError,
}),
mapInto(this.info$),
catchErrorInto(this.error$),
onStart(() => this.isRevalidating$.next(true)),
onComplete(() => this.isRevalidating$.next(false))
);
})
);
waitForRevalidation(signal?: AbortSignal) {
this.revalidate();
return this.isRevalidating$.waitFor(v => v === false, signal);
}
async enableShare(mode: PublicPageMode) {
await this.store.enableSharePage(
this.workspaceService.workspace.id,
this.docService.doc.id,
mode
);
await this.waitForRevalidation();
}
async changeShare(mode: PublicPageMode) {
await this.enableShare(mode);
}
async disableShare() {
await this.store.disableSharePage(
this.workspaceService.workspace.id,
this.docService.doc.id
);
await this.waitForRevalidation();
}
}

View File

@@ -0,0 +1,35 @@
export { ShareService } from './services/share';
export { ShareDocsService } from './services/share-docs';
import {
DocScope,
DocService,
type Framework,
WorkspaceLocalCache,
WorkspaceScope,
WorkspaceService,
} from '@toeverything/infra';
import { GraphQLService } from '../cloud';
import { ShareDocsList } from './entities/share-docs-list';
import { Share } from './entities/share-info';
import { ShareService } from './services/share';
import { ShareDocsService } from './services/share-docs';
import { ShareStore } from './stores/share';
import { ShareDocsStore } from './stores/share-docs';
export function configureShareDocsModule(framework: Framework) {
framework
.scope(WorkspaceScope)
.service(ShareDocsService)
.store(ShareDocsStore, [GraphQLService])
.entity(ShareDocsList, [
WorkspaceService,
ShareDocsStore,
WorkspaceLocalCache,
])
.scope(DocScope)
.service(ShareService)
.entity(Share, [WorkspaceService, DocService, ShareStore])
.store(ShareStore, [GraphQLService]);
}

View File

@@ -0,0 +1,7 @@
import { Service } from '@toeverything/infra';
import { ShareDocsList } from '../entities/share-docs-list';
export class ShareDocsService extends Service {
shareDocs = this.framework.createEntity(ShareDocsList);
}

View File

@@ -0,0 +1,7 @@
import { Service } from '@toeverything/infra';
import { Share } from '../entities/share-info';
export class ShareService extends Service {
share = this.framework.createEntity(Share);
}

View File

@@ -0,0 +1,22 @@
import type { GraphQLService } from '@affine/core/modules/cloud';
import { getWorkspacePublicPagesQuery } from '@affine/graphql';
import { Store } from '@toeverything/infra';
export class ShareDocsStore extends Store {
constructor(private readonly graphqlService: GraphQLService) {
super();
}
async getWorkspacesShareDocs(workspaceId: string, signal?: AbortSignal) {
const data = await this.graphqlService.gql({
query: getWorkspacePublicPagesQuery,
variables: {
workspaceId: workspaceId,
},
context: {
signal,
},
});
return data.workspace.publicPages;
}
}

View File

@@ -0,0 +1,69 @@
import type { PublicPageMode } from '@affine/graphql';
import {
getWorkspacePublicPageByIdQuery,
publishPageMutation,
revokePublicPageMutation,
} from '@affine/graphql';
import { Store } from '@toeverything/infra';
import type { GraphQLService } from '../../cloud';
export class ShareStore extends Store {
constructor(private readonly gqlService: GraphQLService) {
super();
}
async getShareInfoByDocId(
workspaceId: string,
docId: string,
signal?: AbortSignal
) {
const data = await this.gqlService.gql({
query: getWorkspacePublicPageByIdQuery,
variables: {
pageId: docId,
workspaceId,
},
context: {
signal,
},
});
return data.workspace.publicPage ?? undefined;
}
async enableSharePage(
workspaceId: string,
pageId: string,
docMode?: PublicPageMode,
signal?: AbortSignal
) {
await this.gqlService.gql({
query: publishPageMutation,
variables: {
pageId,
workspaceId,
mode: docMode,
},
context: {
signal,
},
});
}
async disableSharePage(
workspaceId: string,
pageId: string,
signal?: AbortSignal
) {
await this.gqlService.gql({
query: revokePublicPageMutation,
variables: {
pageId,
workspaceId,
},
context: {
signal,
},
});
}
}