Skip to content

Commit

Permalink
feat: add customizable gestures
Browse files Browse the repository at this point in the history
  • Loading branch information
CSharperMantle committed Jun 22, 2023
1 parent 7a1f14b commit 052b106
Show file tree
Hide file tree
Showing 14 changed files with 278 additions and 170 deletions.
3 changes: 3 additions & 0 deletions src/common/PeriotrisConst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ export const HistoryLocalStorageKey = "history"
export const SettingsLocalStorageKey = "settings"
export const DefaultBorderThickness = 1
export const DefaultConcurrency = 0
export const DefaultSwipeThreshold = 500
export const DefaultSwipeDeltaX = 15
export const DefaultSwipeDeltaY = 15
8 changes: 8 additions & 0 deletions src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@

import {
DefaultBorderThickness,
DefaultConcurrency,
DefaultGameUpdateIntervalMilliseconds,
DefaultSwipeDeltaX,
DefaultSwipeDeltaY,
DefaultSwipeThreshold,
HistoryLocalStorageKey,
SettingsLocalStorageKey,
StopwatchUpdateIntervalMilliseconds,
Expand All @@ -37,6 +41,10 @@ export {
HistoryLocalStorageKey,
SettingsLocalStorageKey,
DefaultBorderThickness,
DefaultSwipeThreshold,
DefaultConcurrency,
DefaultSwipeDeltaX,
DefaultSwipeDeltaY,
rearrange,
flushed,
waitForEvent,
Expand Down
55 changes: 20 additions & 35 deletions src/components/FileFormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import TextField from "@mui/material/TextField"
import Tooltip from "@mui/material/Tooltip"
import styled from "@mui/system/styled"

import { isNil } from "../common"

const HiddenInput = styled("input")({
display: "none",
})
Expand Down Expand Up @@ -87,58 +85,45 @@ interface IFileFormControlProps {
readonly contentPreprocessor?: (content: string) => string
}

export const FileFormControl = ({
id,
initialFileContent,
accept,
tooltipCaption,
label,
helperText,
readOnly,
onFileChange,
contentPreprocessor,
}: IFileFormControlProps): React.ReactElement => {
const [fileContent, setFileContent] = React.useState(initialFileContent)

const onFileChangeHandler = async (
event: React.ChangeEvent<HTMLInputElement>
) => {
let content = (await head(event.target.files)?.text()) ?? ""
content = contentPreprocessor?.(content) ?? content
const result = onFileChange(content)
if (isNil(result) || result) {
setFileContent(content)
}
}
export const FileFormControl = (
props: IFileFormControlProps
): React.ReactElement => {
const [fileContent, setFileContent] = React.useState(props.initialFileContent)

return (
<FormControl>
<Grid container spacing={0} direction="row" alignItems="center">
<Grid item xs={10}>
<TextField
id={`${id}-string`}
id={`${props.id}-string`}
fullWidth
value={fileContent}
label={label}
label={props.label}
InputProps={{
readOnly: readOnly ?? false,
readOnly: props.readOnly ?? false,
}}
aria-describedby={`${id}-string-helper-text`}
aria-describedby={`${props.id}-string-helper-text`}
/>
</Grid>
<Grid item xs={2}>
<Container maxWidth="sm">
<FileUploadButton
id={`${id}-upload`}
accept={accept}
tooltipCaption={tooltipCaption}
onFileChange={onFileChangeHandler}
id={`${props.id}-upload`}
accept={props.accept}
tooltipCaption={props.tooltipCaption}
onFileChange={async (event) => {
let content = (await head(event.target.files)?.text()) ?? ""
content = props.contentPreprocessor?.(content) ?? content
if (props.onFileChange(content)) {
setFileContent(content)
}
}}
/>
</Container>
</Grid>
</Grid>
<FormHelperText id={`${id}-string-helper-text`}>
{helperText}
<FormHelperText id={`${props.id}-string-helper-text`}>
{props.helperText}
</FormHelperText>
</FormControl>
)
Expand Down
12 changes: 6 additions & 6 deletions src/components/NumberFormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ export const NumberFormControl = (
min: props.min,
max: props.max,
}}
onChange={(event) => {
const content = !isNil(props.contentPreprocessor)
? props.contentPreprocessor(event.target.value)
: event.target.value
const result = props.onChange(content)
if (isNil(result) || result) {
onChange={async (event) => {
const content = isNil(props.contentPreprocessor)
? event.target.value
: props.contentPreprocessor(event.target.value)

if (props.onChange(content)) {
setContent(content)
}
}}
Expand Down
2 changes: 1 addition & 1 deletion src/components/gameControlBackdrop/GameControlBackdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export const GameControlBackdrop = (props: IGameStatusBackdropProps) => {
content = <GameLostContent startGameHandler={props.startGameHandler} />
break
default:
throw new Error("Unknown game state")
throw new Error("GameControlBackdrop: unknown game state")
}

return (
Expand Down
6 changes: 3 additions & 3 deletions src/customization/CustomizationFacade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export class CustomizationFacade {

public readonly settings = Settings.fromLocalStorage()

public clear(flush = true): void {
this.history.clear(flush)
this.settings.clear(flush)
public clear(): void {
this.history.clear()
this.settings.clear()
}
}

Expand Down
19 changes: 9 additions & 10 deletions src/customization/history/History.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

import { HistoryLocalStorageKey, isNil } from "../../common"
import { retrieve, store } from "../../localstorage"
import { remove, retrieve, store } from "../../localstorage"

import type { ILocalStorageSerializable } from "../ILocalStorageSerializable"

Expand Down Expand Up @@ -48,17 +48,16 @@ export class History implements ILocalStorageSerializable {
return isFastestRecordUpdated
}

public clear(flush = true): void {
this._fastestRecord = null
this._records = []
if (flush) {
this.toLocalStorage()
}
public clear(): void {
Object.defineProperties(
this,
Object.getOwnPropertyDescriptors(History.Empty)
)
remove(HistoryLocalStorageKey)
}

private constructor() {
this.clear(false)
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {}

public static fromLocalStorage(): History {
const result = retrieve<History>(HistoryLocalStorageKey)
Expand Down
58 changes: 42 additions & 16 deletions src/customization/settings/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@
* along with this program. If not, see https://www.gnu.org/licenses/ .
*/

import defaultColorScheme from "../../json/DefaultColorScheme.json"
import defaultMap from "../../json/DefaultMap.json"

import {
DefaultBorderThickness,
DefaultConcurrency,
DefaultGameUpdateIntervalMilliseconds,
DefaultSwipeDeltaX,
DefaultSwipeDeltaY,
DefaultSwipeThreshold,
isNil,
SettingsLocalStorageKey,
} from "../../common"
import { DefaultConcurrency } from "../../common/PeriotrisConst"
import defaultColorScheme from "../../json/DefaultColorScheme.json"
import defaultMap from "../../json/DefaultMap.json"
import { retrieve, store } from "../../localstorage"
import { remove, retrieve, store } from "../../localstorage"

import type { IColorScheme } from "../color_scheme"
import type { ILocalStorageSerializable } from "../ILocalStorageSerializable"
Expand Down Expand Up @@ -91,22 +95,44 @@ export class Settings implements ILocalStorageSerializable {
this.toLocalStorage()
}

public clear(flush = true): void {
this._showGridLine = undefined
this._gameUpdateIntervalMilliseconds = undefined
this._gameMap = undefined
this._colorScheme = undefined
this._borderThickness = undefined
this._concurrency = undefined
if (flush) {
this.toLocalStorage()
}
private _swipeThreshold: number | undefined
public get swipeThreshold(): number {
return this._swipeThreshold ?? DefaultSwipeThreshold
}
public set swipeThreshold(v) {
this._swipeThreshold = v
this.toLocalStorage()
}

private constructor() {
this.clear(false)
private _swipeDeltaX: number | undefined
public get swipeDeltaX(): number {
return this._swipeDeltaX ?? DefaultSwipeDeltaX
}
public set swipeDeltaX(v) {
this._swipeDeltaX = v
this.toLocalStorage()
}

private _swipeDeltaY: number | undefined
public get swipeDeltaY(): number {
return this._swipeDeltaY ?? DefaultSwipeDeltaY
}
public set swipeDeltaY(v) {
this._swipeDeltaY = v
this.toLocalStorage()
}

public clear(): void {
Object.defineProperties(
this,
Object.getOwnPropertyDescriptors(Settings.Default)
)
remove(SettingsLocalStorageKey)
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {}

public static fromLocalStorage(): Settings {
const result = retrieve<Settings>(SettingsLocalStorageKey)

Expand Down
10 changes: 7 additions & 3 deletions src/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
"cap_open_button": "Open file...",
"msg_color_scheme_invalid": "Invalid color scheme file. Please check your file format.",
"msg_game_map_invalid": "Invalid game map file. Please check your file format.",
"msg_update_interval_invalid": "Invalid tick interval value.",
"msg_border_thickness_invalid": "Invalid border thickness value.",
"msg_concurrency_invalid": "Invalid worker count value.",
"msg_int_expected": "Invalid value. Expected an integer.",
"msg_clear_all": "All user data purged. Refresh the app to take effect.",
"msg_export_settings_succ": "Settings exported. Check browser downloads for exported file.",
"typ_category_appearance": "Appearance",
Expand All @@ -23,6 +21,12 @@
"typ_game_map_helper": "Controls the in-game periodic table layout in JSON.",
"lbl_update_interval": "Update interval",
"typ_update_interval_helper": "Controls the interval between two game ticks.",
"lbl_swipe_threshold": "Swipe duration threshold",
"typ_swipe_threshold_helper": "Controls the minimal duration above which a dragging is considered a swipe.",
"lbl_swipe_delta_x": "Swipe horizontal offset threshold",
"typ_swipe_delta_x_helper": "Controls the minimal horizontal offset above which a dragging is considered a swipe.",
"lbl_swipe_delta_y": "Swipe vertical offset threshold",
"typ_swipe_delta_y_helper": "Controls the minimal vertical offset above which a dragging is considered a swipe.",
"typ_category_misc": "Misc",
"lbl_concurrency": "Concurrency",
"typ_concurrency_helper": "Controls the maximum number of Web Workers allowed to spawn. Set to '0' to decide based on hardware. A higher value often means quicker game preparation, but a value too large may render the app unresponsive.",
Expand Down
6 changes: 6 additions & 0 deletions src/i18n/locales/zh/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
"typ_game_map_helper": "控制游戏的周期表布局。布局文件使用 JSON 格式。",
"lbl_update_interval": "刷新时间间隔",
"typ_update_interval_helper": "控制两个游戏刻之间的时间长度。",
"lbl_swipe_threshold": "滑动时长阈值",
"typ_swipe_threshold_helper": "控制将移动手势判定为滑动时,该手势需要持续的最短时长。",
"lbl_swipe_delta_x": "滑动水平位移阈值",
"typ_swipe_delta_x_helper": "控制将移动手势判定为滑动时,该手势在水平方向上需要产生的最小位移。",
"lbl_swipe_delta_y": "滑动垂直位移阈值",
"typ_swipe_delta_y_helper": "控制将移动手势判定为滑动时,该手势在垂直方向上需要产生的最小位移。",
"typ_category_misc": "杂项",
"lbl_concurrency": "并行性",
"typ_concurrency_helper": "控制游戏最多可使用的 Web Worker 数量。设为“ 0 ”以根据硬件配置自动选择。更高的数值通常意味着游戏准备能够更快速地进行,但该值过高可能导致应用无响应。",
Expand Down
6 changes: 6 additions & 0 deletions src/localstorage/LocalStorageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,9 @@ export function retrieve<T = unknown>(key: string): T | null {
return null
}
}

export function remove(key: string): void {
if (!isBrowser) return

window.localStorage.removeItem(key)
}
4 changes: 2 additions & 2 deletions src/localstorage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
* along with this program. If not, see https://www.gnu.org/licenses/ .
*/

import { retrieve, store } from "./LocalStorageManager"
import { remove, retrieve, store } from "./LocalStorageManager"

export { store, retrieve }
export { remove, store, retrieve }
9 changes: 6 additions & 3 deletions src/pages/game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Box from "@mui/material/Box"
import { flushed } from "../common"
import { BlocksGrid, CommonHead, GameControlBackdrop } from "../components"
import { GameViewModel } from "../viewmodel"
import { customizationFacade } from "../customization"

const App = (): React.ReactElement => {
const viewModel = new GameViewModel()
Expand Down Expand Up @@ -57,9 +58,11 @@ const App = (): React.ReactElement => {
{
filterTaps: true,
swipe: {
/* TODO: Add these to customization settings! */
distance: [15, 15],
duration: 500,
distance: [
customizationFacade.settings.swipeDeltaX,
customizationFacade.settings.swipeDeltaY,
],
duration: customizationFacade.settings.swipeThreshold,
},
}
)
Expand Down
Loading

0 comments on commit 052b106

Please sign in to comment.