Skip to content

Commit

Permalink
fix(organizations): adjust loading process for invite responses TASK-…
Browse files Browse the repository at this point in the history
…1602 (#5547)

### 📣 Summary
Introduces a delay in refreshing user data after an MMO invitation is
accepted to allow for initial backend project transfers and separates
the loading state of the accept/decline buttons.
  • Loading branch information
jamesrkiger authored Feb 21, 2025
1 parent 39e5fa9 commit dd60d08
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 9 deletions.
38 changes: 30 additions & 8 deletions jsapp/js/account/organization/invites/OrgInviteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export default function OrgInviteModal(props: { orgId: string; inviteId: string;
)

const [isModalOpen, setIsModalOpen] = useState(true)
const [awaitingDataRefresh, setAwaitingDataRefresh] = useState(false)
const [userResponseType, setUserResponseType] = useState<MemberInviteStatus | null>(null)
const session = useSession()
const orgMemberInviteQuery = useOrgMemberInviteQuery(props.orgId, props.inviteId, false)
const patchMemberInvite = usePatchMemberInvite(inviteUrl, false)
Expand All @@ -42,29 +44,39 @@ export default function OrgInviteModal(props: { orgId: string; inviteId: string;
// We use `mmoLabel` as fallback until `organization_name` is available at the endpoint
const orgName = orgMemberInviteQuery.data?.organization_name || mmoLabel

function handleSuccessfulInviteResponse(message: string) {
// Ensure that fresh session is fetched, as we get the organiztion url from it.
session.refreshAccount()
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

async function handleSuccessfulInviteResponse(message: string, refreshData: boolean = false) {
// After a one-second delay to allow for initial backend data transfers,
// refresh session to refresh org data and project list
if (refreshData) {
setAwaitingDataRefresh(true)
await wait(1000)
session.refreshAccount()
}
props.onUserResponse()
setIsModalOpen(false)
notify(message)
}

const handleDeclineInvite = async () => {
try {
setUserResponseType(MemberInviteStatus.declined)
await patchMemberInvite.mutateAsync({ status: MemberInviteStatus.declined })
handleSuccessfulInviteResponse(t('Invitation successfully declined'))
} catch (error) {
setMiscError(t('Unknown error while trying to update an invitation'))
setUserResponseType(null)
}
}

const handleAcceptInvite = async () => {
try {
setUserResponseType(MemberInviteStatus.accepted)
await patchMemberInvite.mutateAsync({ status: MemberInviteStatus.accepted })
handleSuccessfulInviteResponse(t('Invitation successfully accepted'))
await handleSuccessfulInviteResponse(t('Invitation successfully accepted'), true)
} catch (error) {
setMiscError(t('Unknown error while trying to update an invitation'))
setUserResponseType(null)
}
}

Expand All @@ -76,7 +88,7 @@ export default function OrgInviteModal(props: { orgId: string; inviteId: string;
let title: React.ReactNode = null

// Case 1: loading data.
if (orgMemberInviteQuery.isLoading) {
if (orgMemberInviteQuery.isLoading || awaitingDataRefresh) {
content = <LoadingSpinner />
}
// Case 2: failed to get the invitation data from API.
Expand Down Expand Up @@ -146,11 +158,21 @@ export default function OrgInviteModal(props: { orgId: string; inviteId: string;
</Alert>

<Group justify='flex-end'>
<Button variant='light' size='lg' onClick={handleDeclineInvite} loading={patchMemberInvite.isPending}>
<Button
variant='light'
size='lg'
onClick={handleDeclineInvite}
loading={userResponseType === MemberInviteStatus.declined && patchMemberInvite.isPending}
>
{t('Decline')}
</Button>

<Button variant='filled' size='lg' onClick={handleAcceptInvite} loading={patchMemberInvite.isPending}>
<Button
variant='filled'
size='lg'
onClick={handleAcceptInvite}
loading={userResponseType === MemberInviteStatus.accepted && patchMemberInvite.isPending}
>
{t('Accept')}
</Button>
</Group>
Expand Down
4 changes: 3 additions & 1 deletion jsapp/js/projects/universalProjectsRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@ function UniversalProjectsRoute(props: UniversalProjectsRouteProps) {

<ProjectsTable
assets={customView.assets}
isLoading={!customView.isFirstLoadComplete}
// refreshing session will result in refreshing table, so while that is pending
// we want to show a loading spinner
isLoading={!customView.isFirstLoadComplete || session.isPending}
highlightedFields={getFilteredFieldsNames()}
visibleFields={getTableVisibleFields()}
orderableFields={props.defaultOrderableFields}
Expand Down

0 comments on commit dd60d08

Please sign in to comment.