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
116 changes: 115 additions & 1 deletion app/__tests__/puzzleReducer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

import { getMockedPuzzle } from '../lib/getMockedPuzzle';
import { markdownToHast } from '../lib/markdown/markdown';
import { BLOCK, CheatUnit, puzzleFromDB } from '../lib/types';
import { BLOCK, CheatUnit, Direction, KeyK, puzzleFromDB } from '../lib/types';
import { allSolutions } from '../lib/utils';
import { addClues, fromCells } from '../lib/viewableGrid';
import { KeypressAction } from '../reducers/commonActions';
import { PuzzleState, puzzleReducer } from '../reducers/puzzleReducer';
import {
CheatablePuzzleState,
cheat,
Expand Down Expand Up @@ -136,6 +138,7 @@ test('check without alt', () => {
verifiedCells: new Set(),
revealedCells: new Set(),
wrongCells: new Set(),
draftCells: new Set(),
grid,
solutions: allSolutions(dbpuz.g, [])[0],
cellsIterationCount: [],
Expand Down Expand Up @@ -184,6 +187,7 @@ test('check with alt', () => {
verifiedCells: new Set(),
revealedCells: new Set(),
wrongCells: new Set(),
draftCells: new Set(),
grid,
solutions: allSolutions(dbpuz.g, [[[0, 'M']]])[0],
cellsIterationCount: [],
Expand All @@ -204,3 +208,113 @@ test('check with alt', () => {
}
`);
});

function getPuzzleState(): PuzzleState {
const dbpuz = getMockedPuzzle();
const fromDB = puzzleFromDB(dbpuz, 'puzId');
const ourGrid = fromDB.grid.map((s): string => (s === BLOCK ? BLOCK : ' '));
const grid = addClues(
fromCells({
mapper: (e) => e,
width: fromDB.size.cols,
height: fromDB.size.rows,
cells: ourGrid,
allowBlockEditing: false,
cellStyles: new Map(),
vBars: new Set(fromDB.vBars),
hBars: new Set(fromDB.hBars),
hidden: new Set(fromDB.hidden),
}),
fromDB.clues,
(c: string) => markdownToHast({ text: c, inline: true })
);
return {
type: 'puzzle',
wasEntryClick: false,
active: { col: 0, row: 0, dir: Direction.Across },
grid,
showExtraKeyLayout: false,
answers: fromDB.grid,
alternateSolutions: fromDB.alternateSolutions,
solutions: allSolutions(fromDB.grid, fromDB.alternateSolutions)[0],
verifiedCells: new Set(),
wrongCells: new Set(),
revealedCells: new Set(),
draftCells: new Set(),
draftMode: false,
downsOnly: false,
isEnteringRebus: false,
rebusValue: '',
success: false,
ranSuccessEffects: false,
filled: false,
autocheck: false,
dismissedKeepTrying: false,
dismissedSuccess: false,
moderating: false,
showingEmbedOverlay: false,
displaySeconds: 0,
bankedSeconds: 0,
ranMetaSubmitEffects: false,
currentTimeWindowStart: Date.now(),
didCheat: false,
clueView: false,
cellsUpdatedAt: fromDB.grid.map(() => 0),
cellsIterationCount: fromDB.grid.map(() => 0),
cellsEverMarkedWrong: new Set(),
loadedPlayState: true,
isEditable(cellIndex) {
return !this.verifiedCells.has(cellIndex) && !this.success;
},
};
}

function press(key: KeypressAction['key']): KeypressAction {
return { type: 'KEYPRESS', key };
}

test('draft mode toggles with period or draft key', () => {
const state = getPuzzleState();
const withDot = puzzleReducer(state, press({ k: KeyK.Dot }));
expect(withDot.draftMode).toBe(true);
const toggledOff = puzzleReducer(withDot, press({ k: KeyK.Draft }));
expect(toggledOff.draftMode).toBe(false);
});

test('letters entered in draft mode are marked draft until confirmed', () => {
let state = getPuzzleState();
state = puzzleReducer(state, press({ k: KeyK.Draft }));
state = puzzleReducer(state, press({ k: KeyK.AllowedCharacter, c: 'A' }));
expect(state.grid.cells[0]).toBe('A');
expect(state.draftCells.has(0)).toBe(true);

state = puzzleReducer(state, press({ k: KeyK.Draft }));
state = puzzleReducer(state, press({ k: KeyK.ArrowLeft }));
state = puzzleReducer(state, press({ k: KeyK.AllowedCharacter, c: 'B' }));
expect(state.grid.cells[0]).toBe('B');
expect(state.draftCells.has(0)).toBe(false);
});

test('backspace removes draft status', () => {
let state = getPuzzleState();
state = puzzleReducer(state, press({ k: KeyK.Draft }));
state = puzzleReducer(state, press({ k: KeyK.AllowedCharacter, c: 'A' }));
expect(state.draftCells.has(0)).toBe(true);

state = puzzleReducer(state, press({ k: KeyK.ArrowLeft }));
state = puzzleReducer(state, press({ k: KeyK.Backspace }));
expect(state.grid.cells[0]?.trim()).toBe('');
expect(state.draftCells.has(0)).toBe(false);
});

test('revealing a draft cell confirms it', () => {
let state = getPuzzleState();
state = puzzleReducer(state, press({ k: KeyK.Draft }));
state = puzzleReducer(state, press({ k: KeyK.AllowedCharacter, c: 'A' }));
expect(state.draftCells.has(0)).toBe(true);

state = puzzleReducer(state, press({ k: KeyK.ArrowLeft }));
state = cheat(state, CheatUnit.Square, true);
expect(state.draftCells.has(0)).toBe(false);
expect(state.verifiedCells.has(0)).toBe(true);
});
9 changes: 9 additions & 0 deletions app/components/Cell.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
--current-cell-bg: var(--cell-bg);
--current-cell-text: var(--text);
--current-cell-verified: var(--verified-on-bg);
--current-cell-draft: var(--draft-on-bg);

position: relative;
float: left;
Expand Down Expand Up @@ -75,24 +76,28 @@
--current-cell-bg: var(--primary);
--current-cell-text: var(--onprimary);
--current-cell-verified: var(--verified-on-primary);
--current-cell-draft: var(--draft-on-primary);
}

.cellContainerSelected {
--current-cell-bg: var(--selected-cell);
--current-cell-text: var(--on-selected-cell);
--current-cell-verified: var(--verified-on-selected-cell);
--current-cell-draft: var(--draft-on-selected-cell);
}

.cellContainerEntryCell {
--current-cell-bg: var(--lighter);
--current-cell-text: var(--on-lighter);
--current-cell-verified: var(--verified-on-lighter);
--current-cell-draft: var(--draft-on-lighter);
}

.cellContainerRefed {
--current-cell-bg: var(--secondary);
--current-cell-text: var(--on-secondary);
--current-cell-verified: var(--verified-on-secondary);
--current-cell-draft: var(--draft-on-secondary);
}

.cq {
Expand Down Expand Up @@ -186,6 +191,10 @@
color: var(--current-cell-text);
}

.contentsDraft {
color: var(--current-cell-draft);
}

.borderBottom {
border-bottom: 1px solid black;
}
Expand Down
9 changes: 6 additions & 3 deletions app/components/Cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ interface CellProps {
onMouseEnter: (pos: Position) => void;
isVerified: boolean | undefined;
isWrong: boolean | undefined;
isDraft: boolean | undefined;
wasRevealed: boolean | undefined;
cellColor?: number;
isOpposite: boolean;
Expand Down Expand Up @@ -179,9 +180,11 @@ export const Cell = memo(function Cell(props: CellProps) {
styles.contents,
props.isVerified
? styles.contentsVerified
: filledValue
? styles.contentsFilled
: null
: props.isDraft && filledValue
? styles.contentsDraft
: filledValue
? styles.contentsFilled
: null
)}
>
{props.isWrong ? (
Expand Down
4 changes: 4 additions & 0 deletions app/components/ClueList.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@
text-align: center;
}

.clueModeCell[data-draft='true'] {
color: var(--draft-on-bg);
}

.clueModeCell[data-active-cell='true'] {
border: 1px solid var(--black);
}
Expand Down
13 changes: 12 additions & 1 deletion app/components/ClueList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Root } from 'hast';
import { Dispatch, MouseEvent, RefObject, memo, useRef } from 'react';
import { EntryBase, GridBase, valAt } from '../lib/gridBase.js';
import { EntryBase, GridBase, cellIndex, valAt } from '../lib/gridBase.js';
import { Position, directionString } from '../lib/types.js';
import { CluedEntry } from '../lib/viewableGrid.js';
import { PuzzleAction } from '../reducers/commonActions.js';
Expand All @@ -25,6 +25,8 @@ interface ClueListItemProps {
grid: GridBase<EntryBase>;
scrollToCross: boolean;
listRef: RefObject<HTMLDivElement | null>;
draftCells?: Set<number>;
draftMode?: boolean;
}

const ClueListItem = memo(function ClueListItem({
Expand Down Expand Up @@ -97,6 +99,11 @@ const ClueListItem = memo(function ClueListItem({
<span
key={a.col + '-' + a.row}
data-active-cell={isActiveCell}
data-draft={
props.isEnteringRebus && isActiveCell
? Boolean(props.draftMode)
: props.draftCells?.has(cellIndex(props.grid, a))
}
className={styles.clueModeCell}
>
{props.isEnteringRebus && isActiveCell
Expand Down Expand Up @@ -132,6 +139,8 @@ interface ClueListProps {
rebusValue?: string;
grid: GridBase<EntryBase>;
scrollToCross: boolean;
draftCells?: Set<number>;
draftMode?: boolean;
}

export const ClueList = (props: ClueListProps): React.JSX.Element => {
Expand Down Expand Up @@ -161,6 +170,8 @@ export const ClueList = (props: ClueListProps): React.JSX.Element => {
active={
props.showEntries && (isActive || isCross) ? props.active : null
}
draftCells={props.draftCells}
draftMode={props.draftMode}
/>
);
});
Expand Down
7 changes: 7 additions & 0 deletions app/components/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ interface GridViewProps {
dispatch: Dispatch<PuzzleAction>;
revealedCells?: Set<number>;
verifiedCells?: Set<number>;
draftCells?: Set<number>;
draftMode?: boolean;
isEnteringRebus?: boolean;
rebusValue?: string;
wrongCells?: Set<number>;
Expand Down Expand Up @@ -176,6 +178,11 @@ export const GridView = ({
isBlock={cellValue === BLOCK}
isOpposite={isOpposite}
isVerified={props.verifiedCells?.has(idx) || showAsVerified}
isDraft={
props.isEnteringRebus && isActive
? Boolean(props.draftMode)
: props.draftCells?.has(idx)
}
isWrong={props.wrongCells?.has(idx)}
wasRevealed={props.revealedCells?.has(idx)}
styles={Array.from(grid.cellStyles.entries())
Expand Down
5 changes: 5 additions & 0 deletions app/components/Keyboard.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
background: var(--key-bg-click);
}

.key[data-active='true'] {
color: var(--onprimary);
background: var(--primary);
}

/* stylelint-disable-next-line media-query-no-invalid */
@media ($small-and-up) {
.key[data-only-on-tablet='true'] {
Expand Down
27 changes: 27 additions & 0 deletions app/components/Keyboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ interface KeyProps {
onlyOnTablet?: boolean;
notOnTablet?: boolean;
className?: string;
active?: boolean;
touched: RefObject<boolean>;
}
const Key = ({ touched, ...props }: KeyProps) => {
Expand All @@ -59,6 +60,7 @@ const Key = ({ touched, ...props }: KeyProps) => {
data-large-size={props.largeSize}
data-only-on-tablet={props.onlyOnTablet}
data-not-on-tablet={props.notOnTablet}
data-active={props.active}
className={clsx(styles.key, props.className)}
onClick={(e) => {
e.preventDefault();
Expand All @@ -84,6 +86,8 @@ interface KeyboardProps {
showExtraKeyLayout: boolean;
includeBlockKey: boolean;
toggleKeyboard: boolean;
showDraftKey?: boolean;
draftMode?: boolean;
}
export const Keyboard = memo(function Keyboard({
muted,
Expand Down Expand Up @@ -164,6 +168,17 @@ export const Keyboard = memo(function Keyboard({
display="ABC"
onKeypress={keypress}
/>
{props.showDraftKey ? (
<Key
touched={touched}
keyStroke="{draft}"
display="Draft"
active={props.draftMode}
onKeypress={keypress}
/>
) : (
''
)}
<Key
touched={touched}
keyStroke="{rebus}"
Expand Down Expand Up @@ -285,6 +300,18 @@ export const Keyboard = memo(function Keyboard({
display="Rebus"
onKeypress={keypress}
/>
{props.showDraftKey ? (
<Key
touched={touched}
onlyOnTablet
keyStroke="{draft}"
display="Draft"
active={props.draftMode}
onKeypress={keypress}
/>
) : (
''
)}
<Key
touched={touched}
onlyOnTablet
Expand Down
Loading
Loading