Skip to content

Commit

Permalink
fix(clerk-js): Revalidate Organization Memberlist when joining an Org…
Browse files Browse the repository at this point in the history
…anization (#2437)

* fix(clerk-js): Revalidate Organization Switcher memberlist when joining an organization

* chore(repo): Add Changeset

* fix(clerk-js): Revalidate Organization Switcher memberlist when joining an organization

* fix(clerk-js): Avoid revalidation as it will cause layout shifts

* fix(clerk-js): Preserve accepted until next revalidation

* fix(clerk-js): Preserve fetched organization when closing OrganizationSwitcher popover

* fix(clerk-js): On accept invitation decrease the total count by one

* chore(clerk-js): Move `AcceptedInvitationsProvider` inside `ui/contexts`

---------

Co-authored-by: panteliselef <[email protected]>
  • Loading branch information
octoper and panteliselef authored Dec 23, 2023
1 parent a68a673 commit ca67ee7
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 105 deletions.
2 changes: 2 additions & 0 deletions .changeset/fluffy-pets-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ const OrganizationListPageList = (props: { onCreateOrganizationClick: () => void
const handleCreateOrganizationClicked = () => {
props.onCreateOrganizationClick();
};

// Solve weird bug with swr while running unit tests
const userInvitationsData = userInvitations.data?.filter(a => !!a);
const userSuggestionsData = userSuggestions.data?.filter(a => !!a);

return (
<>
<Header.Root
Expand Down Expand Up @@ -157,8 +162,7 @@ const OrganizationListPageList = (props: { onCreateOrganizationClick: () => void
})}

{!userMemberships.hasNextPage &&
(userInvitations.count || 0) > 0 &&
userInvitations.data?.map(inv => {
userInvitationsData?.map(inv => {
return (
<InvitationPreview
key={inv.id}
Expand All @@ -169,8 +173,7 @@ const OrganizationListPageList = (props: { onCreateOrganizationClick: () => void

{!userMemberships.hasNextPage &&
!userInvitations.hasNextPage &&
(userSuggestions.count || 0) > 0 &&
userSuggestions.data?.map(inv => {
userSuggestionsData?.map(inv => {
return (
<SuggestionPreview
key={inv.id}
Expand All @@ -188,7 +191,10 @@ const OrganizationListPageList = (props: { onCreateOrganizationClick: () => void
icon={Add}
label={localizationKeys('organizationList.action__createOrganization')}
onClick={handleCreateOrganizationClicked}
sx={{ borderBottom: 'none' }}
sx={t => ({
borderBottom: 'none',
padding: `${t.space.$5} ${t.space.$5}`,
})}
iconSx={t => ({
width: t.sizes.$9,
height: t.sizes.$6,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,25 @@ export const InvitationPreview = withCardStateProvider((props: UserOrganizationI
const [acceptedOrganization, setAcceptedOrganization] = useState<OrganizationResource | null>(null);
const { userInvitations } = useOrganizationList({
userInvitations: organizationListParams.userInvitations,
userMemberships: organizationListParams.userMemberships,
});

const handleAccept = () => {
return card
.runAsync(async () => {
const updatedItem = await props.accept();
const organization = await getOrganization(props.publicOrganizationData.id);
return [updatedItem, organization] as const;
})
.then(([updatedItem, organization]) => {
// Update cache in case another listener depends on it
void userInvitations?.setData?.(cachedPages => populateCacheUpdateItem(updatedItem, cachedPages));
setAcceptedOrganization(organization);
})
.catch(err => handleError(err, [], card.setError));
return (
card
// When accepting an invitation we don't want to trigger a revalidation as this will cause a layout shift, prefer updating in place
.runAsync(async () => {
const updatedItem = await props.accept();
const organization = await getOrganization(props.publicOrganizationData.id);
return [updatedItem, organization] as const;
})
.then(([updatedItem, organization]) => {
// Update cache in case another listener depends on it
void userInvitations?.setData?.(cachedPages => populateCacheUpdateItem(updatedItem, cachedPages, 'negative'));
setAcceptedOrganization(organization);
})
.catch(err => handleError(err, [], card.setError))
);
};

if (acceptedOrganization) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ export const AcceptRejectInvitationButtons = (props: OrganizationSuggestionResou
});

const handleAccept = () => {
return card
.runAsync(props.accept)
.then(updatedItem => userSuggestions?.setData?.(pages => populateCacheUpdateItem(updatedItem, pages)))
.catch(err => handleError(err, [], card.setError));
return (
card
// When accepting a suggestion, a membership is not getting generated, so we don't need to revalidate memberships, only update suggestions in place
.runAsync(props.accept)
.then(updatedItem => userSuggestions?.setData?.(pages => populateCacheUpdateItem(updatedItem, pages)))
.catch(err => handleError(err, [], card.setError))
);
};

if (props.status === 'accepted') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useId } from 'react';

import { withOrganizationsEnabledGuard } from '../../common';
import { withCoreUserGuard } from '../../contexts';
import { AcceptedInvitationsProvider, withCoreUserGuard } from '../../contexts';
import { Flow } from '../../customizables';
import { Popover, withCardStateProvider, withFloatingTree } from '../../elements';
import { usePopover } from '../../hooks';
Expand All @@ -18,24 +18,26 @@ const _OrganizationSwitcher = withFloatingTree(() => {

return (
<Flow.Root flow='organizationSwitcher'>
<OrganizationSwitcherTrigger
ref={reference}
onClick={toggle}
isOpen={isOpen}
aria-controls={switcherButtonMenuId}
/>
<Popover
nodeId={nodeId}
context={context}
isOpen={isOpen}
>
<OrganizationSwitcherPopover
id={switcherButtonMenuId}
close={toggle}
ref={floating}
style={{ ...styles }}
<AcceptedInvitationsProvider>
<OrganizationSwitcherTrigger
ref={reference}
onClick={toggle}
isOpen={isOpen}
aria-controls={switcherButtonMenuId}
/>
</Popover>
<Popover
nodeId={nodeId}
context={context}
isOpen={isOpen}
>
<OrganizationSwitcherPopover
id={switcherButtonMenuId}
close={toggle}
ref={floating}
style={{ ...styles }}
/>
</Popover>
</AcceptedInvitationsProvider>
</Flow.Root>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const NotificationCountBadgeSwitcherTrigger = () => {
containerSx={t => ({
position: 'absolute',
top: `-${t.space.$2}`,
right: 0,
right: `-${t.space.$2}`,
})}
notificationCount={notificationCount}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const CreateOrganizationButton = ({
sx={t => ({
borderBottom: 'none',
color: t.colors.$blackAlpha600,
padding: `${t.space.$5} ${t.space.$5}`,
':hover': {
color: t.colors.$blackAlpha600,
},
Expand All @@ -57,7 +58,7 @@ export const OrganizationActionList = (props: OrganizationActionListProps) => {

return (
<>
<UserInvitationSuggestionList />
<UserInvitationSuggestionList onOrganizationClick={onOrganizationClick} />
<SecondaryActions
elementDescriptor={descriptors.organizationSwitcherPopoverActions}
role='menu'
Expand Down
Loading

0 comments on commit ca67ee7

Please sign in to comment.