Skip to content

Commit

Permalink
Merge pull request #114 from the-ai-team/feat/ui-updates
Browse files Browse the repository at this point in the history
[Related #112] UI updates and fixes
  • Loading branch information
supunTE authored Mar 18, 2024
2 parents 439f20f + 179bdf7 commit 231a282
Show file tree
Hide file tree
Showing 33 changed files with 501 additions and 179 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/onPR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.19.0]
node-version: [ 16.19.0 ]
steps:
- uses: actions/checkout@v2
with:
Expand All @@ -25,7 +25,6 @@ jobs:
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
onlyChanged: true
autoAcceptChanges: main
- if: steps.chromatic.outputs.buildUrl != 'undefined'
name: Publish Summary
Expand Down
4 changes: 2 additions & 2 deletions apps/client/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {
clearGlobalToastManager,
setGlobalToastManager,
} from './utils/globalToastManager';
import { AppRouter } from './appRouter';
import { useToastContext } from './hooks';
import { Router } from './router';

export function App(): ReactElement {
const addToast = useToastContext();
Expand All @@ -18,5 +18,5 @@ export function App(): ReactElement {
};
}, [addToast]);

return <Router />;
return <AppRouter />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { NotFound } from './pages/NotFound';
import { GuardedRoute } from './utils/guardedRoute';
import { Home, Layout, Leaderboard, Race, Room } from './pages';

export function Router(): ReactElement {
export function AppRouter(): ReactElement {
return (
<BrowserRouter>
<Routes>
Expand Down
76 changes: 69 additions & 7 deletions apps/client/src/components/molecules/timer/Timer.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,42 @@ import { Text } from '../../atoms';

export interface TimerProps {
time: number; // in seconds
showSpeed?: boolean;
speedValue?: string;
speedometerPercentage?: number;
onTimeEnd?: () => void;
}

/**
*
* @param time - Time in seconds
* @param showSpeed - Whether to show speedometer or not
* @param speedValue - Speed value in wpm
* @param speedometerPercentage - Percentage of speedometer (0-1)
* @param [onTimeEnd] - Callback function to be called when time ends (optional)
*/
export function Timer({
time,
showSpeed = false,
speedValue = '0 wpm',
speedometerPercentage = 0,
// eslint-disable-next-line @typescript-eslint/no-empty-function
onTimeEnd = (): void => {},
}: TimerProps): ReactElement {
const startTimestamp = useRef(Date.now());
const previousTimestamp = useRef(Date.now());
const previousTimestamp = useRef(0);
const [seconds, setSeconds] = useState(time);
const [milliseconds, setMilliseconds] = useState(0);
const circleIndicator = useRef<SVGCircleElement>(null);
const timerEnded = useRef(false);

// Consider time is low if time is less than 20% of the total time
const isTimeLow = time > 0 && seconds <= time * 0.15;

useEffect(() => {
startTimestamp.current = Date.now();
previousTimestamp.current = Date.now();
setSeconds(time);
previousTimestamp.current = 0;
setSeconds(Math.trunc(time));
setMilliseconds(0);
timerEnded.current = false;
}, [time]);
Expand All @@ -51,9 +63,14 @@ export function Timer({
return;
}

// TODO: Check possibility of removing previousTimestamp
const now = Date.now();

// Optimizing for high refresh rate screens
// This animation will run on every frame.
// But in high refresh rate screens, time difference between frames is very low.
// Therefore, we are skipping the animation for a specific frame
// where time difference between last and current frames is less than 10ms
if (now - previousTimestamp.current < 10) {
return;
}
Expand Down Expand Up @@ -108,7 +125,7 @@ export function Timer({
'flex justify-center items-center',
'relative',
)}>
<div className='text-center flex flex-col justify-center items-center mb-10'>
<div className='text-center flex flex-col justify-center items-center mb-10 z-20'>
<Text type={TextType.Heading} size={TextSize.ExtraLarge}>
{seconds.toString().padStart(2, '0')}
</Text>
Expand All @@ -119,16 +136,50 @@ export function Timer({
{`.${milliseconds.toString().padStart(2, '0')}`}
</Text>
</div>
{showSpeed && (
<div
className={cs(
'h-1/2 w-2 bg-neutral-30 absolute bottom-1/2',
'z-10 origin-bottom',
'rounded-full',
'transition-all duration-300',
)}
style={{
rotate: `${speedometerPercentage * 180 - 90}deg`,
}}>
<div className='h-1/2 w-2 bg-primary-70 outline outline-neutral-50 outline-offset-2 outline-1 rounded-full'></div>
<Text
type={TextType.Title}
size={TextSize.Small}
className='absolute -top-8 left-0 right-0 flex justify-center whitespace-nowrap'>
{speedValue || '0'}
</Text>
</div>
)}
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 300 300'
preserveAspectRatio='none'
className='absolute top-0 left-0 w-full h-full'>
{showSpeed && (
<circle
cx='50%'
cy='50%'
r='45%'
fill='none'
strokeWidth='26'
transform='rotate(-180,150,150)'
stroke={`url(#gradient)`}
strokeDasharray='140%, 20000'
className={cs('transition-all duration-300')}
/>
)}

{/* Circle below time indicator */}
<circle
cx='50%'
cy='50%'
r='40%'
r='38%'
fill='none'
strokeWidth='15'
transform='rotate(-90,150,150)'
Expand All @@ -139,13 +190,24 @@ export function Timer({
ref={circleIndicator}
cx='50%'
cy='50%'
r='40%'
r='38%'
fill='none'
strokeWidth='15'
strokeLinecap='round'
transform='rotate(-90,150,150)'
className='stroke-neutral-90'
className={cs(
isTimeLow ? 'stroke-error-60' : 'stroke-neutral-90',
'transition-all duration-300',
)}
/>

<linearGradient id='gradient' x1='0%' y1='0%' x2='100%' y2='0%'>
<stop offset='0%' style={{ stopColor: '#D34446', stopOpacity: 1 }} />
<stop
offset='100%'
style={{ stopColor: '#534342', stopOpacity: 1 }}
/>
</linearGradient>
</svg>
</div>
);
Expand Down
3 changes: 3 additions & 0 deletions apps/client/src/components/molecules/timer/Timer.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export default {
component: Timer,
args: {
time: 10,
showSpeed: false,
speedValue: '50 WPM',
speedometerPercentage: 0.25,
onTimeEnd: () => {
console.log('Time ended');
},
Expand Down
31 changes: 16 additions & 15 deletions apps/client/src/components/organisms/panel/Panel.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ export function Panel({ title, children }: PanelProps): ReactElement {
return (
<div
className={cs(
{ 'h-32 pt-4': isCollapse },
{ 'h-2/3 pt-12 pb-20': !isCollapse },
{ 'lg:h-32 h-20 w-96 lg:pt-4 pt-3 px-6': isCollapse },
{ 'h-2/3 lg:pt-8 pt-2 pb-16 lg:px-10 px-8': !isCollapse },
'flex flex-col',
'absolute top-5 right-5 z-50',
'px-12',
'w-1/3 max-w-xl max-h-[1200px] min-w-[400px] rounded-md bg-neutral-20',
'w-1/3 max-w-lg max-h-[1200px] min-w-[350px] rounded-md bg-neutral-20',
'border border-neutral-40',
'hover:ring-4 hover:ring-neutral-40',
'transition-all duration-300',
Expand All @@ -47,7 +46,10 @@ export function Panel({ title, children }: PanelProps): ReactElement {
<div
className={cs(
{ 'gap-20 overflow-y-hidden': isCollapse },
{ 'overflow-y-auto scrollbar': !isCollapse },
{
'overflow-y-auto scrollbar py-4 overflow-mask top-overflowing bottom-overflowing':
!isCollapse,
},
'flex flex-col pr-5',
'transition-all duration-300',
'gap-5',
Expand All @@ -71,21 +73,20 @@ export function Panel({ title, children }: PanelProps): ReactElement {
</div>
</div>
<div>
<div className='flex flex-row justify-center items-center absolute bottom-0 left-0 p-5 w-full text-neutral-90'>
<div
className={cs(
'flex flex-row items-center absolute bottom-0 left-0 p-5 w-full text-neutral-90',
{ 'lg:justify-center justify-end': isCollapse },
{ 'justify-center': !isCollapse },
)}>
<ArrowHeadTopIcon
className={cs('w-10 h-10 transition-all duration-300', {
'rotate-180': isCollapse,
})}
/>
{isCollapse ? (
<Text type={TextType.Title} size={TextSize.Small}>
Show Panel
</Text>
) : (
<Text type={TextType.Title} size={TextSize.Small}>
Hide Panel
</Text>
)}
<Text type={TextType.Title} size={TextSize.Small}>
{isCollapse ? 'Show Panel' : 'Hide Panel'}
</Text>
</div>
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions apps/client/src/constants/race.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export const MAX_INVALID_CHARS_ALLOWED = 5;
export const MIN_RACE_TRACKS = 5;

export const RECENT_WPM_TIME_WINDOW = 5; // seconds
export const RECENT_WPM_UPDATE_INTERVAL = 1000; // milliseconds
6 changes: 6 additions & 0 deletions apps/client/src/i18n/en/home.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
}
]
},
"toasts": {
"invalid_room_id": {
"title": "Invalid room id",
"message": "The room id you entered is invalid. Please try again."
}
},
"room_id": "(Room id: {{id}})",
"actions": {
"join": "Join",
Expand Down
3 changes: 2 additions & 1 deletion apps/client/src/i18n/en/leaderboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@
"content": "Want to show off your leaderboard achievements to your friends and followers on social media? Simply take a screenshot of the leaderboard, featuring your name and stats, and share it with the world. Let everyone see your typing prowess and inspire them to join the action on Razor!"
}
]
}
},
"timeout_players": "Timeout Players"
}
4 changes: 4 additions & 0 deletions apps/client/src/i18n/en/race.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"race_start": {
"title": "Ready, set, go!",
"message": "{{player_name}} started the race. Good luck!"
},
"reconnected_as_new": {
"title": "Reconnected as a New Player",
"message": "Unfortunately, you were removed from the race after an extended period of inactivity. Despite your reconnection, the server had to remove you. You'll now be treated as a new player in the tournament."
}
}
}
12 changes: 10 additions & 2 deletions apps/client/src/pages/home/Home.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { generateAvatarLink } from '@razor/util';
import cs from 'classnames';
import { debounce } from 'lodash';
import { ReactComponent as ChevronRight } from 'pixelarticons/svg/chevron-right.svg';
import { ReactComponent as TagIcon } from 'pixelarticons/svg/label.svg';

import { ReactComponent as Logo } from '../../assets/images/logo.svg';
import { ReactComponent as LogoFill } from '../../assets/images/logo-fill.svg';
Expand All @@ -22,7 +23,9 @@ import {
Link,
Panel,
Text,
ToastType,
} from '../../components';
import { useToastContext } from '../../hooks';
import { TextSize, TextType } from '../../models';
import { endSocket } from '../../services';
import {
Expand All @@ -33,6 +36,7 @@ import {
export function Home(): ReactElement {
const { t } = useTranslation('home');
const { roomId } = useParams();
const addToast = useToastContext();

// disconnect any socket connection if user navigates back to home page.
useEffect(() => {
Expand Down Expand Up @@ -142,8 +146,12 @@ export function Home(): ReactElement {
if (isIdValid) {
navigate(`/${value}`);
} else {
// TODO: Implement proper component
alert('Invalid tournament id');
addToast({
title: t('toasts.invalid_room_id.title'),
type: ToastType.Error,
message: t('toasts.invalid_room_id.message') as string,
icon: <TagIcon />,
});
}
};

Expand Down
5 changes: 3 additions & 2 deletions apps/client/src/pages/leaderboard/Leaderboard.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,12 @@ export function Leaderboard(): ReactElement {
image={panelImages[1]}>
{t('panel.descriptions.1.content') as string}
</Description>
<Description
{/* TODO: Implement screenshot feature */}
{/* <Description
title={t('panel.descriptions.2.title')}
image={panelImages[2]}>
{t('panel.descriptions.2.content') as string}
</Description>
</Description> */}
</Panel>

<div
Expand Down
Loading

0 comments on commit 231a282

Please sign in to comment.