Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions packages/mobile/src/screens/contest-screen/ContestScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ export const ContestScreen = () => {
const eventId = contest?.eventId

const { data: currentUserId } = useCurrentUserId()
const { data: followState } = useEventFollowState(eventId)
const { data: followState, isPending: isFollowStatePending } =
useEventFollowState(eventId)
const { mutate: followEvent } = useFollowEvent()
const { mutate: unfollowEvent } = useUnfollowEvent()
const isOwner = !!currentUserId && currentUserId === track?.owner_id
Expand Down Expand Up @@ -444,10 +445,18 @@ export const ContestScreen = () => {
variant={followState?.isFollowed ? 'secondary' : 'primary'}
size='small'
onPress={handleToggleFollow}
// Show a spinner while the follow state loads instead of
// defaulting to "Follow", which would flash before snapping
// to "Following" for users who already follow the contest.
isLoading={isFollowStatePending}
disabled={!currentUserId || !eventId}
fullWidth
>
{followState?.isFollowed ? messages.following : messages.follow}
{isFollowStatePending
? ''
: followState?.isFollowed
? messages.following
: messages.follow}
</Button>
</Flex>
{!isEnded ? (
Expand Down
18 changes: 18 additions & 0 deletions packages/web/src/pages/contest-page/ContestPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,24 @@ describe('ContestPage', () => {
).not.toBeInTheDocument()
})

it('does not flash "Follow" while the follow state is still loading', () => {
// Mid-flight: no data yet. The button must not render the unfollowed
// "Follow" label, otherwise it flashes before snapping to "Following"
// for users who already follow the contest.
mocks.useEventFollowState.mockReturnValue({
data: undefined,
isPending: true
})
renderContestPage()

expect(
screen.queryByRole('button', { name: /^follow$/i })
).not.toBeInTheDocument()
expect(
screen.queryByRole('button', { name: /^following$/i })
).not.toBeInTheDocument()
})

it('includes the track title in the page header when the contest is attached to a track', () => {
renderContestPage()
// The redesigned hero composes the display title as
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ const ContestPage = ({ containerRef: _containerRef }: ContestPageProps) => {
const eventId = contest?.eventId

const { data: currentUserId } = useCurrentUserId()
const { data: followState } = useEventFollowState(eventId)
const { data: followState, isPending: isFollowStatePending } =
useEventFollowState(eventId)
const { mutate: followEvent } = useFollowEvent()
const { mutate: unfollowEvent } = useUnfollowEvent()
// Drives the Following → Unfollow label swap on hover for the contest
Expand Down Expand Up @@ -452,12 +453,17 @@ const ContestPage = ({ containerRef: _containerRef }: ContestPageProps) => {
size='small'
variant={isFollowed ? 'primary' : 'secondary'}
iconLeft={followIcon}
// While the follow state is still loading, render a spinner-only
// button (fixed width keeps layout stable) so it doesn't flash
// "Follow" before snapping to "Following" for users who already
// follow the contest.
isLoading={isFollowStatePending}
onClick={handleToggleFollow}
onMouseEnter={() => setIsFollowHovering(true)}
onMouseLeave={() => setIsFollowHovering(false)}
css={{ width: 112, justifyContent: 'center' }}
>
{followLabel}
{isFollowStatePending ? '' : followLabel}
</Button>
{!isEnded ? (
<Button size='small' onClick={handleEnterContest}>
Expand All @@ -471,6 +477,7 @@ const ContestPage = ({ containerRef: _containerRef }: ContestPageProps) => {
isOwner,
isEnded,
followState?.isFollowed,
isFollowStatePending,
isFollowHovering,
handleToggleFollow,
handleEditContest,
Expand Down
Loading