mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-22 04:26:23 +08:00
feat(core): invoice service (#8124)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import type { InvoicesQuery } from '@affine/graphql';
|
||||
import {
|
||||
backoffRetry,
|
||||
catchErrorInto,
|
||||
effect,
|
||||
Entity,
|
||||
exhaustMapSwitchUntilChanged,
|
||||
fromPromise,
|
||||
LiveData,
|
||||
onComplete,
|
||||
onStart,
|
||||
} from '@toeverything/infra';
|
||||
import { EMPTY, map, mergeMap } from 'rxjs';
|
||||
|
||||
import { isBackendError, isNetworkError } from '../error';
|
||||
import type { InvoicesStore } from '../stores/invoices';
|
||||
|
||||
export type Invoice = NonNullable<
|
||||
InvoicesQuery['currentUser']
|
||||
>['invoices'][number];
|
||||
|
||||
export class Invoices extends Entity {
|
||||
constructor(private readonly store: InvoicesStore) {
|
||||
super();
|
||||
}
|
||||
|
||||
pageNum$ = new LiveData(0);
|
||||
invoiceCount$ = new LiveData<number | undefined>(undefined);
|
||||
pageInvoices$ = new LiveData<Invoice[] | undefined>(undefined);
|
||||
|
||||
isLoading$ = new LiveData(false);
|
||||
error$ = new LiveData<any>(null);
|
||||
|
||||
readonly PAGE_SIZE = 8;
|
||||
|
||||
readonly revalidate = effect(
|
||||
map(() => this.pageNum$.value),
|
||||
exhaustMapSwitchUntilChanged(
|
||||
(a, b) => a === b,
|
||||
pageNum => {
|
||||
return fromPromise(async signal => {
|
||||
return this.store.fetchInvoices(
|
||||
pageNum * this.PAGE_SIZE,
|
||||
this.PAGE_SIZE,
|
||||
signal
|
||||
);
|
||||
}).pipe(
|
||||
mergeMap(data => {
|
||||
this.invoiceCount$.setValue(data.invoiceCount);
|
||||
this.pageInvoices$.setValue(data.invoices);
|
||||
return EMPTY;
|
||||
}),
|
||||
backoffRetry({
|
||||
when: isNetworkError,
|
||||
count: Infinity,
|
||||
}),
|
||||
backoffRetry({
|
||||
when: isBackendError,
|
||||
}),
|
||||
catchErrorInto(this.error$),
|
||||
onStart(() => {
|
||||
this.pageInvoices$.setValue(undefined);
|
||||
this.isLoading$.setValue(true);
|
||||
}),
|
||||
onComplete(() => this.isLoading$.setValue(false))
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
setPageNum(pageNum: number) {
|
||||
this.pageNum$.setValue(pageNum);
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this.revalidate.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export type { Invoice } from './entities/invoices';
|
||||
export type { AuthAccountInfo } from './entities/session';
|
||||
export {
|
||||
BackendError,
|
||||
@@ -8,6 +9,7 @@ export {
|
||||
export { AccountChanged, AuthService } from './services/auth';
|
||||
export { FetchService } from './services/fetch';
|
||||
export { GraphQLService } from './services/graphql';
|
||||
export { InvoicesService } from './services/invoices';
|
||||
export { ServerConfigService } from './services/server-config';
|
||||
export { SubscriptionService } from './services/subscription';
|
||||
export { UserCopilotQuotaService } from './services/user-copilot-quota';
|
||||
@@ -25,6 +27,7 @@ import {
|
||||
} from '@toeverything/infra';
|
||||
|
||||
import { CloudDocMeta } from './entities/cloud-doc-meta';
|
||||
import { Invoices } from './entities/invoices';
|
||||
import { ServerConfig } from './entities/server-config';
|
||||
import { AuthSession } from './entities/session';
|
||||
import { Subscription } from './entities/subscription';
|
||||
@@ -36,6 +39,7 @@ import { AuthService } from './services/auth';
|
||||
import { CloudDocMetaService } from './services/cloud-doc-meta';
|
||||
import { FetchService } from './services/fetch';
|
||||
import { GraphQLService } from './services/graphql';
|
||||
import { InvoicesService } from './services/invoices';
|
||||
import { ServerConfigService } from './services/server-config';
|
||||
import { SubscriptionService } from './services/subscription';
|
||||
import { UserCopilotQuotaService } from './services/user-copilot-quota';
|
||||
@@ -44,6 +48,7 @@ import { UserQuotaService } from './services/user-quota';
|
||||
import { WebSocketService } from './services/websocket';
|
||||
import { AuthStore } from './stores/auth';
|
||||
import { CloudDocMetaStore } from './stores/cloud-doc-meta';
|
||||
import { InvoicesStore } from './stores/invoices';
|
||||
import { ServerConfigStore } from './stores/server-config';
|
||||
import { SubscriptionStore } from './stores/subscription';
|
||||
import { UserCopilotQuotaStore } from './stores/user-copilot-quota';
|
||||
@@ -78,6 +83,9 @@ export function configureCloudModule(framework: Framework) {
|
||||
.service(UserFeatureService)
|
||||
.entity(UserFeature, [AuthService, UserFeatureStore])
|
||||
.store(UserFeatureStore, [GraphQLService])
|
||||
.service(InvoicesService)
|
||||
.store(InvoicesStore, [GraphQLService])
|
||||
.entity(Invoices, [InvoicesStore])
|
||||
.scope(WorkspaceScope)
|
||||
.scope(DocScope)
|
||||
.service(CloudDocMetaService)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Service } from '@toeverything/infra';
|
||||
|
||||
import { Invoices } from '../entities/invoices';
|
||||
|
||||
export class InvoicesService extends Service {
|
||||
invoices = this.framework.createEntity(Invoices);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { invoicesQuery } from '@affine/graphql';
|
||||
import { Store } from '@toeverything/infra';
|
||||
|
||||
import type { GraphQLService } from '../services/graphql';
|
||||
|
||||
export class InvoicesStore extends Store {
|
||||
constructor(private readonly graphqlService: GraphQLService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async fetchInvoices(skip: number, take: number, signal?: AbortSignal) {
|
||||
const data = await this.graphqlService.gql({
|
||||
query: invoicesQuery,
|
||||
variables: { skip, take },
|
||||
context: { signal },
|
||||
});
|
||||
|
||||
if (!data.currentUser) {
|
||||
throw new Error('No logged in');
|
||||
}
|
||||
|
||||
return data.currentUser;
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,13 @@ import {
|
||||
catchErrorInto,
|
||||
effect,
|
||||
Entity,
|
||||
exhaustMapSwitchUntilChanged,
|
||||
fromPromise,
|
||||
LiveData,
|
||||
onComplete,
|
||||
onStart,
|
||||
} from '@toeverything/infra';
|
||||
import { distinctUntilChanged, EMPTY, map, mergeMap, switchMap } from 'rxjs';
|
||||
import { EMPTY, map, mergeMap } from 'rxjs';
|
||||
|
||||
import { isBackendError, isNetworkError } from '../../cloud';
|
||||
import type { WorkspaceMembersStore } from '../stores/members';
|
||||
@@ -37,36 +38,38 @@ export class WorkspaceMembers extends Entity {
|
||||
|
||||
readonly revalidate = effect(
|
||||
map(() => this.pageNum$.value),
|
||||
distinctUntilChanged<number>(),
|
||||
switchMap(pageNum => {
|
||||
return fromPromise(async signal => {
|
||||
return this.store.fetchMembers(
|
||||
this.workspaceService.workspace.id,
|
||||
pageNum * this.PAGE_SIZE,
|
||||
this.PAGE_SIZE,
|
||||
signal
|
||||
exhaustMapSwitchUntilChanged(
|
||||
(a, b) => a === b,
|
||||
pageNum => {
|
||||
return fromPromise(async signal => {
|
||||
return this.store.fetchMembers(
|
||||
this.workspaceService.workspace.id,
|
||||
pageNum * this.PAGE_SIZE,
|
||||
this.PAGE_SIZE,
|
||||
signal
|
||||
);
|
||||
}).pipe(
|
||||
mergeMap(data => {
|
||||
this.memberCount$.setValue(data.memberCount);
|
||||
this.pageMembers$.setValue(data.members);
|
||||
return EMPTY;
|
||||
}),
|
||||
backoffRetry({
|
||||
when: isNetworkError,
|
||||
count: Infinity,
|
||||
}),
|
||||
backoffRetry({
|
||||
when: isBackendError,
|
||||
}),
|
||||
catchErrorInto(this.error$),
|
||||
onStart(() => {
|
||||
this.pageMembers$.setValue(undefined);
|
||||
this.isLoading$.setValue(true);
|
||||
}),
|
||||
onComplete(() => this.isLoading$.setValue(false))
|
||||
);
|
||||
}).pipe(
|
||||
mergeMap(data => {
|
||||
this.memberCount$.setValue(data.memberCount);
|
||||
this.pageMembers$.setValue(data.members);
|
||||
return EMPTY;
|
||||
}),
|
||||
backoffRetry({
|
||||
when: isNetworkError,
|
||||
count: Infinity,
|
||||
}),
|
||||
backoffRetry({
|
||||
when: isBackendError,
|
||||
}),
|
||||
catchErrorInto(this.error$),
|
||||
onStart(() => {
|
||||
this.pageMembers$.setValue(undefined);
|
||||
this.isLoading$.setValue(true);
|
||||
}),
|
||||
onComplete(() => this.isLoading$.setValue(false))
|
||||
);
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
setPageNum(pageNum: number) {
|
||||
|
||||
Reference in New Issue
Block a user