Skip to content

Commit

Permalink
improve ux for not registered users
Browse files Browse the repository at this point in the history
  • Loading branch information
sspenst committed May 26, 2024
1 parent c715f0a commit 49bd3f4
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 146 deletions.
87 changes: 42 additions & 45 deletions components/formatted/formattedUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,54 +96,51 @@ export default function FormattedUser({ className, hideAvatar, id, noLinks, noTo
<div
className='flex items-center gap-2 truncate w-fit'
data-tooltip-html={renderToStaticMarkup(
<div className='flex flex-col gap-0.5 p-1 items-start text-sm truncate'>
{!userExtendedData ? <LoadingSpinner /> : <>
{!userExtendedData.user?.ts ? <span>Unregistered for {game.displayName}</span> : <>
{!game.isNotAGame &&
<PlayerRank
levelsSolvedByDifficulty={userExtendedData?.levelsSolvedByDifficulty}
user={user}
/>
}
{!game.isNotAGame && !game.disableRanked &&
<div className='flex gap-1'>
<span className='font-medium'>Ranked Solves:</span>
<span className='gray'>{userExtendedData.user.config?.calcRankedSolves} 🏅</span>
</div>
}
{!game.isNotAGame &&
<div className='flex gap-1'>
<span className='font-medium'>Levels Solved:</span>
<span className='gray'>{userExtendedData.user.config?.calcLevelsSolvedCount}</span>
</div>
}
{!game.isNotAGame &&
<div className='flex gap-1'>
<span className='font-medium'>Levels Completed:</span>
<span className='gray'>{userExtendedData.user.config?.calcLevelsCompletedCount}</span>
</div>
}
{user.hideStatus ? null : isOnline(userExtendedData.user) ?
<div className='flex gap-1 items-center'>
<span className='font-medium'>Currently Playing:</span>
<div className='flex gap-1' style={{
color: 'var(--color-gray)',
}}>
<GameLogoAndLabel gameId={userExtendedData.user.lastGame ?? GameId.THINKY} id={id} size={16} />
</div>
!userExtendedData ? <LoadingSpinner /> :
<div className='flex flex-col gap-0.5 p-1 items-start text-sm truncate'>
{!game.isNotAGame &&
<PlayerRank
levelsSolvedByDifficulty={userExtendedData?.levelsSolvedByDifficulty}
user={user}
/>
}
{!game.isNotAGame && !game.disableRanked &&
<div className='flex gap-1'>
<span className='font-medium'>Ranked Solves:</span>
<span className='gray'>{userExtendedData.user.config?.calcRankedSolves ?? 0} 🏅</span>
</div>
}
{!game.isNotAGame &&
<div className='flex gap-1'>
<span className='font-medium'>Levels Solved:</span>
<span className='gray'>{userExtendedData.user.config?.calcLevelsSolvedCount ?? 0}</span>
</div>
}
{!game.isNotAGame &&
<div className='flex gap-1'>
<span className='font-medium'>Levels Completed:</span>
<span className='gray'>{userExtendedData.user.config?.calcLevelsCompletedCount ?? 0}</span>
</div>
}
{user.hideStatus || !userExtendedData.user.ts ? null : isOnline(userExtendedData.user) ?
<div className='flex gap-1 items-center'>
<span className='font-medium'>Currently Playing:</span>
<div className='flex gap-1' style={{
color: 'var(--color-gray)',
}}>
<GameLogoAndLabel gameId={userExtendedData.user.lastGame ?? GameId.THINKY} id={id} size={16} />
</div>
:
<div className='flex gap-1'>
<span className='font-medium'>Last Seen:</span> <FormattedDate ts={user.last_visited_at ? user.last_visited_at : user.ts} />
</div>
}
</div>
:
<div className='flex gap-1'>
<span className='font-medium'>Registered:</span>
<FormattedDate ts={userExtendedData.user?.ts} />
<span className='font-medium'>Last Seen:</span> <FormattedDate ts={user.last_visited_at ? user.last_visited_at : user.ts} />
</div>
</>}
</>}
</div>
}
<div className='flex gap-1'>
<span className='font-medium'>Registered:</span>
{userExtendedData.user?.ts ? <FormattedDate ts={userExtendedData.user.ts} /> : <span className='gray'>Not registered</span>}
</div>
</div>
)}
data-tooltip-id={tooltipId}
>
Expand Down
15 changes: 5 additions & 10 deletions pages/[subdomain]/profile/[name]/[[...tab]]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ export default function ProfilePage({
const difficultyType = game.type === GameType.COMPLETE_AND_SHORTEST ? 'Completed' : 'Solved';
// create an array of objects with the id, trigger element (eg. button), and the content element
const tabsContent = {
[ProfileTab.Profile]: (user.ts ?
[ProfileTab.Profile]:
<div className='flex flex-col gap-12 mt-4'>
<div className='flex flex-col sm:flex-row gap-8 justify-center items-center max-w-full'>
<div className='flex items-center justify-center'>
Expand All @@ -481,7 +481,7 @@ export default function ProfilePage({
<RoleIcons id='profile' size={24} user={user} />
</div>
{!game.isNotAGame && <div className='flex gap-1'>
{levelsSolvedByDifficulty ? <PlayerRank levelsSolvedByDifficulty={levelsSolvedByDifficulty} tooltip='Highest unlocked skill achievement' user={user} /> : '...'}
{levelsSolvedByDifficulty ? <PlayerRank levelsSolvedByDifficulty={levelsSolvedByDifficulty} tooltip='Highest unlocked skill achievement' user={user} /> : <LoadingSpinner size={24} />}
</div>}
<div className='flex gap-4 flex-wrap justify-center py-1'>
<FollowButton
Expand Down Expand Up @@ -517,15 +517,15 @@ export default function ProfilePage({
{!game.isNotAGame && !game.disableRanked && <div><span className='font-bold'>Ranked Solves:</span> {user.config?.calcRankedSolves ?? 0} 🏅</div>}
{!game.isNotAGame && <div><span className='font-bold'>Levels Solved:</span> {user.config?.calcLevelsSolvedCount ?? 0}</div>}
{!game.isNotAGame && <div><span className='font-bold'>Levels Completed:</span> {user.config?.calcLevelsCompletedCount ?? 0}</div>}
{user.hideStatus ? null : isOnline(user) ?
{user.hideStatus || !user.ts ? null : isOnline(user) ?
<div className='flex flex-wrap gap-1 items-center'>
<span className='font-bold'>Currently Playing:</span>
<GameLogoAndLabel gameId={user.lastGame ?? GameId.THINKY} id={'profile'} size={20} />
</div>
:
<div><span className='font-bold'>Last Seen:</span> <FormattedDate style={{ color: 'var(--color)', fontSize: '1rem' }} ts={user.last_visited_at ? user.last_visited_at : user.ts} /></div>
}
<div><span className='font-bold'>Registered:</span> <FormattedDate style={{ color: 'var(--color)', fontSize: '1rem' }} ts={user.ts} /></div>
<div><span className='font-bold'>Registered:</span> {user.ts ? <FormattedDate style={{ color: 'var(--color)', fontSize: '1rem' }} ts={user.ts} /> : 'Not registered'}</div>
</div>
{!game.isNotAGame &&
<div>
Expand All @@ -550,12 +550,7 @@ export default function ProfilePage({
following={following}
isOpen={isFollowingOpen}
/>
</div>
:
<div className='text-center break-words'>
{user.name} has not yet registered.
</div>
),
</div>,
[ProfileTab.Insights]: <ProfileInsights reqUser={reqUser} user={user} />,
[ProfileTab.Multiplayer]: <ProfileMultiplayer user={user} />,
[ProfileTab.Collections]: (
Expand Down
96 changes: 50 additions & 46 deletions pages/[subdomain]/search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -740,58 +740,62 @@ export default function Search({ enrichedLevels, reqUser, searchAuthor, searchQu
</Menu>
<TimeRangeMenu onTimeRangeClick={onTimeRangeClick} timeRange={query.timeRange} />
</div>
{!game.disableRanked &&
<div className='flex items-center gap-1 border border-color-4 rounded-md px-2 py-1'>
<div className='flex flex-wrap items-center justify-center py-0.5 gap-2'>
{!game.disableRanked &&
<div className='flex items-center gap-1 border border-color-3 rounded-md px-2 py-1'>
<input
checked={query.isRanked === 'true'}
id='ranked_checkbox'
onChange={() => {
fetchLevels({
...query,
isRanked: query.isRanked === 'true' ? 'false' : 'true',
page: '1',
});
}}
type='checkbox'
/>
<label className='text-sm font-medium' htmlFor='ranked_checkbox'>
🏅 Ranked
</label>
</div>
}
<div>
<label htmlFor='min-step' className='text-xs font-medium pr-1'>Min steps</label>
<input
checked={query.isRanked === 'true'}
id='ranked_checkbox'
onChange={() => {
fetchLevels({
...query,
isRanked: query.isRanked === 'true' ? 'false' : 'true',
className='w-20 text-sm px-2 py-1'
id='min-step'
max='2500'
min='1'
onChange={(e: React.FormEvent<HTMLInputElement>) => {
queryDebounceHelper({
minSteps: (e.target as HTMLInputElement).value,
page: '1',
});
}}
type='checkbox'
step='1'
type='number'
value={query.minSteps}
/>
</div>
<div>
<label htmlFor='max-step' className='text-xs font-medium pr-1'>Max steps</label>
<input
className='w-20 text-sm px-2 py-1'
id='max-step'
max='2500'
min='1'
onChange={(e: React.FormEvent<HTMLInputElement>) => {
queryDebounceHelper({
maxSteps: (e.target as HTMLInputElement).value,
page: '1',
});
}}
step='1'
type='number'
value={query.maxSteps}
/>
<label className='text-sm font-medium' htmlFor='ranked_checkbox'>
🏅 Ranked
</label>
</div>
}
<div className='flex items-center justify-center py-0.5'>
<label htmlFor='min-step' className='text-xs font-medium pr-1'>Min steps</label>
<input
className='w-20 text-sm px-2 py-1 mr-2'
id='min-step'
max='2500'
min='1'
onChange={(e: React.FormEvent<HTMLInputElement>) => {
queryDebounceHelper({
minSteps: (e.target as HTMLInputElement).value,
page: '1',
});
}}
step='1'
type='number'
value={query.minSteps}
/>
<label htmlFor='max-step' className='text-xs font-medium pr-1'>Max steps</label>
<input
className='w-20 text-sm px-2 py-1'
id='max-step'
max='2500'
min='1'
onChange={(e: React.FormEvent<HTMLInputElement>) => {
queryDebounceHelper({
maxSteps: (e.target as HTMLInputElement).value,
page: '1',
});
}}
step='1'
type='number'
value={query.maxSteps}
/>
</div>
<div className='flex justify-center items-center gap-2'>
<Link href='/pro' passHref>
Expand Down
54 changes: 9 additions & 45 deletions pages/[subdomain]/users/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import Page from '../../../components/page/page';
import Dimensions from '../../../constants/dimensions';
import GraphType from '../../../constants/graphType';
import { AppContext } from '../../../contexts/appContext';
import { TimerUtil } from '../../../helpers/getTs';
import { logger } from '../../../helpers/logger';
import dbConnect from '../../../lib/dbConnect';
import { MultiplayerMatchType } from '../../../models/constants/multiplayer';
Expand All @@ -41,17 +40,15 @@ interface UserWithStats extends User {
export interface UserSearchQuery extends ParsedUrlQuery {
page: string;
search: string;
showOnline: string;
showUnregistered: string;
showNotRegistered: string;
sortBy: string;
sortDir: 'desc' | 'asc';
}

export const DEFAULT_QUERY = {
page: '1',
search: '',
showOnline: 'false',
showUnregistered: 'false',
showNotRegistered: 'false',
sortBy: 'config.calcLevelsSolvedCount',
sortDir: 'desc',
} as UserSearchQuery;
Expand All @@ -71,7 +68,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
}
}

const { page, search, showOnline, showUnregistered, sortBy, sortDir } = searchQuery;
const { page, search, showNotRegistered, sortBy, sortDir } = searchQuery;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const searchObj = {} as { [key: string]: any };
Expand All @@ -83,15 +80,10 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
};
}

if (showUnregistered !== 'true') {
if (showNotRegistered !== 'true') {
searchObj['ts'] = { $exists: true };
}

if (showOnline === 'true') {
searchObj['hideStatus'] = { $ne: true };
searchObj['last_visited_at'] = { $gt: TimerUtil.getTs() - 5 * 60 };
}

const sortObj = [[sortBy, sortDir === 'asc' ? 1 : -1]];

// if we are sortting by completion then make the second order sort by solves
Expand Down Expand Up @@ -506,49 +498,21 @@ export default function PlayersPage({ searchQuery, totalRows, users }: PlayersPr
/>
<div className='flex flex-row gap-2 justify-center text-sm'>
<input
checked={query.showOnline === 'true'}
id='showOnline'
name='collection'
onChange={() => {
fetchLevels({
...query,
showOnline: String(query.showOnline !== 'true'),
});
}}
type='checkbox'
/>
<label htmlFor='showOnline'>
Show online
</label>
</div>
<div className='flex flex-row gap-2 justify-center text-sm'>
<input
checked={query.showUnregistered === 'true'}
id='showUnregistered'
checked={query.showNotRegistered === 'true'}
id='showNotRegistered'
name='collection'
onChange={() => {
fetchLevels({
...query,
showUnregistered: String(query.showUnregistered !== 'true'),
showNotRegistered: String(query.showNotRegistered !== 'true'),
});
}}
type='checkbox'
/>
<label htmlFor='showUnregistered'>
Show unregistered
<label htmlFor='showNotRegistered'>
Show not registered
</label>
</div>
<div className='flex justify-center'>
<button
className='italic underline text-sm'
onClick={() => {
setQuery({ ...DEFAULT_QUERY });
fetchLevels({ ...DEFAULT_QUERY });
}}
>
Reset search filters
</button>
</div>
</div>
<DataTable
conditionalRowStyles={[{
Expand Down

0 comments on commit 49bd3f4

Please sign in to comment.