Skip to content

Commit

Permalink
merge: prepare for release v2.4.1
Browse files Browse the repository at this point in the history
  • Loading branch information
CSharperMantle committed Jul 10, 2023
2 parents d71e104 + 2de451f commit b869539
Show file tree
Hide file tree
Showing 16 changed files with 182 additions and 87 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "periotrisjs",
"version": "2.4.0",
"version": "2.4.1",
"description": "Get familiar with the Periodic Table of Elements in a fun way, directly in your browsers.",
"scripts": {
"clean": "yarn run -B rimraf build coverage public && yarn run -B gatsby clean",
Expand Down
18 changes: 18 additions & 0 deletions src/common/TResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (C) 2021-present Rong "Mantle" Bao
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see https://www.gnu.org/licenses/ .
*/

export type TResult<T, E> = { ok: true; value?: T } | { ok: false; err?: E }
5 changes: 4 additions & 1 deletion src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ import {
import { flushed } from "./flushed"
import { formatDuration } from "./formatDuration"
import { isNil } from "./isNil"
import { getLocalizedLangName } from "./localize"
import { queryPath } from "./queryPath"
import { rearrange } from "./rearrange"
import { waitForEvent } from "./waitForEvent"

import type { ISize } from "./ISize"
import type { TPosition } from "./TPosition"
import type { TResult } from "./TResult"

export {
AutoplaySentinel,
Expand All @@ -56,9 +58,10 @@ export {
StopwatchUpdateIntervalMilliseconds,
flushed,
formatDuration,
getLocalizedLangName,
isNil,
queryPath,
rearrange,
waitForEvent,
}
export type { ISize, TPosition }
export type { ISize, TPosition, TResult }
28 changes: 28 additions & 0 deletions src/common/localize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2021-present Rong "Mantle" Bao
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see https://www.gnu.org/licenses/ .
*/

export function getLocalizedLangName(lang: string): string {
switch (lang) {
case "zh":
return "中文"
case "en":
return "English"

default:
throw new RangeError(`getLocalizedLangName: unknown language ${lang}`)
}
}
13 changes: 8 additions & 5 deletions src/components/FileFormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import TextField from "@mui/material/TextField"
import Tooltip from "@mui/material/Tooltip"
import styled from "@mui/system/styled"

import { isNil } from "../common"
import type { TResult } from "../common"

const HiddenInput = styled("input")({
display: "none",
Expand Down Expand Up @@ -79,13 +79,14 @@ interface IFileFormControlProps {
readonly helperText: string
readonly readOnly?: boolean
readonly disabled?: boolean
readonly onFileChange: (newContent: string) => boolean | void
readonly onFileChange: (newContent: string) => TResult<never, string>
readonly contentPreprocessor?: (content: string) => string
}

export const FileFormControl = (props: IFileFormControlProps) => {
const [fileContent, setFileContent] = React.useState(props.initialFileContent)
const [error, setError] = React.useState(false)
const [helperText, setHelperText] = React.useState(props.helperText)

return (
<Grid container spacing={0} direction="row" alignItems="center">
Expand All @@ -95,7 +96,7 @@ export const FileFormControl = (props: IFileFormControlProps) => {
fullWidth
value={fileContent}
label={props.label}
helperText={props.helperText}
helperText={helperText}
disabled={props.disabled ?? false}
error={error}
InputProps={{
Expand All @@ -113,9 +114,11 @@ export const FileFormControl = (props: IFileFormControlProps) => {
let content = (await head(event.target.files)?.text()) ?? ""
content = props.contentPreprocessor?.(content) ?? content
const result = props.onFileChange(content)
const isSuccessful = isNil(result) || result
setFileContent(content)
setError(!isSuccessful)
setError(!result.ok)
setHelperText(
result.ok ? props.helperText : result.err ?? props.helperText
)
}}
/>
</Grid>
Expand Down
14 changes: 10 additions & 4 deletions src/components/NumberFormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import TextField from "@mui/material/TextField"

import { isNil } from "../common"

import type { TResult } from "../common"

interface INumberFormControlProps {
readonly id: string
readonly initialContent: string
Expand All @@ -34,23 +36,25 @@ interface INumberFormControlProps {
readonly min?: number
readonly max?: number
readonly disabled?: boolean
readonly onChange: (newContent: string) => boolean | void
readonly onChange: (newContent: string) => TResult<never, string>
readonly contentPreprocessor?: (content: string) => string
}

export const NumberFormControl = (props: INumberFormControlProps) => {
const [content, setContent] = React.useState(props.initialContent)
const [error, setError] = React.useState(false)
const [helperText, setHelperText] = React.useState(props.helperText)

return (
<TextField
id={props.id}
value={content}
label={props.label}
type="number"
helperText={props.helperText}
helperText={helperText}
InputProps={{ ...props.adornments }}
inputProps={{
inputMode: "numeric",
step: props.step,
min: props.min,
max: props.max,
Expand All @@ -62,9 +66,11 @@ export const NumberFormControl = (props: INumberFormControlProps) => {
? event.target.value
: props.contentPreprocessor(event.target.value)
const result = props.onChange(content)
const isSuccessful = isNil(result) || result
setContent(content)
setError(!isSuccessful)
setError(!result.ok)
setHelperText(
result.ok ? props.helperText : result.err ?? props.helperText
)
}}
/>
)
Expand Down
53 changes: 31 additions & 22 deletions src/components/gameControlBackdrop/GameControlBackdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import { navigate } from "gatsby"
import React from "react"
import { useI18next } from "gatsby-plugin-react-i18next"

import Backdrop from "@mui/material/Backdrop"
import Button from "@mui/material/Button"
Expand All @@ -33,13 +34,15 @@ interface IGameNotStartedContentProps {
}

const GameNotStartedContent = (props: IGameNotStartedContentProps) => {
const { t } = useI18next()

return (
<>
<Typography align="center" maxWidth="md" variant="h6">
Welcome! Your task: complete the Periodic Table.
<Typography align="center" maxWidth="sm" variant="h6">
{t("typ_h_not_started_intro")}
</Typography>
<Typography align="center" maxWidth="md" variant="body1">
A/D/S/Swipe: move by one. W/Tap: rotate. Space/Long press: drop.
<Typography align="center" maxWidth="sm" variant="body1" paragraph>
{t("typ_p_not_started_intro")}
</Typography>
<Stack direction="row" spacing={5}>
<Button
Expand All @@ -49,28 +52,30 @@ const GameNotStartedContent = (props: IGameNotStartedContentProps) => {
navigate(props.homePagePath)
}}
>
HOME
{t("cap_home")}
</Button>
<Button
variant="contained"
color="primary"
onClick={props.startGameHandler}
>
START
{t("cap_start")}
</Button>
</Stack>
</>
)
}

const GamePreparingContent = () => {
const { t } = useI18next()

return (
<>
<Typography align="center" maxWidth="md" variant="h6">
Good luck!
<Typography align="center" maxWidth="sm" variant="h6">
{t("typ_h_preparing")}
</Typography>
<Typography align="center" maxWidth="md" variant="body1">
Generating new map for you. The game will start in a few seconds.
<Typography align="center" maxWidth="sm" variant="body1" paragraph>
{t("typ_p_preparing")}
</Typography>
<DelayedIndefProgress delayMs={800} />
</>
Expand All @@ -83,13 +88,15 @@ interface IGameLostContentProps {
}

const GameLostContent = (props: IGameLostContentProps) => {
const { t } = useI18next()

return (
<>
<Typography align="center" maxWidth="md" variant="h6">
Oops...
<Typography align="center" maxWidth="sm" variant="h6">
{t("typ_h_lost")}
</Typography>
<Typography align="center" maxWidth="md" variant="body1">
This does not seem to be right. Ready to give it another shot?
<Typography align="center" maxWidth="sm" variant="body1" paragraph>
{t("typ_p_lost")}
</Typography>
<Stack direction="row" spacing={5}>
<Button
Expand All @@ -99,14 +106,14 @@ const GameLostContent = (props: IGameLostContentProps) => {
navigate(props.homePagePath)
}}
>
HOME
{t("cap_home")}
</Button>
<Button
variant="contained"
color="secondary"
onClick={props.startGameHandler}
>
RETRY
{t("cap_retry")}
</Button>
</Stack>
</>
Expand All @@ -119,13 +126,15 @@ interface IGameWonContentProps {
}

const GameWonContent = (props: IGameWonContentProps) => {
const { t } = useI18next()

return (
<>
<Typography align="center" maxWidth="md" variant="h6">
Congrats!
<Typography align="center" maxWidth="sm" variant="h6">
{t("typ_h_won")}
</Typography>
<Typography align="center" maxWidth="md" variant="body1">
You finished the game! Don&apos;t hesitate to brag about it.
<Typography align="center" maxWidth="sm" variant="body1" paragraph>
{t("typ_p_won")}
</Typography>
<Stack direction="row" spacing={5}>
<Button
Expand All @@ -135,14 +144,14 @@ const GameWonContent = (props: IGameWonContentProps) => {
navigate(props.homePagePath)
}}
>
HOME
{t("cap_home")}
</Button>
<Button
variant="contained"
color="primary"
onClick={props.startGameHandler}
>
START
{t("cap_start")}
</Button>
</Stack>
</>
Expand Down
4 changes: 2 additions & 2 deletions src/i18n/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 { langs, defaultLang } from "./lang"
import { defaultLang, langs } from "./lang"

export { langs, defaultLang }
export { defaultLang, langs }
13 changes: 13 additions & 0 deletions src/i18n/locales/en/autoplay.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"typ_h_not_started_intro": "Welcome to Periotris.js AUTOPLAY",
"typ_p_not_started_intro": "In AUTOPLAY, the system takes over game input, and all user key presses and gestures are ignored. This session will not be recorded in play history. Apart from those, it is identical to Normal Mode and observes the same user settings, including map, color scheme and game speed.",
"cap_home": "HOME",
"cap_start": "START",
"cap_retry": "RETRY",
"typ_h_preparing": "Please wait",
"typ_p_preparing": "Generating new unique patterns. The demonstration will start in a few seconds.",
"typ_h_lost": "Ouch...",
"typ_p_lost": "This should not happen. Please report this to the developer.",
"typ_h_won": "Demo complete",
"typ_p_won": "This AUTOPLAY session is over. Feel free to start a new one."
}
13 changes: 13 additions & 0 deletions src/i18n/locales/en/game.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"typ_h_not_started_intro": "Welcome to Periotris.js! Your task: complete the Periodic Table",
"typ_p_not_started_intro": "A/D/S/Swipe: move by one; W/Tap: rotate; Space/Long press: drop",
"cap_home": "HOME",
"cap_start": "START",
"cap_retry": "RETRY",
"typ_h_preparing": "Good luck!",
"typ_p_preparing": "Generating new unique patterns for you. The game will start in a few seconds.",
"typ_h_lost": "Oops...",
"typ_p_lost": "This does not seem to be right. Ready to give it another shot?",
"typ_h_won": "Congrats!",
"typ_p_won": "You finished the game! Don't hesitate to brag about it."
}
13 changes: 13 additions & 0 deletions src/i18n/locales/zh/autoplay.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"typ_h_not_started_intro": "欢迎来到 Periotris.js 自动演示模式",
"typ_p_not_started_intro": "在自动演示模式中,系统将接管游戏输入,用户发出的按键与触屏手势无效。本次游玩将不计入历史。除了以上几点,自动演示模式与常规模式相同,并采用相同的用户自定义设置,包括地图、色彩主题和游戏速度。",
"cap_home": "首页",
"cap_start": "开始",
"cap_retry": "重试",
"typ_h_preparing": "请稍候",
"typ_p_preparing": "正在生成新的分割排列。自动演示将马上开始。",
"typ_h_lost": "不好……",
"typ_p_lost": "这不应该发生。请向开发者报告该事件。",
"typ_h_won": "演示完成",
"typ_p_won": "本次自动演示已结束。你可以开始一次新的自动演示。"
}
13 changes: 13 additions & 0 deletions src/i18n/locales/zh/game.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"typ_h_not_started_intro": "欢迎来到 Periotris.js !你的任务:拼出化学元素周期表",
"typ_p_not_started_intro": "A/D/S/触屏滑动:移动一格;W/触屏短按:旋转;Space/触屏长按:立即下落",
"cap_home": "首页",
"cap_start": "开始",
"cap_retry": "重试",
"typ_h_preparing": "祝你好运!",
"typ_p_preparing": "正在生成全新的分割排列。游戏将马上开始。",
"typ_h_lost": "糟糕……",
"typ_p_lost": "这种排列方式不正确。再试一次?",
"typ_h_won": "祝贺!",
"typ_p_won": "你成功完成了这次挑战,请尽情欣赏你的杰作吧!"
}
2 changes: 1 addition & 1 deletion src/pages/autoplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const query = graphql`
}
locales: allLocale(
filter: { ns: { in: ["index"] }, language: { eq: $language } }
filter: { ns: { in: ["autoplay"] }, language: { eq: $language } }
) {
edges {
node {
Expand Down
Loading

1 comment on commit b869539

@vercel
Copy link

@vercel vercel bot commented on b869539 Jul 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.