Skip to content
Draft
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
4 changes: 3 additions & 1 deletion public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@
"disableNotificationsConfirm": "Disable Notifications?\nYou will stop receiving notifications when friends make a move",
"notificationsDisabled": "Notifications Disabled",
"notificationsEnabled": "Notifications Enabled",
"enableNotifications": "Enable Notifications"
"enableNotifications": "Enable Notifications",
"rulesEnforced": "Rules: On",
"rulesNotEnforced": "Rules: Off"
}
11 changes: 10 additions & 1 deletion src/Dialogues/Friends.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import InfoIcon from '@material-design-icons/svg/filled/info.svg?react';
import LogoutIcon from '@material-design-icons/svg/filled/logout.svg?react';
import LocalIcon from '@material-design-icons/svg/filled/location_on.svg?react';
import GavelIcon from '@material-design-icons/svg/filled/gavel.svg?react';

type Users = { [key: string]: User }

Expand All @@ -34,11 +35,13 @@
friend?: User;
load: (userId?: string | false, key?: string) => void;
reset: () => void;
rulesEnforced: boolean;
toggleRules: (value?: boolean) => void;
}

type NotificationStatus = NotificationPermission | 'unsupported' | 'processing';

export default function Friends({ user, load, reset, friend }: FriendsProps) {
export default function Friends({ user, load, reset, friend, rulesEnforced, toggleRules }: FriendsProps) {
const { t } = useTranslation();
const { toggle } = useContext(DialogContext)!;

Expand Down Expand Up @@ -208,7 +211,7 @@
case 'denied':
alert(t('notificationsDenied'));
break;
// @ts-expect-error

Check failure on line 214 in src/Dialogues/Friends.tsx

View workflow job for this annotation

GitHub Actions / test

Include a description after the "@ts-expect-error" directive to explain why the @ts-expect-error is necessary. The description must be 3 characters or longer
case 'granted':
if (hasFcmToken) {
const confirmMessage = t('disableNotificationsConfirm');
Expand All @@ -220,7 +223,7 @@
break;
}
// fall-through
case 'default':

Check failure on line 226 in src/Dialogues/Friends.tsx

View workflow job for this annotation

GitHub Actions / test

Expected a 'break' statement before 'case'

setNotificationStatus('processing');
fcmTokenRef.current = await saveFcmToken();
Expand Down Expand Up @@ -286,6 +289,12 @@
: t('enableNotifications')}
</a>
</li>
<li>
<a onPointerUp={(e) => { e.preventDefault(); toggleRules(); }} href="#">
<GavelIcon className="material-icons-svg notranslate" />
{rulesEnforced ? t('rulesEnforced') : t('rulesNotEnforced')}
</a>
</li>
<li>
<a onPointerUp={handleReset} href="#">
<RestartAltIcon className="material-icons-svg notranslate" />
Expand Down
11 changes: 10 additions & 1 deletion src/Dialogues/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import BugReportIcon from '@material-design-icons/svg/filled/bug_report.svg?react';
import InfoIcon from '@material-design-icons/svg/filled/info.svg?react';
import CancelIcon from '@material-design-icons/svg/filled/cancel.svg?react';
import GavelIcon from '@material-design-icons/svg/filled/gavel.svg?react';

// Configure FirebaseUI.
const uiConfig = {
Expand All @@ -39,9 +40,11 @@
reset: () => void;
friend?: User;
load: () => void;
rulesEnforced: boolean;
toggleRules: (value?: boolean) => void;
};

export default function Login({ reset, friend, load }: LoginProps) {
export default function Login({ reset, friend, load, rulesEnforced, toggleRules }: LoginProps) {
const { t } = useTranslation();
const [userSignedIn, setUserSignedIn] = useState(false);
const elementRef = useRef(null);
Expand All @@ -62,7 +65,7 @@
});

// Render the firebaseUi Widget.
// @ts-ignore

Check failure on line 68 in src/Dialogues/Login.tsx

View workflow job for this annotation

GitHub Actions / test

Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free
firebaseUiWidget.start(elementRef.current, uiConfig);

return () => {
Expand Down Expand Up @@ -92,6 +95,12 @@
<ToggleFullscreen />
</li>
: null}
<li>
<a onPointerUp={(e) => { e.preventDefault(); toggleRules(); }} href="#">
<GavelIcon className="material-icons-svg notranslate" />
{rulesEnforced ? t('rulesEnforced') : t('rulesNotEnforced')}
</a>
</li>
<li>
<a onPointerUp={(e) => { e.preventDefault(); reset(); }} href="#">
<RestartAltIcon className="material-icons-svg notranslate" />
Expand Down
8 changes: 5 additions & 3 deletions src/Dialogues/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
toggle: (value?: string | boolean) => void;
};

export const DialogContext = createContext<DialogContextType | undefined>(undefined);

Check warning on line 15 in src/Dialogues/index.tsx

View workflow job for this annotation

GitHub Actions / test

Fast refresh only works when a file only exports components. Move your React context(s) to a separate file

interface DialoguesProps {
user: SnapshotOrNullType;
Expand All @@ -21,10 +21,12 @@
reset: () => void;
chats: SnapshotOrNullType;
gameover: User | undefined;
rulesEnforced: boolean;
toggleRules: (value?: boolean) => void;
children: React.ReactNode
}

export default function Dialogues({ user, friend, load, reset, chats, gameover, children }: DialoguesProps) {
export default function Dialogues({ user, friend, load, reset, chats, gameover, rulesEnforced, toggleRules, children }: DialoguesProps) {
const [state, setState] = useState<string | boolean>(false);
const [lastDialog, setLastDialog] = useState<Modal>(Modal.Friends);
const toggle = useCallback((value?: string | boolean) => {
Expand Down Expand Up @@ -62,7 +64,7 @@
<dialog onCancel={() => toggle(false)} open={isOpen}>
{user ? (
state === 'friends' ? (
<Friends user={user} load={load} reset={reset} friend={friend} />
<Friends user={user} load={load} reset={reset} friend={friend} rulesEnforced={rulesEnforced} toggleRules={toggleRules} />
) : state === 'profile' ? (
<Profile user={user} />
) : state === 'chat' ? (
Expand All @@ -71,7 +73,7 @@
<Gameover user={gameover} reset={reset} winner={user?.key === gameover.uid} />
) : null
) : (
<Login reset={reset} friend={friend} load={load} />
<Login reset={reset} friend={friend} load={load} rulesEnforced={rulesEnforced} toggleRules={toggleRules} />
)}
</dialog>
{children}
Expand Down
29 changes: 22 additions & 7 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,21 @@ export function App() {
const [selected, setSelected] = useState<number | null>(null);
const [usedDice, setUsedDice] = useState<UsedDie[]>([]);
const [homeDragOver, setHomeDragOver] = useState<Color | null>(null);
const [rulesEnforced, setRulesEnforced] = useState<boolean>(() => {
const stored = localStorage.getItem('rulesEnforced');
return stored === null ? true : stored === 'true';
});
const hadMatchRef = useRef(false);
const gameSnapshotRef = useRef<SnapshotOrNullType>(null);

const toggleRules = useCallback((value?: boolean) => {
setRulesEnforced(prev => {
const next = value !== undefined ? value : !prev;
localStorage.setItem('rulesEnforced', String(next));
return next;
});
}, []);

const load = useCallback(async (friendId?: string | false, authUserUid?: string) => {
if (friendId === 'PeaceInTheMiddleEast' || friendId === '__' || friendId === 'preview') return;
console.log('Loading', friendId, 'with authUserUid:', authUserUid);
Expand Down Expand Up @@ -159,7 +171,8 @@ export function App() {
setGame({
...game,
dice,
status: Status.Moving
status: Status.Moving,
...(rulesEnforced && { color: game.color === Color.White ? Color.Black : Color.White })
});
}

Expand All @@ -169,7 +182,7 @@ export function App() {
setSelected(null);
// TODO: autoselect bar, but game.color is not set yet
// setSelected(match && game.color && game.prison[game.color] ? -1 : null);
}, [match, game, isMyTurn, user, friend, usedDice]);
}, [match, game, isMyTurn, user, friend, usedDice, rulesEnforced]);

const moves = useMemo(() => {
if (!isMyTurn || game.status !== Status.Moving)
Expand All @@ -191,7 +204,7 @@ export function App() {
}, [usedDice, game])

const move = useCallback((from: number | Color, to: number) => {
if (match && (!moves.has(to) || game.status !== Status.Moving)) return;
if ((match || rulesEnforced) && (!moves.has(to) || game.status !== Status.Moving)) return;
const { state: nextState, moveLabel, usedDie } = calculate(game, from, to, usedDice)
if (!moveLabel) return; // invalid
playCheckerSound()
Expand Down Expand Up @@ -229,7 +242,7 @@ export function App() {
}
return newUsedDice;
});
}, [game, match, moves, usedDice, user, friend]);
}, [game, match, moves, usedDice, user, friend, rulesEnforced]);

const onHomeDragOver: DragEventHandler = useCallback((event) => {
event.preventDefault();
Expand Down Expand Up @@ -438,6 +451,8 @@ export function App() {
reset={reset}
chats={chats}
gameover={winner}
rulesEnforced={rulesEnforced}
toggleRules={toggleRules}
>
<div id="board" className={game.color}>
<Toolbar friend={friendData} />
Expand All @@ -460,7 +475,7 @@ export function App() {
position={-1}
color={Color.White}
onSelect={onSelect}
enabled={isMyTurn && (!game.color || game.color === Color.White) && (!match || sources.has(-1))}
enabled={isMyTurn && (!game.color || game.color === Color.White) && (!(match || rulesEnforced) || sources.has(-1))}
selected={selected}
/>
)}
Expand All @@ -478,7 +493,7 @@ export function App() {
position={-1}
color={Color.Black}
onSelect={onSelect}
enabled={isMyTurn && (!game.color || game.color === Color.Black) && (!match || sources.has(-1))}
enabled={isMyTurn && (!game.color || game.color === Color.Black) && (!(match || rulesEnforced) || sources.has(-1))}
selected={selected}
/>
)}
Expand Down Expand Up @@ -514,7 +529,7 @@ export function App() {
</div>
{game.board.map((pieces: number, index: number) =>
<Point
enabled={!match || sources.has(index)}
enabled={!(match || rulesEnforced) || sources.has(index)}
valid={moves.has(index)}
key={index}
pieces={pieces}
Expand Down
Loading