diff --git a/apps/core/src/components/affine/new-workspace-setting-detail/members.tsx b/apps/core/src/components/affine/new-workspace-setting-detail/members.tsx index 2226680e0c..38c425c9c4 100644 --- a/apps/core/src/components/affine/new-workspace-setting-detail/members.tsx +++ b/apps/core/src/components/affine/new-workspace-setting-detail/members.tsx @@ -21,7 +21,14 @@ import { Tooltip } from '@toeverything/components/tooltip'; import clsx from 'clsx'; import { useSetAtom } from 'jotai'; import type { ReactElement } from 'react'; -import { Suspense, useCallback, useMemo, useState } from 'react'; +import { + Suspense, + useCallback, + useLayoutEffect, + useMemo, + useRef, + useState, +} from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import type { CheckedUser } from '../../../hooks/affine/use-current-user'; @@ -96,6 +103,20 @@ export const CloudWorkspaceMembersPanel = ({ [invite, pushNotification, t] ); + const listContainerRef = useRef(null); + const [memberListHeight, setMemberListHeight] = useState(null); + + useLayoutEffect(() => { + if ( + memberCount > COUNT_PER_PAGE && + listContainerRef.current && + memberListHeight === null + ) { + const rect = listContainerRef.current.getBoundingClientRect(); + setMemberListHeight(rect.height); + } + }, [listContainerRef, memberCount, memberListHeight]); + const onRevoke = useCallback( async memberId => { const res = await revokeMemberPermission(memberId); @@ -129,7 +150,11 @@ export const CloudWorkspaceMembersPanel = ({ ) : null} -
+
}> - + {memberCount > COUNT_PER_PAGE && ( + + )}
); @@ -186,7 +213,7 @@ const MemberList = ({ const currentUser = useCurrentUser(); return ( - <> +
{members.map(member => ( ))} - +
); }; @@ -225,54 +252,56 @@ const MemberItem = ({ }, [currentUser.id, isOwner, member.id, t]); return ( - <> -
- -
- {member.emailVerified ? ( - <> -
{member.name}
-
{member.email}
- - ) : ( -
{member.email}
- )} -
-
- {member.accepted - ? member.permission === Permission.Owner - ? 'Workspace Owner' - : 'Member' - : 'Pending'} -
- - {operationButtonInfo.leaveOrRevokeText} - - } - > - - - - +
+ +
+ {member.emailVerified ? ( + <> +
{member.name}
+
{member.email}
+ + ) : ( +
{member.email}
+ )}
- +
+ {member.accepted + ? member.permission === Permission.Owner + ? 'Workspace Owner' + : 'Member' + : 'Pending'} +
+ + {operationButtonInfo.leaveOrRevokeText} + + } + > + + + + +
); }; diff --git a/apps/core/src/components/affine/new-workspace-setting-detail/style.css.ts b/apps/core/src/components/affine/new-workspace-setting-detail/style.css.ts index 99f06825bc..988c1d32e8 100644 --- a/apps/core/src/components/affine/new-workspace-setting-detail/style.css.ts +++ b/apps/core/src/components/affine/new-workspace-setting-detail/style.css.ts @@ -93,14 +93,17 @@ export const membersFallback = style({ color: 'var(--affine-primary-color)', }); export const membersPanel = style({ - marginTop: '24px', padding: '4px', borderRadius: '12px', background: 'var(--affine-background-primary-color)', border: '1px solid var(--affine-border-color)', + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', }); -export const listItem = style({ +export const memberList = style({}); +export const memberListItem = style({ padding: '0 4px 0 16px', height: '58px', display: 'flex', @@ -155,7 +158,7 @@ export const memberEmail = style({ }); export const iconButton = style({}); -globalStyle(`${listItem}:hover ${iconButton}`, { +globalStyle(`${memberListItem}:hover ${iconButton}`, { opacity: 1, pointerEvents: 'all', }); diff --git a/apps/server/src/modules/workspaces/resolver.ts b/apps/server/src/modules/workspaces/resolver.ts index 57997079f9..74d073e0be 100644 --- a/apps/server/src/modules/workspaces/resolver.ts +++ b/apps/server/src/modules/workspaces/resolver.ts @@ -174,6 +174,9 @@ export class WorkspaceResolver { return this.prisma.userWorkspacePermission.count({ where: { workspaceId: workspace.id, + userId: { + not: null, + }, }, }); } @@ -214,6 +217,9 @@ export class WorkspaceResolver { const data = await this.prisma.userWorkspacePermission.findMany({ where: { workspaceId: workspace.id, + userId: { + not: null, + }, }, skip, take: take || 8, diff --git a/apps/server/src/schema.gql b/apps/server/src/schema.gql index 177f49b31c..63d8248ede 100644 --- a/apps/server/src/schema.gql +++ b/apps/server/src/schema.gql @@ -173,6 +173,15 @@ type Query { } type Mutation { + signUp(name: String!, email: String!, password: String!): UserType! + signIn(email: String!, password: String!): UserType! + changePassword(token: String!, newPassword: String!): UserType! + changeEmail(token: String!): UserType! + sendChangePasswordEmail(email: String!, callbackUrl: String!): Boolean! + sendSetPasswordEmail(email: String!, callbackUrl: String!): Boolean! + sendChangeEmail(email: String!, callbackUrl: String!): Boolean! + sendVerifyChangeEmail(token: String!, email: String!, callbackUrl: String!): Boolean! + """Create a new workspace""" createWorkspace(init: Upload!): WorkspaceType! @@ -196,14 +205,6 @@ type Mutation { removeAvatar: RemoveAvatar! deleteAccount: DeleteAccount! addToNewFeaturesWaitingList(type: NewFeaturesKind!, email: String!): AddToNewFeaturesWaitingList! - signUp(name: String!, email: String!, password: String!): UserType! - signIn(email: String!, password: String!): UserType! - changePassword(token: String!, newPassword: String!): UserType! - changeEmail(token: String!): UserType! - sendChangePasswordEmail(email: String!, callbackUrl: String!): Boolean! - sendSetPasswordEmail(email: String!, callbackUrl: String!): Boolean! - sendChangeEmail(email: String!, callbackUrl: String!): Boolean! - sendVerifyChangeEmail(token: String!, email: String!, callbackUrl: String!): Boolean! } """The `Upload` scalar type represents a file upload."""