From c570d0bb6b2cf2ad8ed706ffe815c8a6429e5f96 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 00:15:55 +0900 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20#17=20=E4=BB=8A=E6=97=A5=E3=81=AE?= =?UTF-8?q?=E3=81=8A=E9=A1=8C=E3=81=8C=E4=BD=95=E5=9B=9E=E7=9B=AE=E3=81=A7?= =?UTF-8?q?=E3=81=82=E3=82=8B=E3=81=8B=E5=8F=96=E5=BE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #17 --- .github/workflows/cd.yaml | 6 ++++++ README.md | 2 +- src/App.tsx | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index b71096b..c8f11c7 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -40,6 +40,12 @@ jobs: BUCKET_NAME: ${{ secrets.BUCKET_NAME }} REGION: ${{ secrets.REGION }} + - name: Deploy New Folder + run: aws s3 sync dist/ s3://${BUCKET_NAME}/game_pages/MyWordleProject --exact-timestamps --region ${REGION} + env: + BUCKET_NAME: ${{ secrets.BUCKET_NAME }} + REGION: ${{ secrets.REGION }} + - name: Clear cache run: | aws cloudfront create-invalidation --distribution-id ${DISTRIBUTION_ID} --paths "/*" diff --git a/README.md b/README.md index 5438e32..61ff69d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # wordle-sample -勉強としてWORDLEを作成 +勉強としてWORDLEを作成 https://kakutory.com/game_pages/ PC表示 diff --git a/src/App.tsx b/src/App.tsx index 9c1642a..2fc6c38 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -28,6 +28,7 @@ export const App = (): JSX.Element => { // 正解単語 const [correctAnswer, setCorrectAnswer] = useState(""); + // const [todays_no, setTodaysNo] = useState(0); const getTodaysWord = async () => { const { data } = await axios.post('https://es5eaffo90.execute-api.ap-southeast-2.amazonaws.com/WORDLE', {}); @@ -35,6 +36,8 @@ export const App = (): JSX.Element => { return; } setCorrectAnswer(data.todays_word); + // setTodaysNo(data.todays_no); + console.log(data); }; // 初回レンダリング時にのみ実行 From 3e1862f51bbea77ab53cc2e8ca99be4796f00c8e Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 01:39:37 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20#16=20=E7=B5=90=E6=9E=9C=E3=82=92?= =?UTF-8?q?=E3=82=B3=E3=83=94=E3=83=BC=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 55 +++++++++++++++++++++++++-- src/components/ShareResultButton.tsx | 57 ++++++++++++++++++++++++++++ src/components/answer.tsx | 56 ++++++++++----------------- 3 files changed, 129 insertions(+), 39 deletions(-) create mode 100644 src/components/ShareResultButton.tsx diff --git a/src/App.tsx b/src/App.tsx index 2fc6c38..afb30ef 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import axios from "axios"; import { Answer } from "./components/answer"; import { Keyboard } from "./components/keyboard"; import { Notes } from "./components/notes"; +import { ShareResultButton } from "./components/ShareResultButton"; export const App = (): JSX.Element => { // 6*5の配列の初期化 @@ -16,6 +17,19 @@ export const App = (): JSX.Element => { // キーボードの文字入力により更新 const [answerList, setAnswerList] = useState(initAnswerList); + // リストの初期化 + const initMatchList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initMatchList[i] = new Array(5).fill("White"); + } + + // 回答欄のCSSリスト + // White: 判定していない + // Black: 文字も位置も無一致 + // Yellow: 文字のみ一致 + // Green: 文字も位置も一致 + const [ matchList, setMatchList ] = useState(initMatchList); + // 回答の判定を行うフラグ // キーボードのEnter入力により更新 const [judge, setJudge] = useState(false); @@ -28,7 +42,7 @@ export const App = (): JSX.Element => { // 正解単語 const [correctAnswer, setCorrectAnswer] = useState(""); - // const [todays_no, setTodaysNo] = useState(0); + const [todays_no, setTodaysNo] = useState(0); const getTodaysWord = async () => { const { data } = await axios.post('https://es5eaffo90.execute-api.ap-southeast-2.amazonaws.com/WORDLE', {}); @@ -36,7 +50,7 @@ export const App = (): JSX.Element => { return; } setCorrectAnswer(data.todays_word); - // setTodaysNo(data.todays_no); + setTodaysNo(data.todays_no); console.log(data); }; @@ -45,18 +59,53 @@ export const App = (): JSX.Element => { getTodaysWord(); }, []); + const convertAnswerMatchToEmojis = (matchList: string[][]): string => { + const emojiList = matchList.map((row) => { + return row.map((match) => { + if (match === "Black") { + return "⬛"; + } else if (match === "Yellow") { + return "🟨"; + } else if (match === "Green") { + return "🟩"; + } else { + return ""; + } + }).join(""); // 各行の絵文字を結合 + }).filter(row => row.length > 0); // 空の行を除外 + return emojiList.join("\n"); // 空でない行のみを改行で結合 + + } + + const makeResultText = () => { + const hashtag = "#MyWordleProject_" + todays_no; + const emojis = convertAnswerMatchToEmojis(matchList); + const notes = "*An unofficial Wordle learning project."; + const url = "https://kakutory.com/game_pages/MyWordleProject" + + return hashtag + "\n" + emojis + "\n\n" + notes + "\n\n" + url; + } return (
- + +
); diff --git a/src/components/ShareResultButton.tsx b/src/components/ShareResultButton.tsx new file mode 100644 index 0000000..9d6cea2 --- /dev/null +++ b/src/components/ShareResultButton.tsx @@ -0,0 +1,57 @@ +import React, { useState } from 'react'; +import Button from "@mui/material/Button"; +import Snackbar from '@mui/material/Snackbar'; +import type { SnackbarCloseReason } from '@mui/material/Snackbar'; + +type Props = { + resultText: string; + }; + +export const ShareResultButton = (props: Props): JSX.Element => { + const [openSnackbar, setOpenSnackbar] = useState(false); + + const handleCopyText = () => { + const textToCopy = props.resultText; // コピーしたいテキスト + navigator.clipboard.writeText(textToCopy).then(() => { + setOpenSnackbar(true); + }).catch(err => { + console.error('テキストのコピーに失敗しました:', err); + }); + }; + + // スナックバーを閉じる関数 + const handleCloseSnackbar = (event?: Event | React.SyntheticEvent, reason?: SnackbarCloseReason) => { + if (reason === 'clickaway') { + return; // クリックアウェイ時はスナックバーを閉じない + } + setOpenSnackbar(false); + }; + + return ( + <> + + + + + ) + +} + \ No newline at end of file diff --git a/src/components/answer.tsx b/src/components/answer.tsx index eb7e5cc..823c96f 100644 --- a/src/components/answer.tsx +++ b/src/components/answer.tsx @@ -3,6 +3,8 @@ import axios from "axios"; type Props = { answerList: string[][]; + matchList: string[][]; + setMatchList: React.Dispatch>; judge: boolean; setJudge: React.Dispatch>; correctAnswer: string; @@ -54,36 +56,16 @@ export const Answer = (props: Props) => { greenTdStyle["color"] = "White"; greenTdStyle["backgroundColor"] = "538d4e"; + const styleDict: { [key: string]: React.CSSProperties } = { + "White": whiteTdStyle, + "Black": blackTdStyle, + "Yellow": yellowTdStyle, + "Green": greenTdStyle, + }; + // ラウンド const [round, setRound] = useState(0); - // リストの初期化 - const initMatchList: string[][] = new Array(6); - for (let i = 0; i < 6; i++) { - initMatchList[i] = new Array(5).fill("White"); - } - - // 回答欄のCSSリスト - // White: 判定していない - // Black: 文字も位置も無一致 - // Yellow: 文字のみ一致 - // Green: 文字も位置も一致 - // const [ matchList, setMatchList ] = useState(initMatchList); - - // リストの初期化 - const initMatchStyleList: React.CSSProperties[][] = new Array(6); - for (let i = 0; i < 6; i++) { - initMatchStyleList[i] = new Array(5).fill(whiteTdStyle); - } - - // 回答欄のCSSリスト - // White: 判定していない - // Black: 文字も位置も無一致 - // Yellow: 文字のみ一致 - // Green: 文字も位置も一致 - const [matchStyleList, setMatchStyleList] = - useState(initMatchStyleList); - // 単語の妥当性判定 const wordValidityJudgement = async () => { const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', @@ -97,9 +79,9 @@ export const Answer = (props: Props) => { } // 単語一致判定 - const wordMatchJudgement = () => { + const wordMatchJudgement = (): string[][] => { // 一度ディープコピーする - const tmpMatchStyleList = Array.from(matchStyleList); + const tmpMatchList = Array.from(props.matchList); // 1文字ずつ判定 for (let i = 0; i < 5; i++) { @@ -107,21 +89,21 @@ export const Answer = (props: Props) => { if (props.correctAnswer.indexOf(props.answerList[round - 1][i]) !== -1) { // 位置も一致(Green) if (props.answerList[round - 1][i] === props.correctAnswer[i]) { - tmpMatchStyleList[round - 1][i] = greenTdStyle; + tmpMatchList[round - 1][i] = "Green"; } // 文字だけ一致(Yellow) else { - tmpMatchStyleList[round - 1][i] = yellowTdStyle; + tmpMatchList[round - 1][i] = "Yellow"; } } // 文字も位置も一致していない(Black) else { - tmpMatchStyleList[round - 1][i] = blackTdStyle; + tmpMatchList[round - 1][i] = "Black"; } } - return tmpMatchStyleList; + return tmpMatchList; }; // クリア判定 @@ -181,11 +163,13 @@ export const Answer = (props: Props) => { // ゲーム継続中なら if (props.gameStatus == "playing") { // 単語一致判定 - const tmpMatchStyleList = wordMatchJudgement(); + // const tmpMatchStyleList = wordMatchJudgement(); + const tmpMatchList = wordMatchJudgement(); // クリア判定 clearJudgement(); // スタイル更新 - setMatchStyleList(tmpMatchStyleList); + // setMatchStyleList(tmpMatchStyleList); + props.setMatchList(tmpMatchList); // ラウンド更新 setRound(round + 1); } @@ -204,7 +188,7 @@ export const Answer = (props: Props) => { {props.answerList.map((answer, i) => ( {answer.map((letter, j) => ( - + {letter} ))} From 3b49c5198a817cc46dcf22b2fcabf72c25dfbe9e Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 15:52:04 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20#16=20=E3=83=87=E3=82=B6=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 249 +++++++++++++++++++++++++-- package.json | 4 +- public/kakutory_gray.png | Bin 0 -> 23456 bytes src/App.tsx | 2 +- src/components/ShareResultButton.tsx | 49 ++++-- src/components/notes.tsx | 64 ++++--- 6 files changed, 313 insertions(+), 55 deletions(-) create mode 100644 public/kakutory_gray.png diff --git a/package-lock.json b/package-lock.json index 8ccc82b..bbb720e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,12 @@ "dependencies": { "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", + "@mui/icons-material": "^5.15.10", "@mui/material": "^5.15.9", "axios": "^1.6.7", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "styled-components": "^6.1.8" }, "devDependencies": { "@types/react": "^18.2.55", @@ -1187,6 +1189,31 @@ "url": "https://opencollective.com/mui-org" } }, + "node_modules/@mui/icons-material": { + "version": "5.15.10", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.10.tgz", + "integrity": "sha512-9cF8oUHZKo9oQ7EQ3pxPELaZuZVmphskU4OI6NiJNDVN7zcuvrEsuWjYo1Zh4fLiC39Nrvm30h/B51rcUjvSGA==", + "dependencies": { + "@babel/runtime": "^7.23.9" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/material": { "version": "5.15.9", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.9.tgz", @@ -1681,6 +1708,11 @@ "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, + "node_modules/@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", @@ -2064,6 +2096,14 @@ "node": ">=6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001585", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001585.tgz", @@ -2170,6 +2210,24 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3208,7 +3266,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -3369,8 +3426,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -3412,6 +3468,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3672,6 +3733,11 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3714,7 +3780,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3743,6 +3808,75 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/styled-components": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz", + "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.1", + "@emotion/unitless": "0.8.0", + "@types/stylis": "4.2.0", + "css-to-react-native": "3.2.0", + "csstype": "3.1.2", + "postcss": "8.4.31", + "shallowequal": "1.1.0", + "stylis": "4.3.1", + "tslib": "2.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, + "node_modules/styled-components/node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -3808,6 +3942,11 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4748,6 +4887,14 @@ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.9.tgz", "integrity": "sha512-CSDpVevGaxsvMkiYBZ8ztki1z/eT0mM2MqUT21eCRiMz3DU4zQw5rXG5ML/yTuJF9Z2Wv9SliIeaRAuSR/9Nig==" }, + "@mui/icons-material": { + "version": "5.15.10", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.10.tgz", + "integrity": "sha512-9cF8oUHZKo9oQ7EQ3pxPELaZuZVmphskU4OI6NiJNDVN7zcuvrEsuWjYo1Zh4fLiC39Nrvm30h/B51rcUjvSGA==", + "requires": { + "@babel/runtime": "^7.23.9" + } + }, "@mui/material": { "version": "5.15.9", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.9.tgz", @@ -5043,6 +5190,11 @@ "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, + "@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", @@ -5280,6 +5432,11 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, + "camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==" + }, "caniuse-lite": { "version": "1.0.30001585", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001585.tgz", @@ -5357,6 +5514,21 @@ "which": "^2.0.1" } }, + "css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==" + }, + "css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "requires": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -6140,8 +6312,7 @@ "nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" }, "natural-compare": { "version": "1.4.0", @@ -6251,8 +6422,7 @@ "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "picomatch": { "version": "2.3.1", @@ -6271,6 +6441,11 @@ "source-map-js": "^1.0.2" } }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6451,6 +6626,11 @@ } } }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6480,8 +6660,7 @@ "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, "strip-ansi": { "version": "6.0.1", @@ -6498,6 +6677,49 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "styled-components": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz", + "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==", + "requires": { + "@emotion/is-prop-valid": "1.2.1", + "@emotion/unitless": "0.8.0", + "@types/stylis": "4.2.0", + "css-to-react-native": "3.2.0", + "csstype": "3.1.2", + "postcss": "8.4.31", + "shallowequal": "1.1.0", + "stylis": "4.3.1", + "tslib": "2.5.0" + }, + "dependencies": { + "@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, + "csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + } + } + }, "stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -6543,6 +6765,11 @@ "dev": true, "requires": {} }, + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 23090ab..c119cb2 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,12 @@ "dependencies": { "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", + "@mui/icons-material": "^5.15.10", "@mui/material": "^5.15.9", "axios": "^1.6.7", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "styled-components": "^6.1.8" }, "devDependencies": { "@types/react": "^18.2.55", diff --git a/public/kakutory_gray.png b/public/kakutory_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..c3ec8f49ab7543a0bfe1ae50ca7da7c956591654 GIT binary patch literal 23456 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 N`yas^uKO%H_dh4K@G1ZR literal 0 HcmV?d00001 diff --git a/src/App.tsx b/src/App.tsx index afb30ef..bafe06f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -83,7 +83,7 @@ export const App = (): JSX.Element => { const notes = "*An unofficial Wordle learning project."; const url = "https://kakutory.com/game_pages/MyWordleProject" - return hashtag + "\n" + emojis + "\n\n" + notes + "\n\n" + url; + return hashtag + "\n" + emojis + "\n\n" + notes + "\n" + url; } return ( diff --git a/src/components/ShareResultButton.tsx b/src/components/ShareResultButton.tsx index 9d6cea2..6aba6df 100644 --- a/src/components/ShareResultButton.tsx +++ b/src/components/ShareResultButton.tsx @@ -2,11 +2,25 @@ import React, { useState } from 'react'; import Button from "@mui/material/Button"; import Snackbar from '@mui/material/Snackbar'; import type { SnackbarCloseReason } from '@mui/material/Snackbar'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; type Props = { resultText: string; }; +const shareStyle: React.CSSProperties = { + marginBottom: "40px", + marginTop: "100px", + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + + color: "rgb(88, 88, 88)", + + fontFamily: ["Inter", "system-ui", "Avenir", "Helvetica", "Arial", "sans-serif"].join(','), +}; + export const ShareResultButton = (props: Props): JSX.Element => { const [openSnackbar, setOpenSnackbar] = useState(false); @@ -28,29 +42,32 @@ export const ShareResultButton = (props: Props): JSX.Element => { }; return ( - <> +
+ padding: "10px 30px", + fontSize: "20px", + "&:hover": { + backgroundColor: "#585858", + }, + }} + startIcon={} // アイコンをボタンの前に追加 + > + Share + - +
) } diff --git a/src/components/notes.tsx b/src/components/notes.tsx index c82c78b..34e18e1 100644 --- a/src/components/notes.tsx +++ b/src/components/notes.tsx @@ -1,8 +1,17 @@ import React from "react"; -import Button from "@mui/material/Button"; import Divider from "@mui/material/Divider"; +import styled from "styled-components"; export const Notes = () => { + const HoverLink = styled.a` + transition: transform 0.3s ease; + display: inline-block; + + &:hover { + transform: scale(1.1); + } + `; + // 回答のCSSスタイル const noteStyle: React.CSSProperties = { marginBottom: "40px", @@ -14,37 +23,40 @@ export const Notes = () => { color: "rgb(88, 88, 88)", - fontFamily: ["Inter", "system-ui", "Avenir", "Helvetica", "Arial", "sans-serif"].join(','), + fontFamily: [ + "Inter", + "system-ui", + "Avenir", + "Helvetica", + "Arial", + "sans-serif", + ].join(","), }; return (
- - -
    -
  • New York Times社のWORDLE - をもとに作成した勉強用サイトです
  • -
  • GitHubリポジトリはこちら
  • -
- - + /> + +
    +
  • + New York Times社の + WORDLE + をもとに作成した勉強用サイトです +
  • +
  • + GitHubリポジトリは + こちら +
  • +
+ + + kakutory +
); }; From 05f87dc20c9f4ac38530024097aaf57424adbb31 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 18:20:13 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20#18=20Answer=E3=82=B3=E3=83=B3?= =?UTF-8?q?=E3=83=9D=E3=83=BC=E3=83=8D=E3=83=B3=E3=83=88=E4=B8=8A=E3=81=AE?= =?UTF-8?q?=E6=AD=A3=E8=A7=A3=E5=88=A4=E5=AE=9A=E3=83=AD=E3=82=B8=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=82=92App=E3=81=AB=E7=A7=BB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 121 ++++++++++++++++++++++++++++++++---- src/components/answer.tsx | 126 -------------------------------------- 2 files changed, 109 insertions(+), 138 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index bafe06f..8e88ef7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -34,12 +34,6 @@ export const App = (): JSX.Element => { // キーボードのEnter入力により更新 const [judge, setJudge] = useState(false); - // 現在の状態 - // playing: ゲーム中 - // success: 成功 - // fail: 失敗 - const [gameStatus, setGameStatus] = useState("playing"); - // 正解単語 const [correctAnswer, setCorrectAnswer] = useState(""); const [todays_no, setTodaysNo] = useState(0); @@ -54,6 +48,115 @@ export const App = (): JSX.Element => { console.log(data); }; + // ラウンド + const [round, setRound] = useState(0); + + // 単語の妥当性判定 + const wordValidityJudgement = async () => { + const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', + {"word": answerList[round - 1].join("")},); + console.log(data); + if (data.isValid === undefined) { + return false; + } + + return data.isValid; + } + + // 単語一致判定 + const wordMatchJudgement = (): string[][] => { + // 一度ディープコピーする + const tmpMatchList = Array.from(matchList); + + // 1文字ずつ判定 + for (let i = 0; i < 5; i++) { + // 文字が一致 + if (correctAnswer.indexOf(answerList[round - 1][i]) !== -1) { + // 位置も一致(Green) + if (answerList[round - 1][i] === correctAnswer[i]) { + tmpMatchList[round - 1][i] = "Green"; + } + + // 文字だけ一致(Yellow) + else { + tmpMatchList[round - 1][i] = "Yellow"; + } + } + + // 文字も位置も一致していない(Black) + else { + tmpMatchList[round - 1][i] = "Black"; + } + } + return tmpMatchList; + }; + + // クリア判定 + const clearJudgement = () => { + // 正解が空文字だった場合はサーバーエラー + if (correctAnswer === ""){ + alert("Server Error: Please reload the page."); + return; + } + + // ワードを抽出 + const wordList = []; + for (let j = 0; j < 5; j++) { + wordList.push(answerList[round - 1][j]); + } + const submitWord = wordList.join(""); + + if (submitWord == correctAnswer) { + alert("clear!!"); + } else if (round == 6) { + alert(correctAnswer); + } + + }; + + // Appコンポーネントのjudgeが変化した時に呼ばれる + useEffect(() => { + + const checkProcess = async () => { + + // Enterを押したら + if (judge === true) { + + // 単語の妥当性判定 + const isValid = await wordValidityJudgement(); + if (!isValid) + { + alert("データセットに存在しない単語です"); + return; + } + + // 一度フラグをおろす + setJudge(false); + } + + // フラグをおろしてからここへ + else { + // コンポーネント初期化時にここを通る + if (round == 0) { + setRound(round + 1); // ラウンドを1に + return; + } + // 単語一致判定 + const tmpMatchList = wordMatchJudgement(); + // クリア判定 + clearJudgement(); + // スタイル更新 + // setMatchStyleList(tmpMatchStyleList); + setMatchList(tmpMatchList); + // ラウンド更新 + setRound(round + 1); + } + } + + checkProcess(); + + }, [judge]); + // 初回レンダリング時にのみ実行 useEffect(() => { getTodaysWord(); @@ -91,12 +194,6 @@ export const App = (): JSX.Element => { >; - judge: boolean; - setJudge: React.Dispatch>; - correctAnswer: string; - gameStatus: string; - setGameStatus: React.Dispatch>; }; export const Answer = (props: Props) => { @@ -63,123 +54,6 @@ export const Answer = (props: Props) => { "Green": greenTdStyle, }; - // ラウンド - const [round, setRound] = useState(0); - - // 単語の妥当性判定 - const wordValidityJudgement = async () => { - const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', - {"word": props.answerList[round - 1].join("")},); - console.log(data); - if (data.isValid === undefined) { - return false; - } - - return data.isValid; - } - - // 単語一致判定 - const wordMatchJudgement = (): string[][] => { - // 一度ディープコピーする - const tmpMatchList = Array.from(props.matchList); - - // 1文字ずつ判定 - for (let i = 0; i < 5; i++) { - // 文字が一致 - if (props.correctAnswer.indexOf(props.answerList[round - 1][i]) !== -1) { - // 位置も一致(Green) - if (props.answerList[round - 1][i] === props.correctAnswer[i]) { - tmpMatchList[round - 1][i] = "Green"; - } - - // 文字だけ一致(Yellow) - else { - tmpMatchList[round - 1][i] = "Yellow"; - } - } - - // 文字も位置も一致していない(Black) - else { - tmpMatchList[round - 1][i] = "Black"; - } - } - return tmpMatchList; - }; - - // クリア判定 - const clearJudgement = () => { - // 正解が空文字だった場合、alertを出す - if (props.correctAnswer === ""){ - alert("Server Error: Please reload the page."); - return; - } - - // ワードを抽出 - const wordList = []; - for (let j = 0; j < 5; j++) { - wordList.push(props.answerList[round - 1][j]); - } - const submitWord = wordList.join(""); - - if (submitWord == props.correctAnswer) { - alert("clear!!"); - return "success"; - } else if (round == 6) { - alert(props.correctAnswer); - return "fail"; - } - - return "playing"; - }; - - // Appコンポーネントのjudgeが変化した時に呼ばれる - useEffect(() => { - - const checkProcess = async () => { - - // Enterを押したら - if (props.judge === true) { - - // 単語の妥当性判定 - const isValid = await wordValidityJudgement(); - if (!isValid) - { - alert("データセットに存在しない単語です"); - return; - } - - // 一度フラグをおろす - props.setJudge(false); - } - - // フラグをおろしてからここへ - else { - // コンポーネント初期化時にここを通る - if (round == 0) { - setRound(round + 1); // ラウンドを1に - return; - } - - // ゲーム継続中なら - if (props.gameStatus == "playing") { - // 単語一致判定 - // const tmpMatchStyleList = wordMatchJudgement(); - const tmpMatchList = wordMatchJudgement(); - // クリア判定 - clearJudgement(); - // スタイル更新 - // setMatchStyleList(tmpMatchStyleList); - props.setMatchList(tmpMatchList); - // ラウンド更新 - setRound(round + 1); - } - } - } - - checkProcess(); - - }, [props.judge]); - return ( // mapにより回答table作成
From 2970dfed90760549a2f91e7b0da3fffe585da7af Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 18:47:18 +0900 Subject: [PATCH 05/10] =?UTF-8?q?refactor:=20#18=20Enter=E3=82=92=E6=8A=BC?= =?UTF-8?q?=E3=81=97=E3=81=9F=E9=9A=9B=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?=E3=83=A2=E3=82=B8=E3=83=A5=E3=83=BC=E3=83=AB=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 126 ++++---------------------- src/components/ShareResultButton.tsx | 2 +- src/game_logics/checkClear.ts | 22 +++++ src/game_logics/checkWordMatch.ts | 27 ++++++ src/game_logics/checkWordValidity.ts | 13 +++ src/game_logics/pushedEnterProcess.ts | 43 +++++++++ 6 files changed, 125 insertions(+), 108 deletions(-) create mode 100644 src/game_logics/checkClear.ts create mode 100644 src/game_logics/checkWordMatch.ts create mode 100644 src/game_logics/checkWordValidity.ts create mode 100644 src/game_logics/pushedEnterProcess.ts diff --git a/src/App.tsx b/src/App.tsx index 8e88ef7..b94bee7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,6 +6,8 @@ import { Keyboard } from "./components/keyboard"; import { Notes } from "./components/notes"; import { ShareResultButton } from "./components/ShareResultButton"; +import { pushedEnterProcess } from "./game_logics/pushedEnterProcess"; + export const App = (): JSX.Element => { // 6*5の配列の初期化 const initAnswerList: string[][] = new Array(6); @@ -51,117 +53,27 @@ export const App = (): JSX.Element => { // ラウンド const [round, setRound] = useState(0); - // 単語の妥当性判定 - const wordValidityJudgement = async () => { - const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', - {"word": answerList[round - 1].join("")},); - console.log(data); - if (data.isValid === undefined) { - return false; - } - - return data.isValid; - } - - // 単語一致判定 - const wordMatchJudgement = (): string[][] => { - // 一度ディープコピーする - const tmpMatchList = Array.from(matchList); - - // 1文字ずつ判定 - for (let i = 0; i < 5; i++) { - // 文字が一致 - if (correctAnswer.indexOf(answerList[round - 1][i]) !== -1) { - // 位置も一致(Green) - if (answerList[round - 1][i] === correctAnswer[i]) { - tmpMatchList[round - 1][i] = "Green"; - } - - // 文字だけ一致(Yellow) - else { - tmpMatchList[round - 1][i] = "Yellow"; - } - } - - // 文字も位置も一致していない(Black) - else { - tmpMatchList[round - 1][i] = "Black"; - } - } - return tmpMatchList; - }; - - // クリア判定 - const clearJudgement = () => { - // 正解が空文字だった場合はサーバーエラー - if (correctAnswer === ""){ - alert("Server Error: Please reload the page."); - return; - } - - // ワードを抽出 - const wordList = []; - for (let j = 0; j < 5; j++) { - wordList.push(answerList[round - 1][j]); - } - const submitWord = wordList.join(""); - - if (submitWord == correctAnswer) { - alert("clear!!"); - } else if (round == 6) { - alert(correctAnswer); - } - - }; + // 初回レンダリング時にのみ実行 + useEffect(() => { + getTodaysWord(); + setRound(round + 1); + }, []); - // Appコンポーネントのjudgeが変化した時に呼ばれる + // Enterを押した際 useEffect(() => { - - const checkProcess = async () => { - - // Enterを押したら - if (judge === true) { - - // 単語の妥当性判定 - const isValid = await wordValidityJudgement(); - if (!isValid) - { - alert("データセットに存在しない単語です"); - return; - } - - // 一度フラグをおろす - setJudge(false); - } - - // フラグをおろしてからここへ - else { - // コンポーネント初期化時にここを通る - if (round == 0) { - setRound(round + 1); // ラウンドを1に - return; - } - // 単語一致判定 - const tmpMatchList = wordMatchJudgement(); - // クリア判定 - clearJudgement(); - // スタイル更新 - // setMatchStyleList(tmpMatchStyleList); - setMatchList(tmpMatchList); - // ラウンド更新 - setRound(round + 1); - } - } - - checkProcess(); - + pushedEnterProcess( + judge, + setJudge, + correctAnswer, + answerList, + matchList, + round, + setRound, + setMatchList + ); }, [judge]); - // 初回レンダリング時にのみ実行 - useEffect(() => { - getTodaysWord(); - }, []); - + const convertAnswerMatchToEmojis = (matchList: string[][]): string => { const emojiList = matchList.map((row) => { return row.map((match) => { diff --git a/src/components/ShareResultButton.tsx b/src/components/ShareResultButton.tsx index 6aba6df..41c5ca1 100644 --- a/src/components/ShareResultButton.tsx +++ b/src/components/ShareResultButton.tsx @@ -10,7 +10,7 @@ type Props = { const shareStyle: React.CSSProperties = { marginBottom: "40px", - marginTop: "100px", + marginTop: "40px", display: "flex", flexDirection: "column", justifyContent: "center", diff --git a/src/game_logics/checkClear.ts b/src/game_logics/checkClear.ts new file mode 100644 index 0000000..1653d45 --- /dev/null +++ b/src/game_logics/checkClear.ts @@ -0,0 +1,22 @@ +// クリア判定 +export const checkClear = ( correctAnswer: string, answerList: string[][], round: number ) => { + // 正解が空文字だった場合はサーバーエラー + if (correctAnswer === ""){ + alert("Server Error: Please reload the page."); + return; + } + + // ワードを抽出 + const wordList = []; + for (let j = 0; j < 5; j++) { + wordList.push(answerList[round - 1][j]); + } + const submitWord = wordList.join(""); + + if (submitWord == correctAnswer) { + alert("clear!!"); + } else if (round == 6) { + alert(correctAnswer); + } + +}; \ No newline at end of file diff --git a/src/game_logics/checkWordMatch.ts b/src/game_logics/checkWordMatch.ts new file mode 100644 index 0000000..b7adc62 --- /dev/null +++ b/src/game_logics/checkWordMatch.ts @@ -0,0 +1,27 @@ +// 単語一致判定 +export const checkWordMatch = ( correctAnswer: string, answerList: string[][], matchList: string[][], round: number ): string[][] => { + // 一度ディープコピーする + const tmpMatchList = Array.from(matchList); + + // 1文字ずつ判定 + for (let i = 0; i < 5; i++) { + // 文字が一致 + if (correctAnswer.indexOf(answerList[round - 1][i]) !== -1) { + // 位置も一致(Green) + if (answerList[round - 1][i] === correctAnswer[i]) { + tmpMatchList[round - 1][i] = "Green"; + } + + // 文字だけ一致(Yellow) + else { + tmpMatchList[round - 1][i] = "Yellow"; + } + } + + // 文字も位置も一致していない(Black) + else { + tmpMatchList[round - 1][i] = "Black"; + } + } + return tmpMatchList; + }; \ No newline at end of file diff --git a/src/game_logics/checkWordValidity.ts b/src/game_logics/checkWordValidity.ts new file mode 100644 index 0000000..349c5f1 --- /dev/null +++ b/src/game_logics/checkWordValidity.ts @@ -0,0 +1,13 @@ +import axios from "axios"; + +// 単語の妥当性判定 +export const checkWordValidity = async ( answerList: string[][], round: number ) : Promise => { + const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', + {"word": answerList[round - 1].join("")},); + console.log(data); + if (data.isValid === undefined) { + return false; + } + + return data.isValid; +} \ No newline at end of file diff --git a/src/game_logics/pushedEnterProcess.ts b/src/game_logics/pushedEnterProcess.ts new file mode 100644 index 0000000..19c6029 --- /dev/null +++ b/src/game_logics/pushedEnterProcess.ts @@ -0,0 +1,43 @@ +import { checkWordValidity } from "./checkWordValidity"; +import { checkWordMatch } from "./checkWordMatch"; +import { checkClear } from "./checkClear"; + +export const pushedEnterProcess = async ( + judge: boolean, + setJudge: React.Dispatch>, + correctAnswer: string, + answerList: string[][], + matchList: string[][], + round: number, + setRound: React.Dispatch>, + setMatchList: React.Dispatch>, + ) => { + + // Enterを押したら + if (judge === true) { + + // 単語の妥当性判定 + const isValid = await checkWordValidity(answerList, round); + if (!isValid) + { + alert("データセットに存在しない単語です"); + return; + } + + // 一度フラグをおろす + setJudge(false); + } + + // フラグをおろしてからここへ + else { + + // 単語一致判定 + const tmpMatchList = checkWordMatch(correctAnswer, answerList, matchList, round); + // クリア判定 + checkClear(correctAnswer, answerList, round); + // スタイル更新 + setMatchList(tmpMatchList); + // ラウンド更新 + setRound(round + 1); + } + } \ No newline at end of file From 04c60c479dcf82c2b743f7002902f0a4878a22a7 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 19:04:20 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat:=20#18=20Utils=E3=83=A2=E3=82=B8?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=83=AB=E3=81=B8=E5=88=86=E5=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 68 +++++++-------------------------- src/utils/getTodaysWord.ts | 14 +++++++ src/utils/makeGameResultText.ts | 27 +++++++++++++ 3 files changed, 55 insertions(+), 54 deletions(-) create mode 100644 src/utils/getTodaysWord.ts create mode 100644 src/utils/makeGameResultText.ts diff --git a/src/App.tsx b/src/App.tsx index b94bee7..a46860c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,4 @@ import { useState, useEffect } from "react"; -import axios from "axios"; import { Answer } from "./components/answer"; import { Keyboard } from "./components/keyboard"; @@ -7,29 +6,27 @@ import { Notes } from "./components/notes"; import { ShareResultButton } from "./components/ShareResultButton"; import { pushedEnterProcess } from "./game_logics/pushedEnterProcess"; +import { getTodaysWord } from "./utils/getTodaysWord"; +import { makeGameResultText } from "./utils/makeGameResultText"; export const App = (): JSX.Element => { - // 6*5の配列の初期化 + + // 回答欄の文字列 const initAnswerList: string[][] = new Array(6); for (let i = 0; i < 6; i++) { initAnswerList[i] = new Array(5).fill(""); } - - // 回答一覧 - // キーボードの文字入力により更新 const [answerList, setAnswerList] = useState(initAnswerList); - // リストの初期化 - const initMatchList: string[][] = new Array(6); - for (let i = 0; i < 6; i++) { - initMatchList[i] = new Array(5).fill("White"); - } - - // 回答欄のCSSリスト + // 回答欄のマッチ状況 // White: 判定していない // Black: 文字も位置も無一致 // Yellow: 文字のみ一致 // Green: 文字も位置も一致 + const initMatchList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initMatchList[i] = new Array(5).fill("White"); + } const [ matchList, setMatchList ] = useState(initMatchList); // 回答の判定を行うフラグ @@ -38,24 +35,15 @@ export const App = (): JSX.Element => { // 正解単語 const [correctAnswer, setCorrectAnswer] = useState(""); - const [todays_no, setTodaysNo] = useState(0); + // 今日が何回目のゲームか + const [todaysNo, setTodaysNo] = useState(0); - const getTodaysWord = async () => { - const { data } = await axios.post('https://es5eaffo90.execute-api.ap-southeast-2.amazonaws.com/WORDLE', {}); - if (data.todays_word === undefined) { - return; - } - setCorrectAnswer(data.todays_word); - setTodaysNo(data.todays_no); - console.log(data); - }; - - // ラウンド + // ラウンド(=現在の行番号+1) const [round, setRound] = useState(0); // 初回レンダリング時にのみ実行 useEffect(() => { - getTodaysWord(); + getTodaysWord(setCorrectAnswer, setTodaysNo); setRound(round + 1); }, []); @@ -73,34 +61,6 @@ export const App = (): JSX.Element => { ); }, [judge]); - - const convertAnswerMatchToEmojis = (matchList: string[][]): string => { - const emojiList = matchList.map((row) => { - return row.map((match) => { - if (match === "Black") { - return "⬛"; - } else if (match === "Yellow") { - return "🟨"; - } else if (match === "Green") { - return "🟩"; - } else { - return ""; - } - }).join(""); // 各行の絵文字を結合 - }).filter(row => row.length > 0); // 空の行を除外 - return emojiList.join("\n"); // 空でない行のみを改行で結合 - - } - - const makeResultText = () => { - const hashtag = "#MyWordleProject_" + todays_no; - const emojis = convertAnswerMatchToEmojis(matchList); - const notes = "*An unofficial Wordle learning project."; - const url = "https://kakutory.com/game_pages/MyWordleProject" - - return hashtag + "\n" + emojis + "\n\n" + notes + "\n" + url; - } - return (
{ setJudge={setJudge} />
diff --git a/src/utils/getTodaysWord.ts b/src/utils/getTodaysWord.ts new file mode 100644 index 0000000..d6778db --- /dev/null +++ b/src/utils/getTodaysWord.ts @@ -0,0 +1,14 @@ +import axios from 'axios'; + +export const getTodaysWord = async ( + setCorrectAnswer: React.Dispatch>, + setTodaysNo: React.Dispatch> + ) => { + const { data } = await axios.post('https://es5eaffo90.execute-api.ap-southeast-2.amazonaws.com/WORDLE', {}); + if (data.todays_word === undefined) { + return; + } + setCorrectAnswer(data.todays_word); + setTodaysNo(data.todays_no); + console.log(data); + }; \ No newline at end of file diff --git a/src/utils/makeGameResultText.ts b/src/utils/makeGameResultText.ts new file mode 100644 index 0000000..a63892a --- /dev/null +++ b/src/utils/makeGameResultText.ts @@ -0,0 +1,27 @@ + +export const makeGameResultText = ( matchList: string[][], todaysNo: number ) => { + const hashtag = "#MyWordleProject_" + todaysNo; + const emojis = convertAnswerMatchToEmojis(matchList); + const notes = "*An unofficial Wordle learning project."; + const url = "https://kakutory.com/game_pages/MyWordleProject" + + return hashtag + "\n" + emojis + "\n\n" + notes + "\n" + url; +} + +const convertAnswerMatchToEmojis = (matchList: string[][]): string => { + const emojiList = matchList.map((row) => { + return row.map((match) => { + if (match === "Black") { + return "⬛"; + } else if (match === "Yellow") { + return "🟨"; + } else if (match === "Green") { + return "🟩"; + } else { + return ""; + } + }).join(""); // 各行の絵文字を結合 + }).filter(row => row.length > 0); // 空の行を除外 + return emojiList.join("\n"); // 空でない行のみを改行で結合 +} + From 0926d317629c5ce33d53f49bf90b40210bd6458c Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 19:27:52 +0900 Subject: [PATCH 07/10] =?UTF-8?q?refactor:=20Keyboard=E3=82=B3=E3=83=B3?= =?UTF-8?q?=E3=83=9D=E3=83=BC=E3=83=8D=E3=83=B3=E3=83=88=E3=81=8B=E3=82=89?= =?UTF-8?q?=E3=82=82=E5=8D=98=E8=AA=9E=E5=A6=A5=E5=BD=93=E6=80=A7=E3=83=81?= =?UTF-8?q?=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92App=E3=81=B8=E7=A7=BB?= =?UTF-8?q?=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 9 ++- src/components/keyboard.tsx | 93 ++++++++++++++------------- src/game_logics/pushedEnterProcess.ts | 20 +++--- 3 files changed, 69 insertions(+), 53 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a46860c..e03b9d7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,7 +10,7 @@ import { getTodaysWord } from "./utils/getTodaysWord"; import { makeGameResultText } from "./utils/makeGameResultText"; export const App = (): JSX.Element => { - + // 回答欄の文字列 const initAnswerList: string[][] = new Array(6); for (let i = 0; i < 6; i++) { @@ -40,6 +40,7 @@ export const App = (): JSX.Element => { // ラウンド(=現在の行番号+1) const [round, setRound] = useState(0); + const [columncnt, setColumncnt] = useState(0); // 初回レンダリング時にのみ実行 useEffect(() => { @@ -54,9 +55,11 @@ export const App = (): JSX.Element => { setJudge, correctAnswer, answerList, + setAnswerList, matchList, round, setRound, + setColumncnt, setMatchList ); }, [judge]); @@ -68,6 +71,10 @@ export const App = (): JSX.Element => { matchList={matchList} /> >; + columncnt: number; + setColumncnt: React.Dispatch>; answerList: string[][]; setAnswerList: React.Dispatch>; setJudge: React.Dispatch>; }; type Props = { - rowcnt: number; - setColumncnt: React.Dispatch>; + round: number; + setRound: React.Dispatch>; columncnt: number; - setRowcnt: React.Dispatch>; + setColumncnt: React.Dispatch>; answerList: string[][], setAnswerList: React.Dispatch>; keyLayout: string[]; @@ -40,17 +44,18 @@ const KeyboardRow = (props: Props) => { return tmpList; }; - // 単語の妥当性判定 - const wordValidityJudgement = async () => { - const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', - {"word": props.answerList[props.rowcnt].join("")}); - console.log(data); - if (data.isValid === undefined) { - return false; - } + // // 単語の妥当性判定 + // const wordValidityJudgement = async () => { + // const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', + // {"word": props.answerList[props.rowcnt].join("")}); + // console.log(data); + // if (data.isValid === undefined) { + // return false; + // } - return data.isValid; - } + // return data.isValid; + // } + const handleClick = async ( event: React.MouseEvent @@ -64,29 +69,29 @@ const KeyboardRow = (props: Props) => { alert("文字数が足りません"); } - // 単語の妥当性判定 - const isValid = await wordValidityJudgement(); - if (!isValid) - { - alert("データセットに存在しない単語です"); - props.setAnswerList(prevState => - prevState.map((row, index) => - index === props.rowcnt ? Array(5).fill("") : row - ) - ); + // // 単語の妥当性判定 + // const isValid = await wordValidityJudgement(); + // if (!isValid) + // { + // alert("データセットに存在しない単語です"); + // props.setAnswerList(prevState => + // prevState.map((row, index) => + // index === props.rowcnt ? Array(5).fill("") : row + // ) + // ); - props.setColumncnt(0); + // props.setColumncnt(0); - } + // } // 5文字入力した状態 else { // フラグ送信(正解判定の依頼) props.setJudge(true); // 列数リセット - props.setColumncnt(0); + // props.setColumncnt(0); // 次の行へ移行 - props.setRowcnt((prev) => prev + 1); + // props.setRound((prev) => prev + 1); } } @@ -95,7 +100,7 @@ const KeyboardRow = (props: Props) => { // 1文字以上入力 if (props.columncnt > 0) { props.setAnswerList((prevState) => - updateAnswer(prevState, "", props.rowcnt, props.columncnt - 1) + updateAnswer(prevState, "", props.round-1, props.columncnt - 1) ); props.setColumncnt((prev) => prev - 1); } @@ -104,7 +109,7 @@ const KeyboardRow = (props: Props) => { // アルファベット入力 else if (props.columncnt < 5) { props.setAnswerList((prevState) => - updateAnswer(prevState, letter, props.rowcnt, props.columncnt) + updateAnswer(prevState, letter, props.round-1, props.columncnt) ); props.setColumncnt((prev) => prev + 1); } @@ -192,36 +197,36 @@ export const Keyboard = (props: appProps) => { "Delete", ]; - const [rowcnt, setRowcnt] = useState(0); - const [columncnt, setColumncnt] = useState(0); + // const [rowcnt, setRowcnt] = useState(0); + // const [columncnt, setColumncnt] = useState(0); return (
>, correctAnswer: string, answerList: string[][], + setAnswerList: React.Dispatch>, matchList: string[][], round: number, setRound: React.Dispatch>, + setColumncnt: React.Dispatch>, setMatchList: React.Dispatch>, ) => { // Enterを押したら if (judge === true) { - // 単語の妥当性判定 const isValid = await checkWordValidity(answerList, round); if (!isValid) { alert("データセットに存在しない単語です"); + setAnswerList(prevState => + prevState.map((row, index) => + index === round-1 ? Array(5).fill("") : row + ) + ); + setColumncnt(0); return; } - // 一度フラグをおろす - setJudge(false); - } - - // フラグをおろしてからここへ - else { - // 単語一致判定 const tmpMatchList = checkWordMatch(correctAnswer, answerList, matchList, round); // クリア判定 @@ -38,6 +38,10 @@ export const pushedEnterProcess = async ( // スタイル更新 setMatchList(tmpMatchList); // ラウンド更新 + setColumncnt(0); setRound(round + 1); + + // フラグを戻す + setJudge(false); } } \ No newline at end of file From 8d275915a194f46d86d711f4d149e06c67dc339f Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 19:45:21 +0900 Subject: [PATCH 08/10] =?UTF-8?q?refactor:=20pushedEnterProcess=E3=81=AE?= =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=82=92=E6=B8=9B=E3=82=89=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 55 ++++++++++++++++++--------- src/game_logics/pushedEnterProcess.ts | 47 +++++++---------------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e03b9d7..940bb4d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,14 +11,15 @@ import { makeGameResultText } from "./utils/makeGameResultText"; export const App = (): JSX.Element => { - // 回答欄の文字列 + // (リスト初期化) const initAnswerList: string[][] = new Array(6); for (let i = 0; i < 6; i++) { initAnswerList[i] = new Array(5).fill(""); } + /* 回答欄の文字列 */ const [answerList, setAnswerList] = useState(initAnswerList); - // 回答欄のマッチ状況 + /* 回答欄のマッチ状況 */ // White: 判定していない // Black: 文字も位置も無一致 // Yellow: 文字のみ一致 @@ -29,39 +30,55 @@ export const App = (): JSX.Element => { } const [ matchList, setMatchList ] = useState(initMatchList); - // 回答の判定を行うフラグ + /* 回答の判定を行うフラグ */ // キーボードのEnter入力により更新 const [judge, setJudge] = useState(false); - // 正解単語 + /* 正解単語 */ const [correctAnswer, setCorrectAnswer] = useState(""); - // 今日が何回目のゲームか + /* 今日が何回目のゲームか */ const [todaysNo, setTodaysNo] = useState(0); - // ラウンド(=現在の行番号+1) + /* ラウンド(=現在の行番号+1)*/ const [round, setRound] = useState(0); const [columncnt, setColumncnt] = useState(0); - // 初回レンダリング時にのみ実行 + // 初回レンダリング時 useEffect(() => { + // 本日の単語を取得 getTodaysWord(setCorrectAnswer, setTodaysNo); + // ラウンドを1に設定 setRound(round + 1); }, []); // Enterを押した際 useEffect(() => { - pushedEnterProcess( - judge, - setJudge, - correctAnswer, - answerList, - setAnswerList, - matchList, - round, - setRound, - setColumncnt, - setMatchList - ); + if (!judge) return; + + pushedEnterProcess(correctAnswer, answerList, matchList,round,setMatchList) + .then((isValid) => { + /* 単語が妥当でない場合 */ + if (!isValid) { + alert("データセットに存在しない単語です"); + // 回答欄を1行リセット + setAnswerList((prevState) => + prevState.map((row, index) => + index === round - 1 ? Array(5).fill("") : row + ) + ); + setColumncnt(0); + } + + /* 問題ない場合 */ + else { + // 次の行へ移行 + setRound(round + 1); + setColumncnt(0); + } + }); + + setJudge(false); + }, [judge]); return ( diff --git a/src/game_logics/pushedEnterProcess.ts b/src/game_logics/pushedEnterProcess.ts index 040ea34..597f7db 100644 --- a/src/game_logics/pushedEnterProcess.ts +++ b/src/game_logics/pushedEnterProcess.ts @@ -3,45 +3,24 @@ import { checkWordMatch } from "./checkWordMatch"; import { checkClear } from "./checkClear"; export const pushedEnterProcess = async ( - judge: boolean, - setJudge: React.Dispatch>, correctAnswer: string, answerList: string[][], - setAnswerList: React.Dispatch>, matchList: string[][], round: number, - setRound: React.Dispatch>, - setColumncnt: React.Dispatch>, setMatchList: React.Dispatch>, - ) => { + ) : Promise => { - // Enterを押したら - if (judge === true) { - // 単語の妥当性判定 - const isValid = await checkWordValidity(answerList, round); - if (!isValid) - { - alert("データセットに存在しない単語です"); - setAnswerList(prevState => - prevState.map((row, index) => - index === round-1 ? Array(5).fill("") : row - ) - ); - setColumncnt(0); - return; - } + // 単語の妥当性判定 + const isValid = await checkWordValidity(answerList, round); + if (!isValid) return false; - // 単語一致判定 - const tmpMatchList = checkWordMatch(correctAnswer, answerList, matchList, round); - // クリア判定 - checkClear(correctAnswer, answerList, round); - // スタイル更新 - setMatchList(tmpMatchList); - // ラウンド更新 - setColumncnt(0); - setRound(round + 1); - - // フラグを戻す - setJudge(false); - } + // 単語一致判定 + const tmpMatchList = checkWordMatch(correctAnswer, answerList, matchList, round); + // クリア判定 + checkClear(correctAnswer, answerList, round); + + // スタイル更新 + setMatchList(tmpMatchList); + + return true; } \ No newline at end of file From d59929e26bf4abc477b0ab21d133a0fff9c390b8 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 20:07:50 +0900 Subject: [PATCH 09/10] =?UTF-8?q?refactor:=20#18=20=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E5=90=8D=E5=A4=89=E6=9B=B4=E3=81=A8=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=83=87=E3=83=B3=E3=83=88=E7=B5=B1=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 143 ++++----- src/components/answer.tsx | 118 ++++---- src/components/keyboard.tsx | 418 ++++++++++++-------------- src/components/notes.tsx | 112 +++---- src/game_logics/checkClear.ts | 44 +-- src/game_logics/checkWordMatch.ts | 29 +- src/game_logics/checkWordValidity.ts | 4 +- src/game_logics/pushedEnterProcess.ts | 38 +-- src/utils/getTodaysWord.ts | 3 +- src/utils/makeGameResultText.ts | 50 +-- 10 files changed, 458 insertions(+), 501 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 940bb4d..102d2eb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,8 @@ import { useState, useEffect } from "react"; -import { Answer } from "./components/answer"; -import { Keyboard } from "./components/keyboard"; -import { Notes } from "./components/notes"; +import { Answer } from "./components/Answer"; +import { Keyboard } from "./components/Keyboard"; +import { Notes } from "./components/Notes"; import { ShareResultButton } from "./components/ShareResultButton"; import { pushedEnterProcess } from "./game_logics/pushedEnterProcess"; @@ -10,102 +10,87 @@ import { getTodaysWord } from "./utils/getTodaysWord"; import { makeGameResultText } from "./utils/makeGameResultText"; export const App = (): JSX.Element => { - - // (リスト初期化) - const initAnswerList: string[][] = new Array(6); - for (let i = 0; i < 6; i++) { - initAnswerList[i] = new Array(5).fill(""); - } - /* 回答欄の文字列 */ - const [answerList, setAnswerList] = useState(initAnswerList); - - /* 回答欄のマッチ状況 */ - // White: 判定していない - // Black: 文字も位置も無一致 - // Yellow: 文字のみ一致 - // Green: 文字も位置も一致 - const initMatchList: string[][] = new Array(6); - for (let i = 0; i < 6; i++) { - initMatchList[i] = new Array(5).fill("White"); - } - const [ matchList, setMatchList ] = useState(initMatchList); - - /* 回答の判定を行うフラグ */ - // キーボードのEnter入力により更新 - const [judge, setJudge] = useState(false); - - /* 正解単語 */ - const [correctAnswer, setCorrectAnswer] = useState(""); - /* 今日が何回目のゲームか */ - const [todaysNo, setTodaysNo] = useState(0); - - /* ラウンド(=現在の行番号+1)*/ - const [round, setRound] = useState(0); - const [columncnt, setColumncnt] = useState(0); - - // 初回レンダリング時 - useEffect(() => { - // 本日の単語を取得 - getTodaysWord(setCorrectAnswer, setTodaysNo); - // ラウンドを1に設定 - setRound(round + 1); - }, []); - - // Enterを押した際 - useEffect(() => { - if (!judge) return; - - pushedEnterProcess(correctAnswer, answerList, matchList,round,setMatchList) - .then((isValid) => { - /* 単語が妥当でない場合 */ + // (リスト初期化) + const initAnswerList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initAnswerList[i] = new Array(5).fill(""); + } + const initMatchList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initMatchList[i] = new Array(5).fill("White"); + } + + /* State */ + const [answerList, setAnswerList] = useState(initAnswerList); // 回答欄の文字列 + const [ matchList, setMatchList ] = useState(initMatchList); // 回答欄のマッチ状況(White/Black/Yellow/Grren) + const [judge, setJudge] = useState(false); // Enterを押したか + const [correctAnswer, setCorrectAnswer] = useState(""); // 今日の単語 + const [todaysNo, setTodaysNo] = useState(0); // 今日が何回目か + const [round, setRound] = useState(0); // ラウンド(現在の行番号+1) + const [columncnt, setColumncnt] = useState(0); // 現在の列番号 + + // 初回レンダリング時 + useEffect(() => { + // 今日の単語を取得 + getTodaysWord(setCorrectAnswer, setTodaysNo); + setRound(round + 1); // ラウンドを1にセット + }, []); + + // Enterを押した際 + useEffect(() => { + if (!judge) return; + + pushedEnterProcess(correctAnswer, answerList, matchList,round,setMatchList) + .then((isValid) => { + /* 単語が妥当でない場合 */ if (!isValid) { alert("データセットに存在しない単語です"); + // 回答欄を1行リセット setAnswerList((prevState) => - prevState.map((row, index) => - index === round - 1 ? Array(5).fill("") : row - ) + prevState.map((row, index) => + index === round - 1 ? Array(5).fill("") : row + ) ); - setColumncnt(0); + setColumncnt(0); // 列番号を0にリセット } /* 問題ない場合 */ else { - // 次の行へ移行 - setRound(round + 1); - setColumncnt(0); + setRound(round + 1); // 次の行へ移行 + setColumncnt(0); // 列番号を0にリセット } }); - setJudge(false); + setJudge(false); }, [judge]); return (
- - - - + + + +
); }; const appStyle: React.CSSProperties = { - margin: "0 auto", - width: "100%", - maxWidth: "600px", // 最大幅を指定する + margin: "0 auto", + width: "100%", + maxWidth: "600px", // 最大幅を指定する }; diff --git a/src/components/answer.tsx b/src/components/answer.tsx index f94a2fd..de837d6 100644 --- a/src/components/answer.tsx +++ b/src/components/answer.tsx @@ -4,72 +4,72 @@ type Props = { }; export const Answer = (props: Props) => { - // 回答のCSSスタイル - const answerStyle: React.CSSProperties = { - borderSpacing: "6px 6px", - display: "flex", - justifyContent: "center", - marginBottom: "40px", - marginTop: "100px", - }; + // 回答のCSSスタイル + const answerStyle: React.CSSProperties = { + borderSpacing: "6px 6px", + display: "flex", + justifyContent: "center", + marginBottom: "40px", + marginTop: "100px", + }; - /* td要素のCSSスタイル */ - // Whiteスタイル - const whiteTdStyle: React.CSSProperties = { - border: "2px solid rgb(217, 217, 217)", - width: "60px", - height: "70px", + /* td要素のCSSスタイル */ + // Whiteスタイル + const whiteTdStyle: React.CSSProperties = { + border: "2px solid rgb(217, 217, 217)", + width: "60px", + height: "70px", - fontSize: "30px", - fontWeight: "bold", - textAlign: "center", - lineHeight: "60px", + fontSize: "30px", + fontWeight: "bold", + textAlign: "center", + lineHeight: "60px", - // 文字色 - color: "Black", // 背景色Whiteの時のみ + // 文字色 + color: "Black", // 背景色Whiteの時のみ - // 背景色 - backgroundColor: "White", - }; + // 背景色 + backgroundColor: "White", + }; - // Blackスタイル - const blackTdStyle = { ...whiteTdStyle }; - blackTdStyle["color"] = "White"; - blackTdStyle["backgroundColor"] = "3a3a3c"; + // Blackスタイル + const blackTdStyle = { ...whiteTdStyle }; + blackTdStyle["color"] = "White"; + blackTdStyle["backgroundColor"] = "3a3a3c"; - // Yellowスタイル - const yellowTdStyle = { ...whiteTdStyle }; - yellowTdStyle["color"] = "White"; - yellowTdStyle["backgroundColor"] = "b59f3b"; + // Yellowスタイル + const yellowTdStyle = { ...whiteTdStyle }; + yellowTdStyle["color"] = "White"; + yellowTdStyle["backgroundColor"] = "b59f3b"; - // Greenスタイル - const greenTdStyle = { ...whiteTdStyle }; - greenTdStyle["color"] = "White"; - greenTdStyle["backgroundColor"] = "538d4e"; + // Greenスタイル + const greenTdStyle = { ...whiteTdStyle }; + greenTdStyle["color"] = "White"; + greenTdStyle["backgroundColor"] = "538d4e"; - const styleDict: { [key: string]: React.CSSProperties } = { - "White": whiteTdStyle, - "Black": blackTdStyle, - "Yellow": yellowTdStyle, - "Green": greenTdStyle, - }; + const styleDict: { [key: string]: React.CSSProperties } = { + "White": whiteTdStyle, + "Black": blackTdStyle, + "Yellow": yellowTdStyle, + "Green": greenTdStyle, + }; - return ( - // mapにより回答table作成 -
- - - {props.answerList.map((answer, i) => ( - - {answer.map((letter, j) => ( - - ))} - - ))} - -
- {letter} -
-
- ); + return ( + // mapにより回答table作成 +
+ + + {props.answerList.map((answer, i) => ( + + {answer.map((letter, j) => ( + + ))} + + ))} + +
+ {letter} +
+
+ ); }; diff --git a/src/components/keyboard.tsx b/src/components/keyboard.tsx index c89e4f9..77ee41b 100644 --- a/src/components/keyboard.tsx +++ b/src/components/keyboard.tsx @@ -2,236 +2,200 @@ import React, { useState, useEffect } from "react"; // import axios from "axios"; type appProps = { - round: number; - setRound: React.Dispatch>; - columncnt: number; - setColumncnt: React.Dispatch>; - answerList: string[][]; - setAnswerList: React.Dispatch>; - setJudge: React.Dispatch>; + round: number; + setRound: React.Dispatch>; + columncnt: number; + setColumncnt: React.Dispatch>; + answerList: string[][]; + setAnswerList: React.Dispatch>; + setJudge: React.Dispatch>; }; type Props = { - round: number; - setRound: React.Dispatch>; - columncnt: number; - setColumncnt: React.Dispatch>; - answerList: string[][], - setAnswerList: React.Dispatch>; - keyLayout: string[]; - setJudge: React.Dispatch>; + round: number; + setRound: React.Dispatch>; + columncnt: number; + setColumncnt: React.Dispatch>; + answerList: string[][], + setAnswerList: React.Dispatch>; + keyLayout: string[]; + setJudge: React.Dispatch>; }; const KeyboardRow = (props: Props) => { - const [windowWidth, setWindowWidth] = useState(window.innerWidth); - const updateDimensions = () => { - setWindowWidth(window.innerWidth); - }; - useEffect(() => { - window.addEventListener("resize", updateDimensions); - return () => window.removeEventListener("resize", updateDimensions); - }, []); - - const updateAnswer = ( - prevState: string[][], - letter: string, - row: number, - column: number - ) => { - const tmpList = Array.from(prevState); - tmpList[row][column] = letter; - - return tmpList; - }; - - // // 単語の妥当性判定 - // const wordValidityJudgement = async () => { - // const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', - // {"word": props.answerList[props.rowcnt].join("")}); - // console.log(data); - // if (data.isValid === undefined) { - // return false; - // } - - // return data.isValid; - // } - - - const handleClick = async ( - event: React.MouseEvent - ) => { - const letter = event.currentTarget.value; - - // Enter入力 - if (letter == "Enter") { - // 文字数不足 - if (props.columncnt < 5) { - alert("文字数が足りません"); - } - - // // 単語の妥当性判定 - // const isValid = await wordValidityJudgement(); - // if (!isValid) - // { - // alert("データセットに存在しない単語です"); - // props.setAnswerList(prevState => - // prevState.map((row, index) => - // index === props.rowcnt ? Array(5).fill("") : row - // ) - // ); - - // props.setColumncnt(0); - - // } - - // 5文字入力した状態 - else { - // フラグ送信(正解判定の依頼) - props.setJudge(true); - // 列数リセット - // props.setColumncnt(0); - // 次の行へ移行 - // props.setRound((prev) => prev + 1); - } - } - - // Delete入力 - else if (letter == "Delete") { - // 1文字以上入力 - if (props.columncnt > 0) { - props.setAnswerList((prevState) => - updateAnswer(prevState, "", props.round-1, props.columncnt - 1) - ); - props.setColumncnt((prev) => prev - 1); - } - } - - // アルファベット入力 - else if (props.columncnt < 5) { - props.setAnswerList((prevState) => - updateAnswer(prevState, letter, props.round-1, props.columncnt) - ); - props.setColumncnt((prev) => prev + 1); - } - }; - - // キーボードのCSSスタイル - const keyboardStyle: React.CSSProperties = { - borderSpacing: windowWidth < 600 ? "3px 3px" : "6px 6px", - display: "flex", - justifyContent: "center", - }; - - // ボタンのCSSスタイル - const buttonStyle: React.CSSProperties = { - backgroundColor: "rgb(217, 217, 217)", - borderRadius: "4px", - border: "none", - width: windowWidth < 600 ? "30px" : "45px", - height: windowWidth < 600 ? "45px" : "60px", - fontSize: windowWidth < 600 ? "10px" : "13px", - fontWeight: "bold", - cursor: "pointer", - }; - - // EnterとDeleteのCSSスタイル - // buttonStyleとの差分のみ記述 - const enterAndDeleteButtonStyle: React.CSSProperties = { - ...buttonStyle, - width: windowWidth < 600 ? "50px" : "70px", - }; - - return ( - // mapによりキーボードtable作成 - - - - {props.keyLayout.map((key, i) => ( - - ))} - - -
- {/* EnterとDeleteのときのみstyleを変更 */} - -
- ); -}; - -export const Keyboard = (props: appProps) => { - const upKeyLayout: string[] = [ - "Q", - "W", - "E", - "R", - "T", - "Y", - "U", - "I", - "O", - "P", - ]; - const middleKeyLayout: string[] = [ - "A", - "S", - "D", - "F", - "G", - "H", - "J", - "K", - "L", - ]; - const downKeyLayout: string[] = [ - "Enter", - "Z", - "X", - "C", - "V", - "B", - "N", - "M", - "Delete", - ]; - - // const [rowcnt, setRowcnt] = useState(0); - // const [columncnt, setColumncnt] = useState(0); - - return ( -
- - - -
- ); -}; + const [windowWidth, setWindowWidth] = useState(window.innerWidth); + const updateDimensions = () => { + setWindowWidth(window.innerWidth); + }; + useEffect(() => { + window.addEventListener("resize", updateDimensions); + return () => window.removeEventListener("resize", updateDimensions); + }, []); + + const updateAnswer = ( + prevState: string[][], + letter: string, + row: number, + column: number + ) => { + const tmpList = Array.from(prevState); + tmpList[row][column] = letter; + + return tmpList; + }; + + const handleClick = async ( + event: React.MouseEvent + ) => { + const letter = event.currentTarget.value; + + // Enter入力 + if (letter == "Enter") { + // 文字数不足 + if (props.columncnt < 5) { + alert("文字数が足りません"); + } + + // 5文字入力した場合はフラグ送信(判定依頼) + else { + props.setJudge(true); + } + } + + // Delete入力 + else if (letter == "Delete") { + // 1文字以上入力 + if (props.columncnt > 0) { + props.setAnswerList((prevState) => + updateAnswer(prevState, "", props.round-1, props.columncnt - 1) + ); + props.setColumncnt((prev) => prev - 1); + } + } + + // アルファベット入力 + else if (props.columncnt < 5) { + props.setAnswerList((prevState) => + updateAnswer(prevState, letter, props.round-1, props.columncnt) + ); + props.setColumncnt((prev) => prev + 1); + } + }; + + // キーボードのCSSスタイル + const keyboardStyle: React.CSSProperties = { + borderSpacing: windowWidth < 600 ? "3px 3px" : "6px 6px", + display: "flex", + justifyContent: "center", + }; + + // ボタンのCSSスタイル + const buttonStyle: React.CSSProperties = { + backgroundColor: "rgb(217, 217, 217)", + borderRadius: "4px", + border: "none", + width: windowWidth < 600 ? "30px" : "45px", + height: windowWidth < 600 ? "45px" : "60px", + fontSize: windowWidth < 600 ? "10px" : "13px", + fontWeight: "bold", + cursor: "pointer", + }; + + // EnterとDeleteのCSSスタイル + // buttonStyleとの差分のみ記述 + const enterAndDeleteButtonStyle: React.CSSProperties = { + ...buttonStyle, + width: windowWidth < 600 ? "50px" : "70px", + }; + + return ( + // mapによりキーボードtable作成 + + + + {props.keyLayout.map((key, i) => ( + + ))} + + +
+ {/* EnterとDeleteのときのみstyleを変更 */} + +
+ ); + }; + + export const Keyboard = (props: appProps) => { + const upKeyLayout: string[] = [ + "Q", + "W", + "E", + "R", + "T", + "Y", + "U", + "I", + "O", + "P", + ]; + const middleKeyLayout: string[] = [ + "A", + "S", + "D", + "F", + "G", + "H", + "J", + "K", + "L", + ]; + const downKeyLayout: string[] = [ + "Enter", + "Z", + "X", + "C", + "V", + "B", + "N", + "M", + "Delete", + ]; + + return ( +
+ + + +
+ ); + }; diff --git a/src/components/notes.tsx b/src/components/notes.tsx index 34e18e1..60e2663 100644 --- a/src/components/notes.tsx +++ b/src/components/notes.tsx @@ -3,60 +3,60 @@ import Divider from "@mui/material/Divider"; import styled from "styled-components"; export const Notes = () => { - const HoverLink = styled.a` - transition: transform 0.3s ease; - display: inline-block; - - &:hover { - transform: scale(1.1); - } - `; - - // 回答のCSSスタイル - const noteStyle: React.CSSProperties = { - marginBottom: "40px", - marginTop: "100px", - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - - color: "rgb(88, 88, 88)", - - fontFamily: [ - "Inter", - "system-ui", - "Avenir", - "Helvetica", - "Arial", - "sans-serif", - ].join(","), - }; - - return ( -
- - -
    -
  • - New York Times社の - WORDLE - をもとに作成した勉強用サイトです -
  • -
  • - GitHubリポジトリは - こちら -
  • -
- - - kakutory - -
- ); + const HoverLink = styled.a` + transition: transform 0.3s ease; + display: inline-block; + + &:hover { + transform: scale(1.1); + } + `; + + // 回答のCSSスタイル + const noteStyle: React.CSSProperties = { + marginBottom: "40px", + marginTop: "100px", + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + + color: "rgb(88, 88, 88)", + + fontFamily: [ + "Inter", + "system-ui", + "Avenir", + "Helvetica", + "Arial", + "sans-serif", + ].join(","), + }; + + return ( +
+ + +
    +
  • + New York Times社の + WORDLE + をもとに作成した勉強用サイトです +
  • +
  • + GitHubリポジトリは + こちら +
  • +
+ + + kakutory + +
+ ); }; diff --git a/src/game_logics/checkClear.ts b/src/game_logics/checkClear.ts index 1653d45..ea500ca 100644 --- a/src/game_logics/checkClear.ts +++ b/src/game_logics/checkClear.ts @@ -1,22 +1,28 @@ // クリア判定 -export const checkClear = ( correctAnswer: string, answerList: string[][], round: number ) => { - // 正解が空文字だった場合はサーバーエラー - if (correctAnswer === ""){ - alert("Server Error: Please reload the page."); - return; - } +export const checkClear = ( + correctAnswer: string, + answerList: string[][], + round: number + ) => { + // 正解が空文字だった場合はサーバーエラー + if (correctAnswer === "") { + alert("Server Error: Please reload the page."); + return; + } - // ワードを抽出 - const wordList = []; - for (let j = 0; j < 5; j++) { - wordList.push(answerList[round - 1][j]); - } - const submitWord = wordList.join(""); + // ワードを抽出 + const wordList = []; + for (let j = 0; j < 5; j++) { + wordList.push(answerList[round - 1][j]); + } + const submitWord = wordList.join(""); - if (submitWord == correctAnswer) { - alert("clear!!"); - } else if (round == 6) { - alert(correctAnswer); - } - -}; \ No newline at end of file + // クリア + if (submitWord == correctAnswer) { + alert("clear!!"); + } + // 6回目で不正解だった場合 + else if (round == 6) { + alert(correctAnswer); + } +}; diff --git a/src/game_logics/checkWordMatch.ts b/src/game_logics/checkWordMatch.ts index b7adc62..d515570 100644 --- a/src/game_logics/checkWordMatch.ts +++ b/src/game_logics/checkWordMatch.ts @@ -5,23 +5,24 @@ export const checkWordMatch = ( correctAnswer: string, answerList: string[][], m // 1文字ずつ判定 for (let i = 0; i < 5; i++) { - // 文字が一致 - if (correctAnswer.indexOf(answerList[round - 1][i]) !== -1) { - // 位置も一致(Green) - if (answerList[round - 1][i] === correctAnswer[i]) { - tmpMatchList[round - 1][i] = "Green"; - } + // 文字が一致 + if (correctAnswer.indexOf(answerList[round - 1][i]) !== -1) { + // 位置も一致(Green) + if (answerList[round - 1][i] === correctAnswer[i]) { + tmpMatchList[round - 1][i] = "Green"; + } - // 文字だけ一致(Yellow) - else { - tmpMatchList[round - 1][i] = "Yellow"; - } - } + // 文字だけ一致(Yellow) + else { + tmpMatchList[round - 1][i] = "Yellow"; + } + } // 文字も位置も一致していない(Black) - else { - tmpMatchList[round - 1][i] = "Black"; - } + else { + tmpMatchList[round - 1][i] = "Black"; + } } + return tmpMatchList; }; \ No newline at end of file diff --git a/src/game_logics/checkWordValidity.ts b/src/game_logics/checkWordValidity.ts index 349c5f1..e929ca5 100644 --- a/src/game_logics/checkWordValidity.ts +++ b/src/game_logics/checkWordValidity.ts @@ -3,10 +3,10 @@ import axios from "axios"; // 単語の妥当性判定 export const checkWordValidity = async ( answerList: string[][], round: number ) : Promise => { const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', - {"word": answerList[round - 1].join("")},); + {"word": answerList[round - 1].join("")},); console.log(data); if (data.isValid === undefined) { - return false; + return false; } return data.isValid; diff --git a/src/game_logics/pushedEnterProcess.ts b/src/game_logics/pushedEnterProcess.ts index 597f7db..62e054b 100644 --- a/src/game_logics/pushedEnterProcess.ts +++ b/src/game_logics/pushedEnterProcess.ts @@ -3,24 +3,24 @@ import { checkWordMatch } from "./checkWordMatch"; import { checkClear } from "./checkClear"; export const pushedEnterProcess = async ( - correctAnswer: string, - answerList: string[][], - matchList: string[][], - round: number, - setMatchList: React.Dispatch>, - ) : Promise => { + correctAnswer: string, + answerList: string[][], + matchList: string[][], + round: number, + setMatchList: React.Dispatch>, + ) : Promise => { - // 単語の妥当性判定 - const isValid = await checkWordValidity(answerList, round); - if (!isValid) return false; + // 単語の妥当性判定 + const isValid = await checkWordValidity(answerList, round); + if (!isValid) return false; - // 単語一致判定 - const tmpMatchList = checkWordMatch(correctAnswer, answerList, matchList, round); - // クリア判定 - checkClear(correctAnswer, answerList, round); - - // スタイル更新 - setMatchList(tmpMatchList); - - return true; - } \ No newline at end of file + // 単語一致判定 + const tmpMatchList = checkWordMatch(correctAnswer, answerList, matchList, round); + // クリア判定 + checkClear(correctAnswer, answerList, round); + + // スタイル更新 + setMatchList(tmpMatchList); + + return true; +} \ No newline at end of file diff --git a/src/utils/getTodaysWord.ts b/src/utils/getTodaysWord.ts index d6778db..577933e 100644 --- a/src/utils/getTodaysWord.ts +++ b/src/utils/getTodaysWord.ts @@ -6,9 +6,8 @@ export const getTodaysWord = async ( ) => { const { data } = await axios.post('https://es5eaffo90.execute-api.ap-southeast-2.amazonaws.com/WORDLE', {}); if (data.todays_word === undefined) { - return; + return; } setCorrectAnswer(data.todays_word); setTodaysNo(data.todays_no); - console.log(data); }; \ No newline at end of file diff --git a/src/utils/makeGameResultText.ts b/src/utils/makeGameResultText.ts index a63892a..9e9ec56 100644 --- a/src/utils/makeGameResultText.ts +++ b/src/utils/makeGameResultText.ts @@ -1,27 +1,29 @@ +export const makeGameResultText = (matchList: string[][], todaysNo: number) => { + const hashtag = "#MyWordleProject_" + todaysNo; + const emojis = convertAnswerMatchToEmojis(matchList); + const notes = "*An unofficial Wordle learning project."; + const url = "https://kakutory.com/game_pages/MyWordleProject"; -export const makeGameResultText = ( matchList: string[][], todaysNo: number ) => { - const hashtag = "#MyWordleProject_" + todaysNo; - const emojis = convertAnswerMatchToEmojis(matchList); - const notes = "*An unofficial Wordle learning project."; - const url = "https://kakutory.com/game_pages/MyWordleProject" - - return hashtag + "\n" + emojis + "\n\n" + notes + "\n" + url; -} + return hashtag + "\n" + emojis + "\n\n" + notes + "\n" + url; +}; const convertAnswerMatchToEmojis = (matchList: string[][]): string => { - const emojiList = matchList.map((row) => { - return row.map((match) => { - if (match === "Black") { - return "⬛"; - } else if (match === "Yellow") { - return "🟨"; - } else if (match === "Green") { - return "🟩"; - } else { - return ""; - } - }).join(""); // 各行の絵文字を結合 - }).filter(row => row.length > 0); // 空の行を除外 - return emojiList.join("\n"); // 空でない行のみを改行で結合 -} - + const emojiList = matchList + .map((row) => { + return row + .map((match) => { + if (match === "Black") { + return "⬛"; + } else if (match === "Yellow") { + return "🟨"; + } else if (match === "Green") { + return "🟩"; + } else { + return ""; + } + }) + .join(""); // 各行の絵文字を結合 + }) + .filter((row) => row.length > 0); // 空の行を除外 + return emojiList.join("\n"); // 空でない行のみを改行で結合 +}; From c3bc0b3d4f8ad69ff31d03ab52a3583ffef25591 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Tue, 20 Feb 2024 20:16:51 +0900 Subject: [PATCH 10/10] =?UTF-8?q?feat:=20#16=20=E3=82=B9=E3=83=8A=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=83=90=E3=83=BC=E3=81=AE=E4=BD=8D=E7=BD=AE=E8=AA=BF?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ShareResultButton.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/ShareResultButton.tsx b/src/components/ShareResultButton.tsx index 41c5ca1..a1c23f1 100644 --- a/src/components/ShareResultButton.tsx +++ b/src/components/ShareResultButton.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useRef } from 'react'; import Button from "@mui/material/Button"; import Snackbar from '@mui/material/Snackbar'; import type { SnackbarCloseReason } from '@mui/material/Snackbar'; @@ -21,8 +21,21 @@ const shareStyle: React.CSSProperties = { fontFamily: ["Inter", "system-ui", "Avenir", "Helvetica", "Arial", "sans-serif"].join(','), }; + + export const ShareResultButton = (props: Props): JSX.Element => { const [openSnackbar, setOpenSnackbar] = useState(false); + const buttonRef = useRef(null); + + // スナックバーのスタイルを動的に計算 + const snackbarStyle = buttonRef.current + ? { + position: 'absolute', + top: `${buttonRef.current.offsetTop - 60}px`, // ボタンの上に配置 + left: '50%', // ビューポートの中央 + transform: 'translate(-50%, 0)', // 要素の幅の半分だけ左にずらす + } + : {}; const handleCopyText = () => { const textToCopy = props.resultText; // コピーしたいテキスト @@ -44,6 +57,7 @@ export const ShareResultButton = (props: Props): JSX.Element => { return (