mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
refactor(server): workspace doc query (#10042)
This commit is contained in:
@@ -61,6 +61,9 @@ class DocType implements Partial<PrismaWorkspaceDoc> {
|
|||||||
|
|
||||||
@Field()
|
@Field()
|
||||||
public!: boolean;
|
public!: boolean;
|
||||||
|
|
||||||
|
@Field(() => DocRole)
|
||||||
|
defaultRole!: DocRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
@InputType()
|
@InputType()
|
||||||
@@ -180,31 +183,13 @@ export class WorkspaceDocResolver {
|
|||||||
description: 'Get public page of a workspace by page id.',
|
description: 'Get public page of a workspace by page id.',
|
||||||
complexity: 2,
|
complexity: 2,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
deprecationReason: 'use [WorkspaceType.publicDoc] instead',
|
deprecationReason: 'use [WorkspaceType.doc] instead',
|
||||||
})
|
})
|
||||||
async publicPage(
|
async publicPage(
|
||||||
@Parent() workspace: WorkspaceType,
|
@Parent() workspace: WorkspaceType,
|
||||||
@Args('pageId') pageId: string
|
@Args('pageId') pageId: string
|
||||||
) {
|
) {
|
||||||
return this.publicDoc(workspace, pageId);
|
return this.doc(workspace, pageId);
|
||||||
}
|
|
||||||
|
|
||||||
@ResolveField(() => DocType, {
|
|
||||||
description: 'Get public page of a workspace by page id.',
|
|
||||||
complexity: 2,
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
async publicDoc(
|
|
||||||
@Parent() workspace: WorkspaceType,
|
|
||||||
@Args('docId') docId: string
|
|
||||||
) {
|
|
||||||
return this.prisma.workspaceDoc.findFirst({
|
|
||||||
where: {
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
docId,
|
|
||||||
public: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResolveField(() => DocType, {
|
@ResolveField(() => DocType, {
|
||||||
@@ -215,21 +200,26 @@ export class WorkspaceDocResolver {
|
|||||||
@Parent() workspace: WorkspaceType,
|
@Parent() workspace: WorkspaceType,
|
||||||
@Args('docId') docId: string
|
@Args('docId') docId: string
|
||||||
): Promise<DocType> {
|
): Promise<DocType> {
|
||||||
const doc = await this.prisma.workspaceDoc.findFirst({
|
const doc = await this.prisma.workspaceDoc.findUnique({
|
||||||
where: {
|
where: {
|
||||||
workspaceId: workspace.id,
|
workspaceId_docId: {
|
||||||
docId,
|
workspaceId: workspace.id,
|
||||||
|
docId,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
if (!doc) {
|
||||||
doc ?? {
|
return {
|
||||||
docId,
|
docId,
|
||||||
workspaceId: workspace.id,
|
workspaceId: workspace.id,
|
||||||
public: false,
|
|
||||||
mode: PublicDocMode.Page,
|
mode: PublicDocMode.Page,
|
||||||
}
|
public: false,
|
||||||
);
|
defaultRole: DocRole.Manager,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mutation(() => DocType, {
|
@Mutation(() => DocType, {
|
||||||
|
|||||||
@@ -297,6 +297,8 @@ enum DocRole {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DocType {
|
type DocType {
|
||||||
|
defaultRole: DocRole!
|
||||||
|
|
||||||
"""paginated doc granted users list"""
|
"""paginated doc granted users list"""
|
||||||
grantedUsersList(pagination: PaginationInput!): PaginatedGrantedDocUserType!
|
grantedUsersList(pagination: PaginationInput!): PaginatedGrantedDocUserType!
|
||||||
id: String!
|
id: String!
|
||||||
@@ -1320,14 +1322,11 @@ type WorkspaceType {
|
|||||||
"""is Public workspace"""
|
"""is Public workspace"""
|
||||||
public: Boolean!
|
public: Boolean!
|
||||||
|
|
||||||
"""Get public page of a workspace by page id."""
|
|
||||||
publicDoc(docId: String!): DocType
|
|
||||||
|
|
||||||
"""Get public docs of a workspace"""
|
"""Get public docs of a workspace"""
|
||||||
publicDocs: [DocType!]!
|
publicDocs: [DocType!]!
|
||||||
|
|
||||||
"""Get public page of a workspace by page id."""
|
"""Get public page of a workspace by page id."""
|
||||||
publicPage(pageId: String!): DocType @deprecated(reason: "use [WorkspaceType.publicDoc] instead")
|
publicPage(pageId: String!): DocType @deprecated(reason: "use [WorkspaceType.doc] instead")
|
||||||
publicPages: [DocType!]! @deprecated(reason: "use [WorkspaceType.publicDocs] instead")
|
publicPages: [DocType!]! @deprecated(reason: "use [WorkspaceType.publicDocs] instead")
|
||||||
|
|
||||||
"""quota of workspace"""
|
"""quota of workspace"""
|
||||||
|
|||||||
@@ -117,6 +117,9 @@ export class DocGrantedUsersService extends Service {
|
|||||||
this.grantedUsers$.next(
|
this.grantedUsers$.next(
|
||||||
this.grantedUsers$.value.filter(user => user.user.id !== userId)
|
this.grantedUsers$.value.filter(user => user.user.id !== userId)
|
||||||
);
|
);
|
||||||
|
if (this.grantedUserCount$.value > 0) {
|
||||||
|
this.grantedUserCount$.next(this.grantedUserCount$.value - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateUserRole(userId: string, role: DocRole) {
|
async updateUserRole(userId: string, role: DocRole) {
|
||||||
@@ -136,6 +139,14 @@ export class DocGrantedUsersService extends Service {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateDocDefaultRole(role: DocRole) {
|
||||||
|
return await this.store.updateDocDefaultRole({
|
||||||
|
docId: this.docService.doc.id,
|
||||||
|
workspaceId: this.workspaceService.workspace.id,
|
||||||
|
role,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
override dispose(): void {
|
override dispose(): void {
|
||||||
this.loadMore.unsubscribe();
|
this.loadMore.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
grantDocUserRolesMutation,
|
grantDocUserRolesMutation,
|
||||||
type PaginationInput,
|
type PaginationInput,
|
||||||
revokeDocUserRolesMutation,
|
revokeDocUserRolesMutation,
|
||||||
|
type UpdateDocDefaultRoleInput,
|
||||||
|
updateDocDefaultRoleMutation,
|
||||||
updateDocUserRoleMutation,
|
updateDocUserRoleMutation,
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
import { Store } from '@toeverything/infra';
|
import { Store } from '@toeverything/infra';
|
||||||
@@ -92,4 +94,18 @@ export class DocGrantedUsersStore extends Store {
|
|||||||
|
|
||||||
return res.updateDocUserRole;
|
return res.updateDocUserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateDocDefaultRole(input: UpdateDocDefaultRoleInput) {
|
||||||
|
if (!this.workspaceServerService.server) {
|
||||||
|
throw new Error('No Server');
|
||||||
|
}
|
||||||
|
const res = await this.workspaceServerService.server.gql({
|
||||||
|
query: updateDocDefaultRoleMutation,
|
||||||
|
variables: {
|
||||||
|
input,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.updateDocDefaultRole;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import type {
|
import type { GetWorkspacePageByIdQuery, PublicDocMode } from '@affine/graphql';
|
||||||
GetWorkspacePublicPageByIdQuery,
|
|
||||||
PublicDocMode,
|
|
||||||
} from '@affine/graphql';
|
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
backoffRetry,
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
@@ -20,14 +17,11 @@ import type { DocService } from '../../doc';
|
|||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import type { ShareStore } from '../stores/share';
|
import type { ShareStore } from '../stores/share';
|
||||||
|
|
||||||
type ShareInfoType = GetWorkspacePublicPageByIdQuery['workspace']['publicDoc'];
|
type ShareInfoType = GetWorkspacePageByIdQuery['workspace']['doc'];
|
||||||
|
|
||||||
export class ShareInfo extends Entity {
|
export class ShareInfo extends Entity {
|
||||||
info$ = new LiveData<ShareInfoType | undefined | null>(null);
|
info$ = new LiveData<ShareInfoType | undefined | null>(null);
|
||||||
isShared$ = this.info$.map(info =>
|
isShared$ = this.info$.map(info => info?.public);
|
||||||
// null means not loaded yet, undefined means not shared
|
|
||||||
info !== null ? info !== undefined : null
|
|
||||||
);
|
|
||||||
sharedMode$ = this.info$.map(info => (info !== null ? info?.mode : null));
|
sharedMode$ = this.info$.map(info => (info !== null ? info?.mode : null));
|
||||||
|
|
||||||
error$ = new LiveData<any>(null);
|
error$ = new LiveData<any>(null);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { PublicDocMode } from '@affine/graphql';
|
import type { PublicDocMode } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
getWorkspacePublicPageByIdQuery,
|
getWorkspacePageByIdQuery,
|
||||||
publishPageMutation,
|
publishPageMutation,
|
||||||
revokePublicPageMutation,
|
revokePublicPageMutation,
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
@@ -22,7 +22,7 @@ export class ShareStore extends Store {
|
|||||||
throw new Error('No Server');
|
throw new Error('No Server');
|
||||||
}
|
}
|
||||||
const data = await this.workspaceServerService.server.gql({
|
const data = await this.workspaceServerService.server.gql({
|
||||||
query: getWorkspacePublicPageByIdQuery,
|
query: getWorkspacePageByIdQuery,
|
||||||
variables: {
|
variables: {
|
||||||
pageId: docId,
|
pageId: docId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@@ -31,7 +31,7 @@ export class ShareStore extends Store {
|
|||||||
signal,
|
signal,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return data.workspace.publicDoc ?? undefined;
|
return data.workspace.doc ?? undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async enableSharePage(
|
async enableSharePage(
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { Menu, MenuItem, MenuTrigger } from '@affine/component';
|
import { Menu, MenuItem, MenuTrigger } from '@affine/component';
|
||||||
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
|
import { DocGrantedUsersService } from '@affine/core/modules/permissions';
|
||||||
|
import { ShareInfoService } from '@affine/core/modules/share-doc';
|
||||||
import { DocRole } from '@affine/graphql';
|
import { DocRole } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { PlanTag } from '../plan-tag';
|
import { PlanTag } from '../plan-tag';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
const getRoleName = (role: DocRole, t: ReturnType<typeof useI18n>) => {
|
const getRoleName = (t: ReturnType<typeof useI18n>, role?: DocRole) => {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case DocRole.Manager:
|
case DocRole.Manager:
|
||||||
return t['com.affine.share-menu.option.permission.can-manage']();
|
return t['com.affine.share-menu.option.permission.can-manage']();
|
||||||
@@ -18,7 +22,7 @@ const getRoleName = (role: DocRole, t: ReturnType<typeof useI18n>) => {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// TODO(@JimmFly): impl the real permission
|
|
||||||
export const MembersPermission = ({
|
export const MembersPermission = ({
|
||||||
openPaywallModal,
|
openPaywallModal,
|
||||||
hittingPaywall,
|
hittingPaywall,
|
||||||
@@ -29,32 +33,44 @@ export const MembersPermission = ({
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [docRole, setDocRole] = useState<DocRole>(DocRole.Manager);
|
const shareInfoService = useService(ShareInfoService);
|
||||||
const currentRoleName = useMemo(() => getRoleName(docRole, t), [docRole, t]);
|
const docGrantedUsersService = useService(DocGrantedUsersService);
|
||||||
|
const docDefaultRole = useLiveData(
|
||||||
|
shareInfoService.shareInfo.info$
|
||||||
|
)?.defaultRole;
|
||||||
|
const currentRoleName = useMemo(
|
||||||
|
() => getRoleName(t, docDefaultRole),
|
||||||
|
[docDefaultRole, t]
|
||||||
|
);
|
||||||
|
|
||||||
const changePermission = useCallback((newPermission: DocRole) => {
|
const changePermission = useCallback(
|
||||||
setDocRole(newPermission);
|
async (docRole: DocRole) => {
|
||||||
}, []);
|
await docGrantedUsersService.updateDocDefaultRole(docRole);
|
||||||
|
shareInfoService.shareInfo.revalidate();
|
||||||
|
},
|
||||||
|
[docGrantedUsersService, shareInfoService.shareInfo]
|
||||||
|
);
|
||||||
|
|
||||||
const selectManage = useCallback(() => {
|
const selectManage = useAsyncCallback(async () => {
|
||||||
changePermission(DocRole.Manager);
|
await changePermission(DocRole.Manager);
|
||||||
}, [changePermission]);
|
}, [changePermission]);
|
||||||
|
|
||||||
const selectEdit = useCallback(() => {
|
const selectEdit = useAsyncCallback(async () => {
|
||||||
if (hittingPaywall) {
|
if (hittingPaywall) {
|
||||||
openPaywallModal?.();
|
openPaywallModal?.();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
changePermission(DocRole.Editor);
|
await changePermission(DocRole.Editor);
|
||||||
}, [changePermission, hittingPaywall, openPaywallModal]);
|
}, [changePermission, hittingPaywall, openPaywallModal]);
|
||||||
|
|
||||||
const selectRead = useCallback(() => {
|
const selectRead = useAsyncCallback(async () => {
|
||||||
if (hittingPaywall) {
|
if (hittingPaywall) {
|
||||||
openPaywallModal?.();
|
openPaywallModal?.();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
changePermission(DocRole.Reader);
|
await changePermission(DocRole.Reader);
|
||||||
}, [changePermission, hittingPaywall, openPaywallModal]);
|
}, [changePermission, hittingPaywall, openPaywallModal]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.rowContainerStyle}>
|
<div className={styles.rowContainerStyle}>
|
||||||
<div className={styles.labelStyle}>
|
<div className={styles.labelStyle}>
|
||||||
@@ -69,7 +85,7 @@ export const MembersPermission = ({
|
|||||||
<>
|
<>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onSelect={selectManage}
|
onSelect={selectManage}
|
||||||
selected={docRole === DocRole.Manager}
|
selected={docDefaultRole === DocRole.Manager}
|
||||||
>
|
>
|
||||||
<div className={styles.publicItemRowStyle}>
|
<div className={styles.publicItemRowStyle}>
|
||||||
{t['com.affine.share-menu.option.permission.can-manage']()}
|
{t['com.affine.share-menu.option.permission.can-manage']()}
|
||||||
@@ -77,7 +93,7 @@ export const MembersPermission = ({
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onSelect={selectEdit}
|
onSelect={selectEdit}
|
||||||
selected={docRole === DocRole.Editor}
|
selected={docDefaultRole === DocRole.Editor}
|
||||||
>
|
>
|
||||||
<div className={styles.publicItemRowStyle}>
|
<div className={styles.publicItemRowStyle}>
|
||||||
<div className={styles.tagContainerStyle}>
|
<div className={styles.tagContainerStyle}>
|
||||||
@@ -88,7 +104,7 @@ export const MembersPermission = ({
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onSelect={selectRead}
|
onSelect={selectRead}
|
||||||
selected={docRole === DocRole.Reader}
|
selected={docDefaultRole === DocRole.Reader}
|
||||||
>
|
>
|
||||||
<div className={styles.publicItemRowStyle}>
|
<div className={styles.publicItemRowStyle}>
|
||||||
<div className={styles.tagContainerStyle}>
|
<div className={styles.tagContainerStyle}>
|
||||||
|
|||||||
@@ -124,7 +124,10 @@ export const sentEmail = style({
|
|||||||
gap: '8px',
|
gap: '8px',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
fontSize: cssVar('fontSm'),
|
fontSize: cssVar('fontSm'),
|
||||||
cursor: 'pointer',
|
|
||||||
|
// TODO(@JimmFly): remove this when we have a sent email feature
|
||||||
|
cursor: 'not-allowed',
|
||||||
|
color: cssVarV2('text/disable'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const checkbox = style({
|
export const checkbox = style({
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { useI18n } from '@affine/i18n';
|
|||||||
import { ArrowLeftBigIcon } from '@blocksuite/icons/rc';
|
import { ArrowLeftBigIcon } from '@blocksuite/icons/rc';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
type CompositionEventHandler,
|
type CompositionEventHandler,
|
||||||
useCallback,
|
useCallback,
|
||||||
@@ -70,7 +71,6 @@ export const InviteMemberEditor = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const memberSearchService = useService(MemberSearchService);
|
const memberSearchService = useService(MemberSearchService);
|
||||||
const searchText = useLiveData(memberSearchService.searchText$);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// reset the search text when the component is mounted
|
// reset the search text when the component is mounted
|
||||||
@@ -78,20 +78,25 @@ export const InviteMemberEditor = ({
|
|||||||
memberSearchService.loadMore();
|
memberSearchService.loadMore();
|
||||||
}, [memberSearchService]);
|
}, [memberSearchService]);
|
||||||
|
|
||||||
|
const debouncedSearch = useMemo(
|
||||||
|
() => debounce((value: string) => memberSearchService.search(value), 300),
|
||||||
|
[memberSearchService]
|
||||||
|
);
|
||||||
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
const [composing, setComposing] = useState(false);
|
const [composing, setComposing] = useState(false);
|
||||||
|
const [searchText, setSearchText] = useState('');
|
||||||
|
|
||||||
const handleValueChange = useCallback(
|
const handleValueChange = useCallback(
|
||||||
(value: string) => {
|
(value: string) => {
|
||||||
|
setSearchText(value);
|
||||||
if (!composing) {
|
if (!composing) {
|
||||||
memberSearchService.search(value);
|
debouncedSearch(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[composing, memberSearchService]
|
[composing, debouncedSearch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const [shouldSendEmail, setShouldSendEmail] = useState(false);
|
|
||||||
const workspaceDialogService = useService(WorkspaceDialogService);
|
const workspaceDialogService = useService(WorkspaceDialogService);
|
||||||
|
|
||||||
const onInvite = useAsyncCallback(async () => {
|
const onInvite = useAsyncCallback(async () => {
|
||||||
@@ -122,15 +127,11 @@ export const InviteMemberEditor = ({
|
|||||||
useCallback(
|
useCallback(
|
||||||
e => {
|
e => {
|
||||||
setComposing(false);
|
setComposing(false);
|
||||||
memberSearchService.search(e.currentTarget.value);
|
debouncedSearch(e.currentTarget.value);
|
||||||
},
|
},
|
||||||
[memberSearchService]
|
[debouncedSearch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onCheckboxChange = useCallback(() => {
|
|
||||||
setShouldSendEmail(prev => !prev);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const focusInput = useCallback(() => {
|
const focusInput = useCallback(() => {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -220,13 +221,14 @@ export const InviteMemberEditor = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.sentEmail} onClick={onCheckboxChange}>
|
<div className={styles.sentEmail}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
className={styles.checkbox}
|
className={styles.checkbox}
|
||||||
checked={shouldSendEmail}
|
checked={false}
|
||||||
disabled // not supported yet
|
disabled // TODO(@JimmFly): implement this
|
||||||
/>
|
/>
|
||||||
{t['com.affine.share-menu.invite-editor.sent-email']()}
|
{t['com.affine.share-menu.invite-editor.sent-email']()}
|
||||||
|
{` (coming soon)`}
|
||||||
</div>
|
</div>
|
||||||
<Result onClickMember={handleClickMember} />
|
<Result onClickMember={handleClickMember} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
|
import { AuthService } from '@affine/core/modules/cloud';
|
||||||
import { DocService } from '@affine/core/modules/doc';
|
import { DocService } from '@affine/core/modules/doc';
|
||||||
import {
|
import {
|
||||||
DocGrantedUsersService,
|
DocGrantedUsersService,
|
||||||
@@ -17,6 +18,7 @@ import {
|
|||||||
import { DocRole, UserFriendlyError } from '@affine/graphql';
|
import { DocRole, UserFriendlyError } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { PlanTag } from '../plan-tag';
|
import { PlanTag } from '../plan-tag';
|
||||||
@@ -32,6 +34,10 @@ export const MemberItem = ({
|
|||||||
openPaywallModal: () => void;
|
openPaywallModal: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const user = grantedUser.user;
|
const user = grantedUser.user;
|
||||||
|
const session = useService(AuthService).session;
|
||||||
|
const account = useLiveData(session.account$);
|
||||||
|
const disableManage =
|
||||||
|
account?.id === user.id || grantedUser.role === DocRole.Owner;
|
||||||
|
|
||||||
const role = useMemo(() => {
|
const role = useMemo(() => {
|
||||||
switch (grantedUser.role) {
|
switch (grantedUser.role) {
|
||||||
@@ -78,30 +84,33 @@ export const MemberItem = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{disableManage ? (
|
||||||
<Menu
|
<div className={clsx(styles.memberRoleStyle, 'disable')}>{role}</div>
|
||||||
items={
|
) : (
|
||||||
<Options
|
<Menu
|
||||||
userId={user.id}
|
items={
|
||||||
memberRole={grantedUser.role}
|
<Options
|
||||||
hittingPaywall={hittingPaywall}
|
userId={user.id}
|
||||||
openPaywallModal={openPaywallModal}
|
memberRole={grantedUser.role}
|
||||||
/>
|
hittingPaywall={hittingPaywall}
|
||||||
}
|
openPaywallModal={openPaywallModal}
|
||||||
contentOptions={{
|
/>
|
||||||
align: 'start',
|
}
|
||||||
}}
|
contentOptions={{
|
||||||
>
|
align: 'start',
|
||||||
<MenuTrigger
|
|
||||||
variant="plain"
|
|
||||||
className={styles.menuTriggerStyle}
|
|
||||||
contentStyle={{
|
|
||||||
width: '100%',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{role}
|
<MenuTrigger
|
||||||
</MenuTrigger>
|
variant="plain"
|
||||||
</Menu>
|
className={styles.menuTriggerStyle}
|
||||||
|
contentStyle={{
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{role}
|
||||||
|
</MenuTrigger>
|
||||||
|
</Menu>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -184,6 +193,7 @@ const Options = ({
|
|||||||
const removeMember = useAsyncCallback(async () => {
|
const removeMember = useAsyncCallback(async () => {
|
||||||
try {
|
try {
|
||||||
await docGrantedUsersService.revokeUsersRole(userId);
|
await docGrantedUsersService.revokeUsersRole(userId);
|
||||||
|
docGrantedUsersService.loadMore();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = UserFriendlyError.fromAnyError(error);
|
const err = UserFriendlyError.fromAnyError(error);
|
||||||
notify.error({
|
notify.error({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
export const result = style({
|
export const result = style({
|
||||||
minHeight: '164px',
|
minHeight: '200px',
|
||||||
maxHeight: '342px',
|
maxHeight: '342px',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
query getDocDefaultRole($workspaceId: String!, $docId: String!) {
|
||||||
|
workspace(id: $workspaceId) {
|
||||||
|
doc(docId: $docId) {
|
||||||
|
defaultRole
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
query getWorkspacePageById($workspaceId: String!, $pageId: String!) {
|
||||||
|
workspace(id: $workspaceId) {
|
||||||
|
doc(docId: $pageId) {
|
||||||
|
id
|
||||||
|
mode
|
||||||
|
defaultRole
|
||||||
|
public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
query getWorkspacePublicPageById($workspaceId: String!, $pageId: String!) {
|
|
||||||
workspace(id: $workspaceId) {
|
|
||||||
publicDoc(docId: $pageId) {
|
|
||||||
id
|
|
||||||
mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -594,6 +594,21 @@ query getCurrentUser {
|
|||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDocDefaultRoleQuery = {
|
||||||
|
id: 'getDocDefaultRoleQuery' as const,
|
||||||
|
operationName: 'getDocDefaultRole',
|
||||||
|
definitionName: 'workspace',
|
||||||
|
containsFile: false,
|
||||||
|
query: `
|
||||||
|
query getDocDefaultRole($workspaceId: String!, $docId: String!) {
|
||||||
|
workspace(id: $workspaceId) {
|
||||||
|
doc(docId: $docId) {
|
||||||
|
defaultRole
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
|
||||||
export const getInviteInfoQuery = {
|
export const getInviteInfoQuery = {
|
||||||
id: 'getInviteInfoQuery' as const,
|
id: 'getInviteInfoQuery' as const,
|
||||||
operationName: 'getInviteInfo',
|
operationName: 'getInviteInfo',
|
||||||
@@ -863,6 +878,24 @@ query getWorkspaceInfo($workspaceId: String!) {
|
|||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getWorkspacePageByIdQuery = {
|
||||||
|
id: 'getWorkspacePageByIdQuery' as const,
|
||||||
|
operationName: 'getWorkspacePageById',
|
||||||
|
definitionName: 'workspace',
|
||||||
|
containsFile: false,
|
||||||
|
query: `
|
||||||
|
query getWorkspacePageById($workspaceId: String!, $pageId: String!) {
|
||||||
|
workspace(id: $workspaceId) {
|
||||||
|
doc(docId: $pageId) {
|
||||||
|
id
|
||||||
|
mode
|
||||||
|
defaultRole
|
||||||
|
public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
|
||||||
export const getWorkspacePageMetaByIdQuery = {
|
export const getWorkspacePageMetaByIdQuery = {
|
||||||
id: 'getWorkspacePageMetaByIdQuery' as const,
|
id: 'getWorkspacePageMetaByIdQuery' as const,
|
||||||
operationName: 'getWorkspacePageMetaById',
|
operationName: 'getWorkspacePageMetaById',
|
||||||
@@ -900,22 +933,6 @@ query getWorkspacePublicById($id: String!) {
|
|||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getWorkspacePublicPageByIdQuery = {
|
|
||||||
id: 'getWorkspacePublicPageByIdQuery' as const,
|
|
||||||
operationName: 'getWorkspacePublicPageById',
|
|
||||||
definitionName: 'workspace',
|
|
||||||
containsFile: false,
|
|
||||||
query: `
|
|
||||||
query getWorkspacePublicPageById($workspaceId: String!, $pageId: String!) {
|
|
||||||
workspace(id: $workspaceId) {
|
|
||||||
publicDoc(docId: $pageId) {
|
|
||||||
id
|
|
||||||
mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getWorkspacePublicPagesQuery = {
|
export const getWorkspacePublicPagesQuery = {
|
||||||
id: 'getWorkspacePublicPagesQuery' as const,
|
id: 'getWorkspacePublicPagesQuery' as const,
|
||||||
operationName: 'getWorkspacePublicPages',
|
operationName: 'getWorkspacePublicPages',
|
||||||
@@ -1363,6 +1380,17 @@ mutation updateAccount($id: String!, $input: ManageUserInput!) {
|
|||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateDocDefaultRoleMutation = {
|
||||||
|
id: 'updateDocDefaultRoleMutation' as const,
|
||||||
|
operationName: 'updateDocDefaultRole',
|
||||||
|
definitionName: 'updateDocDefaultRole',
|
||||||
|
containsFile: false,
|
||||||
|
query: `
|
||||||
|
mutation updateDocDefaultRole($input: UpdateDocDefaultRoleInput!) {
|
||||||
|
updateDocDefaultRole(input: $input)
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
|
||||||
export const updateDocUserRoleMutation = {
|
export const updateDocUserRoleMutation = {
|
||||||
id: 'updateDocUserRoleMutation' as const,
|
id: 'updateDocUserRoleMutation' as const,
|
||||||
operationName: 'updateDocUserRole',
|
operationName: 'updateDocUserRole',
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
mutation updateDocDefaultRole($input: UpdateDocDefaultRoleInput!) {
|
||||||
|
updateDocDefaultRole(input: $input)
|
||||||
|
}
|
||||||
@@ -359,6 +359,7 @@ export enum DocRole {
|
|||||||
|
|
||||||
export interface DocType {
|
export interface DocType {
|
||||||
__typename?: 'DocType';
|
__typename?: 'DocType';
|
||||||
|
defaultRole: DocRole;
|
||||||
/** paginated doc granted users list */
|
/** paginated doc granted users list */
|
||||||
grantedUsersList: PaginatedGrantedDocUserType;
|
grantedUsersList: PaginatedGrantedDocUserType;
|
||||||
id: Scalars['String']['output'];
|
id: Scalars['String']['output'];
|
||||||
@@ -1747,13 +1748,11 @@ export interface WorkspaceType {
|
|||||||
pageMeta: WorkspacePageMeta;
|
pageMeta: WorkspacePageMeta;
|
||||||
/** is Public workspace */
|
/** is Public workspace */
|
||||||
public: Scalars['Boolean']['output'];
|
public: Scalars['Boolean']['output'];
|
||||||
/** Get public page of a workspace by page id. */
|
|
||||||
publicDoc: Maybe<DocType>;
|
|
||||||
/** Get public docs of a workspace */
|
/** Get public docs of a workspace */
|
||||||
publicDocs: Array<DocType>;
|
publicDocs: Array<DocType>;
|
||||||
/**
|
/**
|
||||||
* Get public page of a workspace by page id.
|
* Get public page of a workspace by page id.
|
||||||
* @deprecated use [WorkspaceType.publicDoc] instead
|
* @deprecated use [WorkspaceType.doc] instead
|
||||||
*/
|
*/
|
||||||
publicPage: Maybe<DocType>;
|
publicPage: Maybe<DocType>;
|
||||||
/** @deprecated use [WorkspaceType.publicDocs] instead */
|
/** @deprecated use [WorkspaceType.publicDocs] instead */
|
||||||
@@ -1793,10 +1792,6 @@ export interface WorkspaceTypePageMetaArgs {
|
|||||||
pageId: Scalars['String']['input'];
|
pageId: Scalars['String']['input'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkspaceTypePublicDocArgs {
|
|
||||||
docId: Scalars['String']['input'];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WorkspaceTypePublicPageArgs {
|
export interface WorkspaceTypePublicPageArgs {
|
||||||
pageId: Scalars['String']['input'];
|
pageId: Scalars['String']['input'];
|
||||||
}
|
}
|
||||||
@@ -2354,6 +2349,19 @@ export type GetCurrentUserQuery = {
|
|||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GetDocDefaultRoleQueryVariables = Exact<{
|
||||||
|
workspaceId: Scalars['String']['input'];
|
||||||
|
docId: Scalars['String']['input'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type GetDocDefaultRoleQuery = {
|
||||||
|
__typename?: 'Query';
|
||||||
|
workspace: {
|
||||||
|
__typename?: 'WorkspaceType';
|
||||||
|
doc: { __typename?: 'DocType'; defaultRole: DocRole };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type GetInviteInfoQueryVariables = Exact<{
|
export type GetInviteInfoQueryVariables = Exact<{
|
||||||
inviteId: Scalars['String']['input'];
|
inviteId: Scalars['String']['input'];
|
||||||
}>;
|
}>;
|
||||||
@@ -2601,6 +2609,25 @@ export type GetWorkspaceInfoQuery = {
|
|||||||
workspace: { __typename?: 'WorkspaceType'; team: boolean };
|
workspace: { __typename?: 'WorkspaceType'; team: boolean };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GetWorkspacePageByIdQueryVariables = Exact<{
|
||||||
|
workspaceId: Scalars['String']['input'];
|
||||||
|
pageId: Scalars['String']['input'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type GetWorkspacePageByIdQuery = {
|
||||||
|
__typename?: 'Query';
|
||||||
|
workspace: {
|
||||||
|
__typename?: 'WorkspaceType';
|
||||||
|
doc: {
|
||||||
|
__typename?: 'DocType';
|
||||||
|
id: string;
|
||||||
|
mode: PublicDocMode;
|
||||||
|
defaultRole: DocRole;
|
||||||
|
public: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type GetWorkspacePageMetaByIdQueryVariables = Exact<{
|
export type GetWorkspacePageMetaByIdQueryVariables = Exact<{
|
||||||
id: Scalars['String']['input'];
|
id: Scalars['String']['input'];
|
||||||
pageId: Scalars['String']['input'];
|
pageId: Scalars['String']['input'];
|
||||||
@@ -2637,23 +2664,6 @@ export type GetWorkspacePublicByIdQuery = {
|
|||||||
workspace: { __typename?: 'WorkspaceType'; public: boolean };
|
workspace: { __typename?: 'WorkspaceType'; public: boolean };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetWorkspacePublicPageByIdQueryVariables = Exact<{
|
|
||||||
workspaceId: Scalars['String']['input'];
|
|
||||||
pageId: Scalars['String']['input'];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export type GetWorkspacePublicPageByIdQuery = {
|
|
||||||
__typename?: 'Query';
|
|
||||||
workspace: {
|
|
||||||
__typename?: 'WorkspaceType';
|
|
||||||
publicDoc: {
|
|
||||||
__typename?: 'DocType';
|
|
||||||
id: string;
|
|
||||||
mode: PublicDocMode;
|
|
||||||
} | null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetWorkspacePublicPagesQueryVariables = Exact<{
|
export type GetWorkspacePublicPagesQueryVariables = Exact<{
|
||||||
workspaceId: Scalars['String']['input'];
|
workspaceId: Scalars['String']['input'];
|
||||||
}>;
|
}>;
|
||||||
@@ -3056,6 +3066,15 @@ export type UpdateAccountMutation = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdateDocDefaultRoleMutationVariables = Exact<{
|
||||||
|
input: UpdateDocDefaultRoleInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type UpdateDocDefaultRoleMutation = {
|
||||||
|
__typename?: 'Mutation';
|
||||||
|
updateDocDefaultRole: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type UpdateDocUserRoleMutationVariables = Exact<{
|
export type UpdateDocUserRoleMutationVariables = Exact<{
|
||||||
input: UpdateDocUserRoleInput;
|
input: UpdateDocUserRoleInput;
|
||||||
}>;
|
}>;
|
||||||
@@ -3398,6 +3417,11 @@ export type Queries =
|
|||||||
variables: GetCurrentUserQueryVariables;
|
variables: GetCurrentUserQueryVariables;
|
||||||
response: GetCurrentUserQuery;
|
response: GetCurrentUserQuery;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
name: 'getDocDefaultRoleQuery';
|
||||||
|
variables: GetDocDefaultRoleQueryVariables;
|
||||||
|
response: GetDocDefaultRoleQuery;
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
name: 'getInviteInfoQuery';
|
name: 'getInviteInfoQuery';
|
||||||
variables: GetInviteInfoQueryVariables;
|
variables: GetInviteInfoQueryVariables;
|
||||||
@@ -3473,6 +3497,11 @@ export type Queries =
|
|||||||
variables: GetWorkspaceInfoQueryVariables;
|
variables: GetWorkspaceInfoQueryVariables;
|
||||||
response: GetWorkspaceInfoQuery;
|
response: GetWorkspaceInfoQuery;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
name: 'getWorkspacePageByIdQuery';
|
||||||
|
variables: GetWorkspacePageByIdQueryVariables;
|
||||||
|
response: GetWorkspacePageByIdQuery;
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
name: 'getWorkspacePageMetaByIdQuery';
|
name: 'getWorkspacePageMetaByIdQuery';
|
||||||
variables: GetWorkspacePageMetaByIdQueryVariables;
|
variables: GetWorkspacePageMetaByIdQueryVariables;
|
||||||
@@ -3483,11 +3512,6 @@ export type Queries =
|
|||||||
variables: GetWorkspacePublicByIdQueryVariables;
|
variables: GetWorkspacePublicByIdQueryVariables;
|
||||||
response: GetWorkspacePublicByIdQuery;
|
response: GetWorkspacePublicByIdQuery;
|
||||||
}
|
}
|
||||||
| {
|
|
||||||
name: 'getWorkspacePublicPageByIdQuery';
|
|
||||||
variables: GetWorkspacePublicPageByIdQueryVariables;
|
|
||||||
response: GetWorkspacePublicPageByIdQuery;
|
|
||||||
}
|
|
||||||
| {
|
| {
|
||||||
name: 'getWorkspacePublicPagesQuery';
|
name: 'getWorkspacePublicPagesQuery';
|
||||||
variables: GetWorkspacePublicPagesQueryVariables;
|
variables: GetWorkspacePublicPagesQueryVariables;
|
||||||
@@ -3790,6 +3814,11 @@ export type Mutations =
|
|||||||
variables: UpdateAccountMutationVariables;
|
variables: UpdateAccountMutationVariables;
|
||||||
response: UpdateAccountMutation;
|
response: UpdateAccountMutation;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
name: 'updateDocDefaultRoleMutation';
|
||||||
|
variables: UpdateDocDefaultRoleMutationVariables;
|
||||||
|
response: UpdateDocDefaultRoleMutation;
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
name: 'updateDocUserRoleMutation';
|
name: 'updateDocUserRoleMutation';
|
||||||
variables: UpdateDocUserRoleMutationVariables;
|
variables: UpdateDocUserRoleMutationVariables;
|
||||||
|
|||||||
Reference in New Issue
Block a user