mirror of
https://github.com/hazemKrimi/touch-programming.git
synced 2026-05-01 18:20:26 +00:00
Share on twitter
This commit is contained in:
@@ -3,6 +3,21 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Master touch typing with real code snippets from your favorite programming languages, powered by AI."
|
||||
>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="touch typing, programming, code snippets, AI, artificial intelligence, wpm test, typing test, typing practice"
|
||||
>
|
||||
<meta property="og:title" content="Touch Programming">
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Master touch typing with real code snippets from your favorite programming languages, powered by AI."
|
||||
>
|
||||
<meta property="og:url" content=".">
|
||||
<link rel="canonical" href=".">
|
||||
<link rel="icon" sizes="192x192" href="android-chrome-192x192.png">
|
||||
<link rel="icon" sizes="512x512" href="android-chrome-512x512.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
|
||||
|
||||
Generated
+923
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@
|
||||
"typescript-eslint": "^8.22.0",
|
||||
"vite": "^6.0.1",
|
||||
"vite-plugin-mkcert": "^1.17.6",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path fill="#dddddd" d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 402 B |
@@ -11,7 +11,7 @@
|
||||
background: var(--black);
|
||||
}
|
||||
|
||||
.share {
|
||||
.stats-container button {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -21,9 +21,17 @@
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: none;
|
||||
|
||||
padding: 1rem 2rem;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
|
||||
text-decoration: none;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stats-container svg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ import { useParams } from "react-router";
|
||||
|
||||
import { useTypingContext } from "contexts/typing";
|
||||
|
||||
import { renderTimer } from "./utils";
|
||||
import { renderTimer, shareOnTwitter } from "./utils";
|
||||
|
||||
import TwitterIcon from 'assets/icons/x.svg?react';
|
||||
|
||||
import './index.css';
|
||||
|
||||
@@ -19,26 +21,16 @@ function Stats({ loaded }: StatsProps) {
|
||||
} = 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;
|
||||
if (!loaded || !lang) return;
|
||||
|
||||
return (
|
||||
<div className='stats-container'>
|
||||
{renderTimer(timer)}
|
||||
<p>WPM: {Math.round(score)}</p>
|
||||
<p>Accuracy: {Math.round(accuracy)}%</p>
|
||||
{Boolean(navigator.share) && !startedTyping && score > 0 && (
|
||||
<button className='share' onClick={share}>
|
||||
Share
|
||||
{!startedTyping && score > 0 && (
|
||||
<button onClick={() => shareOnTwitter(lang, score, accuracy)}>
|
||||
<TwitterIcon />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -6,3 +6,13 @@ export function renderTimer(timer: number) {
|
||||
<p>Time: {leftValue < 10 && 0}{leftValue}:{rightValue < 10 && 0}{rightValue}</p>
|
||||
);
|
||||
}
|
||||
|
||||
export function shareOnTwitter(lang: string, score: number, accuracy: number) {
|
||||
const tweetText = `I just practiced the ${lang} language on Touch Programming! I did ${Math.round(score)} WPM with ${Math.round(accuracy)}% accuracy. Try it out!`;
|
||||
const url = window.location.href;
|
||||
const encodedTweetText = encodeURIComponent(tweetText);
|
||||
const encodedUrl = encodeURIComponent(url);
|
||||
|
||||
window.open(`https://x.com/intent/tweet?text=${encodedTweetText}&url=${encodedUrl}`);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
@@ -1,4 +1,5 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite-plugin-svgr/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_API_URL: string;
|
||||
|
||||
@@ -2,12 +2,14 @@ import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react-swc';
|
||||
import vitesTSConfigPaths from 'vite-tsconfig-paths';
|
||||
import mkcert from 'vite-plugin-mkcert';
|
||||
import svgr from "vite-plugin-svgr";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
vitesTSConfigPaths(),
|
||||
mkcert()
|
||||
mkcert(),
|
||||
svgr(),
|
||||
],
|
||||
server: {
|
||||
port: 3000,
|
||||
|
||||
Reference in New Issue
Block a user