Skip to content

Commit 572a9e7

Browse files
committed
chore(clerk-js): Split OrganizationActionList into smaller components
1 parent 8a48c3f commit 572a9e7

File tree

3 files changed

+252
-214
lines changed

3 files changed

+252
-214
lines changed
Lines changed: 24 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,27 @@
1-
import type { OrganizationResource, UserOrganizationInvitationResource } from '@clerk/types';
1+
import React from 'react';
22

3-
import { Plus, SwitchArrows } from '../../../ui/icons';
4-
import {
5-
useCoreOrganization,
6-
useCoreOrganizationList,
7-
useCoreUser,
8-
useOrganizationSwitcherContext,
9-
} from '../../contexts';
10-
import { Box, Button, descriptors, Flex, localizationKeys, Spinner, Text } from '../../customizables';
11-
import {
12-
Action,
13-
OrganizationPreview,
14-
PersonalWorkspacePreview,
15-
PreviewButton,
16-
SecondaryActions,
17-
useCardState,
18-
withCardStateProvider,
19-
} from '../../elements';
20-
import { useInView } from '../../hooks';
21-
import { common } from '../../styledSystem';
22-
import { handleError } from '../../utils';
3+
import { Plus } from '../../../ui/icons';
4+
import { useCoreUser } from '../../contexts';
5+
import { descriptors, localizationKeys } from '../../customizables';
6+
import { Action, SecondaryActions } from '../../elements';
7+
import { UserInvitationList } from './UserInvitationList';
8+
import type { UserMembershipListProps } from './UserMembershipList';
9+
import { UserMembershipList } from './UserMembershipList';
2310

24-
type OrganizationActionListProps = {
11+
export interface OrganizationActionListProps extends UserMembershipListProps {
2512
onCreateOrganizationClick: React.MouseEventHandler;
26-
onPersonalWorkspaceClick: React.MouseEventHandler;
27-
onOrganizationClick: (org: OrganizationResource) => unknown;
28-
};
29-
30-
export const OrganizationActionList = (props: OrganizationActionListProps) => {
31-
const { onCreateOrganizationClick, onPersonalWorkspaceClick, onOrganizationClick } = props;
32-
const { organizationList, userInvitations } = useCoreOrganizationList({
33-
userInvitations: {
34-
infinite: true,
35-
},
36-
});
37-
38-
const { ref } = useInView({
39-
threshold: 0,
40-
onChange: inView => {
41-
if (inView) {
42-
userInvitations.fetchNext?.();
43-
}
44-
},
45-
});
13+
}
4614

47-
const { organization: currentOrg } = useCoreOrganization();
15+
const CreateOrganizationButton = ({
16+
onCreateOrganizationClick,
17+
}: Pick<OrganizationActionListProps, 'onCreateOrganizationClick'>) => {
4818
const user = useCoreUser();
49-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
50-
const { username, primaryEmailAddress, primaryPhoneNumber, ...userWithoutIdentifiers } = user;
51-
const { hidePersonal } = useOrganizationSwitcherContext();
5219

53-
const otherOrgs = (organizationList || []).map(e => e.organization).filter(o => o.id !== currentOrg?.id);
20+
if (!user.createOrganizationEnabled) {
21+
return null;
22+
}
5423

55-
const createOrganizationButton = (
24+
return (
5625
<Action
5726
elementDescriptor={descriptors.organizationSwitcherPopoverActionButton}
5827
elementId={descriptors.organizationSwitcherPopoverActionButton.setId('createOrganization')}
@@ -67,175 +36,16 @@ export const OrganizationActionList = (props: OrganizationActionListProps) => {
6736
onClick={onCreateOrganizationClick}
6837
/>
6938
);
70-
71-
return (
72-
<SecondaryActions elementDescriptor={descriptors.organizationSwitcherPopoverActions}>
73-
<Box
74-
sx={t => ({
75-
maxHeight: `calc(4 * ${t.sizes.$12})`,
76-
overflowY: 'auto',
77-
...common.unstyledScrollbar(t),
78-
})}
79-
>
80-
{currentOrg && !hidePersonal && (
81-
<PreviewButton
82-
elementDescriptor={descriptors.organizationSwitcherPreviewButton}
83-
icon={SwitchArrows}
84-
sx={{ borderRadius: 0 }}
85-
onClick={onPersonalWorkspaceClick}
86-
>
87-
<PersonalWorkspacePreview
88-
user={userWithoutIdentifiers}
89-
size='sm'
90-
avatarSx={t => ({ margin: `0 calc(${t.space.$3}/2)` })}
91-
title={localizationKeys('organizationSwitcher.personalWorkspace')}
92-
/>
93-
</PreviewButton>
94-
)}
95-
{otherOrgs.map(organization => (
96-
<PreviewButton
97-
key={organization.id}
98-
elementDescriptor={descriptors.organizationSwitcherPreviewButton}
99-
icon={SwitchArrows}
100-
sx={{ borderRadius: 0 }}
101-
onClick={() => onOrganizationClick(organization)}
102-
>
103-
<OrganizationPreview
104-
elementId={'organizationSwitcher'}
105-
avatarSx={t => ({ margin: `0 calc(${t.space.$3}/2)` })}
106-
organization={organization}
107-
size='sm'
108-
/>
109-
</PreviewButton>
110-
))}
111-
</Box>
112-
113-
{(userInvitations.count ?? 0) > 0 && (
114-
<Flex
115-
direction='col'
116-
elementDescriptor={descriptors.organizationSwitcherPopoverInvitationActions}
117-
>
118-
<Text
119-
variant='smallRegular'
120-
sx={t => ({
121-
minHeight: 'unset',
122-
height: t.space.$12,
123-
padding: `${t.space.$3} ${t.space.$6}`,
124-
display: 'flex',
125-
alignItems: 'center',
126-
})}
127-
// Handle plurals
128-
localizationKey={localizationKeys(
129-
(userInvitations.count ?? 0) > 1
130-
? 'organizationSwitcher.invitationCountLabel_many'
131-
: 'organizationSwitcher.invitationCountLabel_single',
132-
{
133-
count: userInvitations.count,
134-
},
135-
)}
136-
/>
137-
<Box
138-
sx={t => ({
139-
maxHeight: `calc(4 * ${t.sizes.$12})`,
140-
overflowY: 'auto',
141-
...common.unstyledScrollbar(t),
142-
})}
143-
>
144-
{userInvitations?.data?.map(inv => {
145-
return (
146-
<InvitationPreview
147-
key={inv.id}
148-
{...inv}
149-
/>
150-
);
151-
})}
152-
153-
{(userInvitations.hasNextPage || userInvitations.isFetching) && (
154-
<Box
155-
ref={ref}
156-
sx={t => ({
157-
width: '100%',
158-
height: t.space.$12,
159-
position: 'relative',
160-
})}
161-
>
162-
<Box
163-
sx={{
164-
margin: 'auto',
165-
position: 'absolute',
166-
left: '50%',
167-
top: '50%',
168-
transform: 'translateY(-50%) translateX(-50%)',
169-
}}
170-
>
171-
<Spinner
172-
size='md'
173-
colorScheme='primary'
174-
/>
175-
</Box>
176-
</Box>
177-
)}
178-
</Box>
179-
</Flex>
180-
)}
181-
{user.createOrganizationEnabled && createOrganizationButton}
182-
</SecondaryActions>
183-
);
18439
};
18540

186-
const AcceptRejectInvitationButtons = (props: UserOrganizationInvitationResource) => {
187-
const card = useCardState();
188-
const { userInvitations } = useCoreOrganizationList({
189-
userInvitations: {
190-
infinite: true,
191-
},
192-
});
193-
194-
const mutateSwrState = () => {
195-
(userInvitations as any)?.unstable__mutate?.();
196-
};
197-
198-
const handleAccept = () => {
199-
return card
200-
.runAsync(props.accept())
201-
.then(mutateSwrState)
202-
.catch(err => handleError(err, [], card.setError));
203-
};
41+
export const OrganizationActionList = (props: OrganizationActionListProps) => {
42+
const { onCreateOrganizationClick, onPersonalWorkspaceClick, onOrganizationClick } = props;
20443

20544
return (
206-
<>
207-
<Button
208-
elementDescriptor={descriptors.organizationSwitcherInvitationAcceptButton}
209-
textVariant='buttonExtraSmallBold'
210-
variant={'solid'}
211-
isLoading={card.isLoading}
212-
onClick={handleAccept}
213-
localizationKey={localizationKeys('organizationSwitcher.invitationAccept')}
214-
/>
215-
</>
45+
<SecondaryActions elementDescriptor={descriptors.organizationSwitcherPopoverActions}>
46+
<UserMembershipList {...{ onPersonalWorkspaceClick, onOrganizationClick }} />
47+
<UserInvitationList />
48+
<CreateOrganizationButton {...{ onCreateOrganizationClick }} />
49+
</SecondaryActions>
21650
);
21751
};
218-
219-
const InvitationPreview = withCardStateProvider((props: UserOrganizationInvitationResource) => {
220-
return (
221-
<Flex
222-
align='center'
223-
gap={2}
224-
sx={t => ({
225-
minHeight: 'unset',
226-
height: t.space.$12,
227-
justifyContent: 'space-between',
228-
padding: `0 ${t.space.$6}`,
229-
})}
230-
>
231-
<OrganizationPreview
232-
elementId={'organizationSwitcher'}
233-
avatarSx={t => ({ margin: `0 calc(${t.space.$3}/2)` })}
234-
organization={props.publicOrganizationData}
235-
size='sm'
236-
/>
237-
238-
<AcceptRejectInvitationButtons {...props} />
239-
</Flex>
240-
);
241-
});

0 commit comments

Comments
 (0)