From 1102b4d7703274a7810ee7425838ea32f316710d Mon Sep 17 00:00:00 2001 From: Hazem Krimi Date: Fri, 21 Feb 2025 21:08:55 +0100 Subject: [PATCH] SSL certificates, more styles improvements and sharing WIP --- client/package-lock.json | 352 +++++++++++++++++++++++++ client/package.json | 4 +- client/src/components/Code/index.css | 7 + client/src/components/Code/index.tsx | 1 + client/src/components/Footer/index.css | 8 +- client/src/components/Stats/index.css | 17 ++ client/src/components/Stats/index.tsx | 22 +- client/src/index.css | 2 +- client/src/pages/Home/index.css | 17 +- client/src/pages/Languages/index.css | 13 +- client/src/pages/Typing/index.css | 22 +- client/src/pages/Typing/index.tsx | 2 +- client/vite.config.ts | 8 +- server/cmd/main.go | 22 +- 14 files changed, 474 insertions(+), 23 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 6749d22..acafdbb 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14,6 +14,7 @@ }, "devDependencies": { "@eslint/js": "^9.19.0", + "@types/node": "^22.13.4", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "@typescript-eslint/eslint-plugin": "^8.22.0", @@ -30,6 +31,7 @@ "typescript": "^5.7.3", "typescript-eslint": "^8.22.0", "vite": "^6.0.1", + "vite-plugin-mkcert": "^1.17.6", "vite-tsconfig-paths": "^5.1.4" } }, @@ -686,6 +688,173 @@ "node": ">= 8" } }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", + "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", + "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.1.tgz", + "integrity": "sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", + "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.2.tgz", + "integrity": "sha512-EI7kXWidkt3Xlok5uN43suK99VWqc8OaIMktY9d9+RNKl69juoTyxmLoWPIZgJYzi41qj/9zU7G/ljnNOJ5AFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.1.tgz", + "integrity": "sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^5.0.2", + "@octokit/plugin-paginate-rest": "11.3.1", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "13.2.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz", + "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^23.0.1" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -1211,6 +1380,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "22.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -1670,6 +1849,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -1686,6 +1872,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1693,6 +1891,13 @@ "dev": true, "license": "MIT" }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1814,6 +2019,19 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1967,6 +2185,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "license": "ISC" + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -2605,6 +2840,27 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2615,6 +2871,22 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3493,6 +3765,29 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3646,6 +3941,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3864,6 +4169,13 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4694,6 +5006,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true, + "license": "ISC" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4776,6 +5102,25 @@ } } }, + "node_modules/vite-plugin-mkcert": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/vite-plugin-mkcert/-/vite-plugin-mkcert-1.17.6.tgz", + "integrity": "sha512-4JR1RN0HEg/w17eRQJ/Ve2pSa6KCVQcQO6yKtIaKQCFDyd63zGfXHWpygBkvvRSpqa0GcqNKf0fjUJ0HiJQXVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/rest": "^20.1.1", + "axios": "^1.7.4", + "debug": "^4.3.6", + "picocolors": "^1.0.1" + }, + "engines": { + "node": ">=v16.7.0" + }, + "peerDependencies": { + "vite": ">=3" + } + }, "node_modules/vite-tsconfig-paths": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", @@ -4910,6 +5255,13 @@ "node": ">=0.10.0" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/client/package.json b/client/package.json index 4f33f67..e5a2c63 100644 --- a/client/package.json +++ b/client/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --host", "build": "vite build", "lint": "eslint src", "preview": "vite preview", @@ -17,6 +17,7 @@ }, "devDependencies": { "@eslint/js": "^9.19.0", + "@types/node": "^22.13.4", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "@typescript-eslint/eslint-plugin": "^8.22.0", @@ -33,6 +34,7 @@ "typescript": "^5.7.3", "typescript-eslint": "^8.22.0", "vite": "^6.0.1", + "vite-plugin-mkcert": "^1.17.6", "vite-tsconfig-paths": "^5.1.4" } } diff --git a/client/src/components/Code/index.css b/client/src/components/Code/index.css index 2c6a3d2..6ff52cd 100644 --- a/client/src/components/Code/index.css +++ b/client/src/components/Code/index.css @@ -7,6 +7,13 @@ background: var(--black); } +@media (max-width: 768px) { + .code-container { + min-width: 0px; + max-width: 80%; + } +} + .pending, .space { color: gray; } diff --git a/client/src/components/Code/index.tsx b/client/src/components/Code/index.tsx index 54a7e81..ed0aa84 100644 --- a/client/src/components/Code/index.tsx +++ b/client/src/components/Code/index.tsx @@ -32,6 +32,7 @@ function Code({ code, loaded }: CodeProps) { if (!loaded) return; if (KEYS_TO_DISABLE.includes(event.key)) return; + if (characters.length === code.length) return; if (!startedTyping) setStartedTyping(true); const char = code[characters.length]; diff --git a/client/src/components/Footer/index.css b/client/src/components/Footer/index.css index 704fa34..d1e4340 100644 --- a/client/src/components/Footer/index.css +++ b/client/src/components/Footer/index.css @@ -2,7 +2,13 @@ footer { text-align: center; width: 100%; - min-height: 5vh; + padding: 1rem 0rem; + + position: fixed; + bottom: 0; + left: 0; + + background: var(--background); } footer a { diff --git a/client/src/components/Stats/index.css b/client/src/components/Stats/index.css index 7b04af6..4cca25c 100644 --- a/client/src/components/Stats/index.css +++ b/client/src/components/Stats/index.css @@ -10,3 +10,20 @@ background: var(--black); } + +.share { + all: unset; + cursor: pointer; + + background: var(--crimson); + color: var(--white); + + border-radius: 0.75rem; + box-shadow: none; + + padding: 1rem 2rem; + font-size: 1.25rem; + font-weight: bold; + + text-decoration: none; +} diff --git a/client/src/components/Stats/index.tsx b/client/src/components/Stats/index.tsx index 780e3cb..15213ac 100644 --- a/client/src/components/Stats/index.tsx +++ b/client/src/components/Stats/index.tsx @@ -1,18 +1,33 @@ +import { useParams } from "react-router"; + import { useTypingContext } from "contexts/typing"; -import './index.css'; import { renderTimer } from "./utils"; +import './index.css'; + type StatsProps = { loaded: boolean; } function Stats({ loaded }: StatsProps) { const { + startedTyping, timer, score, accuracy, } = useTypingContext(); + const { lang } = useParams(); + + async function share() { + if (navigator.share) { + await navigator.share({ + title: 'Touch Programming', + text: `I just Practiced The ${lang} Language on Touch Programming! I did ${score} WPM with ${accuracy}% accuracy. Try it out!`, + url: window.location.href, + }); + } + } if (!loaded) return; @@ -21,6 +36,11 @@ function Stats({ loaded }: StatsProps) { {renderTimer(timer)}

WPM: {Math.round(score)}

Accuracy: {Math.round(accuracy)}%

+ {Boolean(navigator.share) && !startedTyping && score > 0 && ( + + )} ) } diff --git a/client/src/index.css b/client/src/index.css index ee2a888..1310cd7 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -70,7 +70,7 @@ body::-webkit-scrollbar-thumb { } .app { - min-height: 95vh; + min-height: 100vh; } @media (min-width: 1441px) { diff --git a/client/src/pages/Home/index.css b/client/src/pages/Home/index.css index 69028ff..0f65dbf 100644 --- a/client/src/pages/Home/index.css +++ b/client/src/pages/Home/index.css @@ -5,7 +5,8 @@ @media (max-width: 768px) { .home-container { - padding: 0rem 1rem; + padding: 0rem; + word-break: break-word; } } @@ -55,6 +56,20 @@ header .cta { text-decoration: none; } +@media (max-width: 768px) { + header h1 { + font-size: 2.5rem; + } + + header p { + font-size: 1.25rem; + } + + header .cta { + font-size: 1rem; + } +} + .features { padding: 2.5rem 1.25rem; diff --git a/client/src/pages/Languages/index.css b/client/src/pages/Languages/index.css index c790ae9..4d071ca 100644 --- a/client/src/pages/Languages/index.css +++ b/client/src/pages/Languages/index.css @@ -5,7 +5,8 @@ @media (max-width: 768px) { .languages-container { - padding: 0rem 1rem; + padding: 0rem; + word-break: break-word; } } @@ -26,11 +27,17 @@ header h1 { } header span { - color: var(--crimson); + color: var(--crimson); +} + +@media (max-width: 768px) { + header h1 { + font-size: 2.5rem; + } } .languages-wrapper { - padding: 2.5rem 1.25rem; + padding: 2.5rem 1.25rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); diff --git a/client/src/pages/Typing/index.css b/client/src/pages/Typing/index.css index 5eef99f..5c88769 100644 --- a/client/src/pages/Typing/index.css +++ b/client/src/pages/Typing/index.css @@ -1,10 +1,18 @@ .typing-container { - display: flex; - flex-direction: column; - align-items: center; - gap: 2rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; - margin-bottom: 1rem; + padding: 0rem 3rem; + margin-bottom: 1rem; +} + +@media (max-width: 768px) { + .typing-container { + padding: 0rem; + word-break: break-word; + } } header { @@ -22,9 +30,9 @@ header h1 { color: var(--crimson); - text-transform: capitalize; + text-transform: capitalize; } header span { - color: var(--crimson); + color: var(--crimson); } diff --git a/client/src/pages/Typing/index.tsx b/client/src/pages/Typing/index.tsx index 4a13427..95860b2 100644 --- a/client/src/pages/Typing/index.tsx +++ b/client/src/pages/Typing/index.tsx @@ -12,9 +12,9 @@ import './index.css'; function Typing() { const isMobile = isMobileBrowser(); - const { lang } = useParams(); const [code, setCode] = useState(''); const [loaded, setLoaded] = useState(false); + const { lang } = useParams(); useEffect(() => { if (isMobile) return; diff --git a/client/vite.config.ts b/client/vite.config.ts index c87483b..011acca 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -1,10 +1,14 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react-swc'; import vitesTSConfigPaths from 'vite-tsconfig-paths'; +import mkcert from 'vite-plugin-mkcert'; -// https://vite.dev/config/ export default defineConfig({ - plugins: [react(), vitesTSConfigPaths()], + plugins: [ + react(), + vitesTSConfigPaths(), + mkcert() + ], server: { port: 3000, }, diff --git a/server/cmd/main.go b/server/cmd/main.go index 4f13fde..f35e0cb 100644 --- a/server/cmd/main.go +++ b/server/cmd/main.go @@ -19,14 +19,26 @@ func main() { } PORT := os.Getenv("PORT") - - if len(PORT) == 0 { - PORT = "8080" - } + SSL_CERT_PATH := os.Getenv("SSL_CERT_PATH") + SSL_KEY_PATH := os.Getenv("SSL_KEY_PATH") ech := echo.New() ech.Use(middleware.CORS()) + ech.GET("/generate", handlers.Generate) - ech.Logger.Fatal(ech.Start(fmt.Sprintf(":%s", PORT))) + + if len(SSL_CERT_PATH) == 0 || len(SSL_KEY_PATH) == 0 { + if len(PORT) == 0 { + PORT = "8080" + } + + ech.Logger.Fatal(ech.Start(fmt.Sprintf(":%s", PORT))) + } + + if len(PORT) == 0 { + PORT = "4443" + } + + ech.Logger.Fatal(ech.StartTLS(fmt.Sprintf(":%s", PORT), SSL_CERT_PATH, SSL_KEY_PATH)) }