]> git.localhorst.tv Git - alttp.git/commitdiff
copy episode time on click
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 23 Sep 2025 20:53:36 +0000 (22:53 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 23 Sep 2025 20:53:36 +0000 (22:53 +0200)
package-lock.json
package.json
resources/js/components/episodes/Item.jsx
resources/js/i18n/de.js
resources/js/i18n/en.js
resources/sass/_variables.scss

index f8753493f5e381404a5e7da50ab855d08e639e3f..c3b0f9c7a0645c5fff39e2d75a71360e7f27c877 100644 (file)
@@ -50,6 +50,7 @@
                 "autoprefixer": "^10.4.2",
                 "axios": "^1.5.0",
                 "bootstrap": "^5.1.3",
+                "copy-to-clipboard": "^3.3.3",
                 "eslint": "^9.29.0",
                 "eslint-plugin-import": "^2.25.4",
                 "eslint-plugin-react": "^7.29.3",
                 "csstype": "^3.0.2"
             }
         },
-        "node_modules/@types/react-dom": {
-            "version": "18.3.7",
-            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
-            "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
-            "dev": true,
-            "license": "MIT",
-            "optional": true,
-            "peer": true,
-            "peerDependencies": {
-                "@types/react": "^18.0.0"
-            }
-        },
         "node_modules/@types/react-transition-group": {
             "version": "4.4.12",
             "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
             "dev": true,
             "license": "MIT"
         },
+        "node_modules/copy-to-clipboard": {
+            "version": "3.3.3",
+            "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+            "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "toggle-selection": "^1.0.6"
+            }
+        },
         "node_modules/core-js-compat": {
             "version": "3.43.0",
             "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz",
                 "jquery": ">=1.12.0"
             }
         },
+        "node_modules/toggle-selection": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+            "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/toidentifier": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
index 2f1696f2a3b102521a4f96d084f1a2f8b772bd9c..bf2c41121c4b64f32b1f9b1be67240f638f28edd 100644 (file)
@@ -20,6 +20,7 @@
         "autoprefixer": "^10.4.2",
         "axios": "^1.5.0",
         "bootstrap": "^5.1.3",
+        "copy-to-clipboard": "^3.3.3",
         "eslint": "^9.29.0",
         "eslint-plugin-import": "^2.25.4",
         "eslint-plugin-react": "^7.29.3",
index e8844e21fe6954cc0dfa3b0004294f58f0d3f441..941faf183b2c90becb148eea1975bde8a21804e2 100644 (file)
@@ -1,6 +1,7 @@
+import copy from 'copy-to-clipboard';
 import PropTypes from 'prop-types';
 import React from 'react';
-import { Button } from 'react-bootstrap';
+import { Button, Overlay, Tooltip } from 'react-bootstrap';
 import { useTranslation } from 'react-i18next';
 import { Link } from 'react-router-dom';
 
@@ -20,10 +21,26 @@ import { useEpisodes } from '../../hooks/episodes';
 import { useUser } from '../../hooks/user';
 
 const Item = ({ episode }) => {
+       const [showCopied, setShowCopied] = React.useState(false);
+       const copyButton = React.useRef();
+       const copyTimer = React.useRef(null);
+
        const { onAddRestream, onEditEpisode, onEditRestream } = useEpisodes();
        const { t } = useTranslation();
        const { user } = useUser();
 
+       const copyTime = React.useCallback(() => {
+               copy(`<t:${Math.floor(new Date(episode.start).getTime() / 1000)}:F>`);
+               setShowCopied(true);
+               if (copyTimer.current) {
+                       clearTimeout(copyTimer.current);
+               }
+               copyTimer.current = setTimeout(() => {
+                       setShowCopied(false);
+                       copyTimer.current = null;
+               }, 1000);
+       }, [episode, setShowCopied]);
+
        const classNames = [
                'episodes-item',
                'my-3',
@@ -54,8 +71,19 @@ const Item = ({ episode }) => {
        return <div className={classNames.join(' ')} style={style}>
                <div className="d-flex align-items-stretch">
                        <div className="episode-start me-3 fs-5 fs-md-4 text-end">
-                               {t('schedule.startTime', { date: new Date(episode.start) })}
+                               <button className="m-none p-none border-0 bg-transparent" onClick={copyTime} ref={copyButton}>
+                                       {t('schedule.startTime', { date: new Date(episode.start) })}
+                               </button>
                        </div>
+                       <Overlay
+                               placement="bottom"
+                               show={showCopied}
+                               target={copyButton}
+                       >
+                               <Tooltip>
+                                       {t('general.copied')}
+                               </Tooltip>
+                       </Overlay>
                        <div className="episode-titlebar">
                                {episode.title || episode.event ?
                                        <h3 className="episode-title fs-5 fs-md-4">
index cb5a99c486b7db51a70cf566f87ea4712432cb72..ed99b7c594e9622f2303ead92ba9980f3741971c 100644 (file)
@@ -354,6 +354,7 @@ export default {
                        anonymous: 'Anonym',
                        appDescription: 'Turniere und Tutorials für The Legend of Zelda: A Link to the Past Randomizer',
                        appName: 'ALttP',
+                       copied: 'Kopiert',
                        languages: {
                                de: 'Deutsch',
                                en: 'Englisch',
index 543b0d28e69371cd7e6cb287bc0c3747600df2f7..55ae12bb01855e1e487c929625c9716f5aaf5e20 100644 (file)
@@ -354,6 +354,7 @@ export default {
                        anonymous: 'Anonym',
                        appDescription: 'Tournaments and tutorials for The Legend of Zelda: A Link to the Past Randomizer',
                        appName: 'ALttP',
+                       copied: 'Copied',
                        languages: {
                                de: 'German',
                                en: 'English',
index db11306cad5d7f0a85361c393efbb8b17115b303..18b3f1fd5c9fbcf9bfbd54478eadf7b0bccbc77f 100644 (file)
@@ -28,3 +28,4 @@ $custom-colors: (
 // Fixes
 $form-select-indicator: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$body-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/></svg>");
 $table-color: $body-color;
+$tooltip-color: $body-color;