From dd39d049fe85d8328fee274684b085deb48da85f Mon Sep 17 00:00:00 2001 From: JimmFly Date: Thu, 12 Dec 2024 17:43:19 +0800 Subject: [PATCH] feat(core): improve invite link (#9111) --- .../invite-team-modal/index.tsx | 8 +++- .../invite-team-modal/link-invite.tsx | 47 +++++++++---------- .../invite-team-modal/modal-content.tsx | 8 +++- .../members/cloud-members-panel.tsx | 12 ++++- .../share-setting/entities/share-setting.ts | 4 +- 5 files changed, 50 insertions(+), 29 deletions(-) diff --git a/packages/frontend/component/src/components/member-components/invite-team-modal/index.tsx b/packages/frontend/component/src/components/member-components/invite-team-modal/index.tsx index 41e1bf686a..eaa479b9b3 100644 --- a/packages/frontend/component/src/components/member-components/invite-team-modal/index.tsx +++ b/packages/frontend/component/src/components/member-components/invite-team-modal/index.tsx @@ -1,5 +1,8 @@ import { emailRegex } from '@affine/component/auth-components'; -import type { WorkspaceInviteLinkExpireTime } from '@affine/graphql'; +import type { + InviteLink, + WorkspaceInviteLinkExpireTime, +} from '@affine/graphql'; import { useI18n } from '@affine/i18n'; import { useCallback, useEffect, useState } from 'react'; @@ -18,6 +21,7 @@ export interface InviteTeamMemberModalProps { ) => Promise; onRevokeInviteLink: () => Promise; importCSV: React.ReactNode; + invitationLink: InviteLink | null; } const parseEmailString = (emailString: string): string[] => { @@ -36,6 +40,7 @@ export const InviteTeamMemberModal = ({ onGenerateInviteLink, onRevokeInviteLink, importCSV, + invitationLink, }: InviteTeamMemberModalProps) => { const t = useI18n(); const [inviteEmails, setInviteEmails] = useState(''); @@ -95,6 +100,7 @@ export const InviteTeamMemberModal = ({ childrenContentClassName={styles.contentStyle} > ) => [ ]; export const LinkInvite = ({ + invitationLink, copyTextToClipboard, generateInvitationLink, revokeInvitationLink, }: { + invitationLink: InviteLink | null; generateInvitationLink: ( expireTime: WorkspaceInviteLinkExpireTime ) => Promise; @@ -53,7 +58,6 @@ export const LinkInvite = ({ const [selectedValue, setSelectedValue] = useState( WorkspaceInviteLinkExpireTime.OneWeek ); - const [invitationLink, setInvitationLink] = useState(''); const menuItems = getMenuItems(t); const items = useMemo(() => { return menuItems.map(item => ( @@ -69,21 +73,20 @@ export const LinkInvite = ({ ); const onGenerate = useCallback(() => { - generateInvitationLink(selectedValue) - .then(link => { - setInvitationLink(link); - }) - .catch(err => { - console.error('Failed to generate invitation link: ', err); - notify.error({ - title: 'Failed to generate invitation link', - message: err.message, - }); + generateInvitationLink(selectedValue).catch(err => { + console.error('Failed to generate invitation link: ', err); + notify.error({ + title: 'Failed to generate invitation link', + message: err.message, }); + }); }, [generateInvitationLink, selectedValue]); const onCopy = useCallback(() => { - copyTextToClipboard(invitationLink) + if (!invitationLink) { + return; + } + copyTextToClipboard(invitationLink.link) .then(() => notify.success({ title: t['Copied link to clipboard'](), @@ -99,17 +102,13 @@ export const LinkInvite = ({ }, [copyTextToClipboard, invitationLink, t]); const onReset = useCallback(() => { - revokeInvitationLink() - .then(() => { - setInvitationLink(''); - }) - .catch(err => { - console.error('Failed to revoke invitation link: ', err); - notify.error({ - title: 'Failed to revoke invitation link', - message: err.message, - }); + revokeInvitationLink().catch(err => { + console.error('Failed to revoke invitation link: ', err); + notify.error({ + title: 'Failed to revoke invitation link', + message: err.message, }); + }); }, [revokeInvitationLink]); return ( @@ -136,7 +135,7 @@ export const LinkInvite = ({ void; inviteMethod: InviteMethodType; onInviteMethodChange: (value: InviteMethodType) => void; @@ -78,6 +83,7 @@ export const ModalContent = ({ /> ) : ( void; isTeam?: boolean; }) => { + const workspaceShareSettingService = useService(WorkspaceShareSettingService); + const inviteLink = useLiveData( + workspaceShareSettingService.sharePreview.inviteLink$ + ); const serverService = useService(ServerService); const hasPaymentFeature = useLiveData( serverService.server.features$.map(f => f?.payment) @@ -90,15 +95,17 @@ export const CloudWorkspaceMembersPanel = ({ async (expireTime: WorkspaceInviteLinkExpireTime) => { const { link } = await permissionService.permission.generateInviteLink(expireTime); + workspaceShareSettingService.sharePreview.revalidate(); return link; }, - [permissionService.permission] + [permissionService.permission, workspaceShareSettingService.sharePreview] ); const onRevokeInviteLink = useCallback(async () => { const success = await permissionService.permission.revokeInviteLink(); + workspaceShareSettingService.sharePreview.revalidate(); return success; - }, [permissionService.permission]); + }, [permissionService.permission, workspaceShareSettingService.sharePreview]); const onInviteBatchConfirm = useCallback< InviteTeamMemberModalProps['onConfirm'] @@ -218,6 +225,7 @@ export const CloudWorkspaceMembersPanel = ({ onGenerateInviteLink={onGenerateInviteLink} onRevokeInviteLink={onRevokeInviteLink} importCSV={} + invitationLink={inviteLink} /> )} diff --git a/packages/frontend/core/src/modules/share-setting/entities/share-setting.ts b/packages/frontend/core/src/modules/share-setting/entities/share-setting.ts index e99a0336ec..f77bccadd0 100644 --- a/packages/frontend/core/src/modules/share-setting/entities/share-setting.ts +++ b/packages/frontend/core/src/modules/share-setting/entities/share-setting.ts @@ -1,5 +1,5 @@ import { DebugLogger } from '@affine/debug'; -import type { GetWorkspaceConfigQuery } from '@affine/graphql'; +import type { GetWorkspaceConfigQuery, InviteLink } from '@affine/graphql'; import type { WorkspaceService } from '@toeverything/infra'; import { backoffRetry, @@ -25,6 +25,7 @@ const logger = new DebugLogger('affine:workspace-permission'); export class WorkspaceShareSetting extends Entity { enableAi$ = new LiveData(null); enableUrlPreview$ = new LiveData(null); + inviteLink$ = new LiveData(null); isLoading$ = new LiveData(false); error$ = new LiveData(null); @@ -56,6 +57,7 @@ export class WorkspaceShareSetting extends Entity { if (value) { this.enableAi$.next(value.enableAi); this.enableUrlPreview$.next(value.enableUrlPreview); + this.inviteLink$.next(value.inviteLink); } return EMPTY; }),