Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added an auto-pause preference, added Esc to unpause, & dup'd preferences to solver more menu #466

Merged
merged 4 commits into from
Feb 2, 2024
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
41 changes: 34 additions & 7 deletions app/components/Puzzle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
FaEdit,
FaRegFile,
FaMoon,
FaCog,
} from 'react-icons/fa';
import { ClueText } from './ClueText';
import { IoMdStats } from 'react-icons/io';
Expand Down Expand Up @@ -91,6 +92,7 @@ import {
TopBarDropDownLinkA,
TopBarDropDown,
TopBarDropDownLinkSimpleA,
NestedDropDown,
} from './TopBar';
import {
SLATE_PADDING_LARGE,
Expand Down Expand Up @@ -148,6 +150,7 @@ import { SlateColorTheme } from './SlateColorTheme';
import { Check, Clues, Grid, More, Pause, Reveal, Timer } from './SlateIcons';
import { removeSpoilers } from '../lib/markdown/markdown';
import { SlateBegin, SlatePause, SlateSuccess } from './SlateOverlays';
import { SolverPreferencesList } from './SolverPreferencesList';

const ModeratingOverlay = dynamic(
() => import('./ModerateOverlay').then((mod) => mod.ModeratingOverlay),
Expand Down Expand Up @@ -392,7 +395,7 @@ export const Puzzle = ({

// Pause when page goes out of focus
function prodPause() {
if (process.env.NODE_ENV !== 'development') {
if (process.env.NODE_ENV !== 'development' && !props.prefs?.dontPauseOnLostFocus) {
window.parent.postMessage(
{
type: 'pause',
Expand Down Expand Up @@ -679,9 +682,9 @@ export const Puzzle = ({

const physicalKeyboardHandler = useCallback(
(e: KeyboardEvent) => {
// Disable keyboard when paused / loading play
// Disable keyboard when loading play
if (!(state.success && state.dismissedSuccess)) {
if (loadingPlayState || !state.currentTimeWindowStart) {
if (loadingPlayState) {
return;
}
}
Expand All @@ -696,7 +699,6 @@ export const Puzzle = ({
[
dispatch,
loadingPlayState,
state.currentTimeWindowStart,
state.success,
state.dismissedSuccess,
]
Expand Down Expand Up @@ -1030,15 +1032,16 @@ export const Puzzle = ({
),
[state.autocheck, isSlate]
);


const user = props.user;
const moreMenu = useMemo(
() => (
<>
<TopBarDropDown
icon={isSlate ? <More /> : <FaEllipsisH />}
text={t`More`}
>
{() => (
{(closeDropdown) => (
<>
{!state.success ? (
<TopBarDropDownLink
Expand Down Expand Up @@ -1152,6 +1155,28 @@ export const Puzzle = ({
}}
/>
) : null}
{user !== undefined ? (
<NestedDropDown
closeParent={closeDropdown}
icon={<FaCog /> }
text={"Solver Preferences"}
>
{() =>
<ul
css={{
listStyleType: 'none',
padding: '0 10vw',
}}
>
<SolverPreferencesList
prefs={props.prefs}
userId={user.uid}
/>
</ul>
}
</NestedDropDown>
) : (''
)}
<TopBarDropDownLinkA
href="/account"
icon={<FaUser />}
Expand All @@ -1172,7 +1197,9 @@ export const Puzzle = ({
[
muted,
props.isAdmin,
props.user?.uid,
props.user,
props.prefs,
user,
puzzle,
setMuted,
state.success,
Expand Down
95 changes: 95 additions & 0 deletions app/components/SolverPreferencesList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { setDoc } from "firebase/firestore";
import { AccountPrefsFlagsT, AccountPrefsT } from "../lib/prefs";
import { logAsyncErrors } from "../lib/utils";
import { useSnackbar } from "./Snackbar";
import { getDocRef } from "../lib/firebaseWrapper";

interface PrefSettingProps {
prefs: AccountPrefsT | undefined;
userId: string;
flag: keyof AccountPrefsFlagsT;
text: string;
invert?: boolean;
}

export const PrefSetting = (props: PrefSettingProps) => {
const { showSnackbar } = useSnackbar();
const prefSet = props.prefs?.[props.flag] ?? false;
return (
<label>
<input
css={{ marginRight: '1em' }}
type="checkbox"
checked={props.invert ? !prefSet : prefSet}
onChange={logAsyncErrors(async (e) => {
await setDoc(
getDocRef('prefs', props.userId),
{
[props.flag]: props.invert ? !e.target.checked : e.target.checked,
},
{ merge: true }
).then(() => {
showSnackbar('Preferences Updated');
});
})}
onClick={(e) => {
e.stopPropagation();
}}
/>
{props.text}
</label>
);
};

export const SolverPreferencesList = ({prefs, userId}: {prefs?: AccountPrefsT, userId: string}) => {
if (!prefs) {
return null; // or some default content
}
return (
<>
<li>
<PrefSetting
prefs={prefs}
userId={userId}
flag={'advanceOnPerpendicular'}
text="Advance to next square when changing direction with arrow keys"
/>
</li>
<li>
<PrefSetting
prefs={prefs}
userId={userId}
flag={'dontSkipCompleted'}
invert={true}
text="Skip over completed squares after entering a letter"
/>
</li>
<li>
<PrefSetting
prefs={prefs}
userId={userId}
flag={'dontAdvanceWordAfterCompletion'}
invert={true}
text="Move to next clue after completing an entry"
/>
</li>
<li>
<PrefSetting
prefs={prefs}
userId={userId}
flag={'solveDownsOnly'}
text="Start puzzles in downs-only mode"
/>
</li>
<li>
<PrefSetting
prefs={prefs}
userId={userId}
flag={'dontPauseOnLostFocus'}
invert={true}
text="Automatically pause solving when page loses focus"
/>
</li>
</>
);
};
1 change: 1 addition & 0 deletions app/lib/prefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const AccountPrefsFlagsV = t.partial({
dontAdvanceWordAfterCompletion: t.boolean,
solveDownsOnly: t.boolean,
disableCommentsByDefault: t.boolean,
dontPauseOnLostFocus: t.boolean
});
export type AccountPrefsFlagsT = t.TypeOf<typeof AccountPrefsFlagsV>;

Expand Down
74 changes: 5 additions & 69 deletions app/pages/account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { PROFILE_PIC, COVER_PIC } from '../lib/style';
import {
UnsubscribeFlags,
AccountPrefsT,
AccountPrefsFlagsT,
} from '../lib/prefs';

import dynamic from 'next/dynamic';
Expand All @@ -36,6 +35,7 @@ import {
import { signOut } from 'firebase/auth';
import { BioEditor } from '../components/BioEditor';
import { logAsyncErrors } from '../lib/utils';
import { PrefSetting, SolverPreferencesList } from '../components/SolverPreferencesList';

export const getStaticProps = withStaticTranslation(() => {
return { props: {} };
Expand Down Expand Up @@ -81,40 +81,6 @@ const UnsubSetting = (props: UnsubSettingProps) => {
);
};

interface PrefSettingProps {
prefs: AccountPrefsT | undefined;
userId: string;
flag: keyof AccountPrefsFlagsT;
text: string;
invert?: boolean;
}

const PrefSetting = (props: PrefSettingProps) => {
const { showSnackbar } = useSnackbar();
const prefSet = props.prefs?.[props.flag] ?? false;
return (
<label>
<input
css={{ marginRight: '1em' }}
type="checkbox"
checked={props.invert ? !prefSet : prefSet}
onChange={logAsyncErrors(async (e) => {
await setDoc(
getDocRef('prefs', props.userId),
{
[props.flag]: props.invert ? !e.target.checked : e.target.checked,
},
{ merge: true }
).then(() => {
showSnackbar('Preferences Updated');
});
})}
/>
{props.text}
</label>
);
};

export const AccountPage = ({ user, constructorPage, prefs }: AuthProps) => {
const [settingProfilePic, setSettingProfilePic] = useState(false);
const [settingCoverPic, setSettingCoverPic] = useState(false);
Expand Down Expand Up @@ -209,40 +175,10 @@ export const AccountPage = ({ user, constructorPage, prefs }: AuthProps) => {
padding: '0 0',
}}
>
<li>
<PrefSetting
prefs={prefs}
userId={user.uid}
flag={'advanceOnPerpendicular'}
text="Advance to next square when changing direction with arrow keys"
/>
</li>
<li>
<PrefSetting
prefs={prefs}
userId={user.uid}
flag={'dontSkipCompleted'}
invert={true}
text="Skip over completed squares after entering a letter"
/>
</li>
<li>
<PrefSetting
prefs={prefs}
userId={user.uid}
flag={'dontAdvanceWordAfterCompletion'}
invert={true}
text="Move to next clue after completing an entry"
/>
</li>
<li>
<PrefSetting
prefs={prefs}
userId={user.uid}
flag={'solveDownsOnly'}
text="Start puzzles in downs-only mode"
/>
</li>
<SolverPreferencesList
prefs={prefs}
userId={user.uid}
/>
</ul>
<hr css={{ margin: '2em 0' }} />
<h2>Browser-specific Settings</h2>
Expand Down
13 changes: 13 additions & 0 deletions app/reducers/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,19 @@ export function gridInterfaceReducer<T extends GridInterfaceState>(
}
if (isKeypressAction(action)) {
const key = action.key;
// Resume on Esc, but only during midgame pause
if (isPuzzleState(state) && !state.currentTimeWindowStart) {
if (state.bankedSeconds === 0) {
return state;
}
if (key.k === KeyK.Escape) {
return {
...state,
currentTimeWindowStart: new Date().getTime()
}
}
return state;
}
if (key.k === KeyK.NumLayout || key.k === KeyK.AbcLayout) {
return { ...state, showExtraKeyLayout: !state.showExtraKeyLayout };
}
Expand Down
Loading