diff --git a/ui/package-lock.json b/ui/package-lock.json index 519a2cc7..1fad65b1 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -54,6 +54,7 @@ "react-image-size": "^2.0.2", "react-router-dom": "^6.11.2", "slugify": "^1.6.0", + "usehooks-ts": "^2.9.1", "zustand": "^3.7.2" }, "devDependencies": { @@ -10769,6 +10770,19 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/usehooks-ts": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.9.1.tgz", + "integrity": "sha512-2FAuSIGHlY+apM9FVlj8/oNhd+1y+Uwv5QNkMQz1oSfdHk4PXo1qoCw9I5M7j0vpH8CSWFJwXbVPeYDjLCx9PA==", + "engines": { + "node": ">=16.15.0", + "npm": ">=8" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "dev": true, @@ -18525,6 +18539,12 @@ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", "requires": {} }, + "usehooks-ts": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.9.1.tgz", + "integrity": "sha512-2FAuSIGHlY+apM9FVlj8/oNhd+1y+Uwv5QNkMQz1oSfdHk4PXo1qoCw9I5M7j0vpH8CSWFJwXbVPeYDjLCx9PA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "dev": true diff --git a/ui/package.json b/ui/package.json index e030eb13..43f1d114 100644 --- a/ui/package.json +++ b/ui/package.json @@ -61,9 +61,10 @@ "react-dom": "^17.0.2", "react-error-boundary": "^3.1.3", "react-hook-form": "^7.38.0", - "react-router-dom": "^6.11.2", "react-image-size": "^2.0.2", + "react-router-dom": "^6.11.2", "slugify": "^1.6.0", + "usehooks-ts": "^2.9.1", "zustand": "^3.7.2" }, "devDependencies": { diff --git a/ui/src/components/ShipCode.tsx b/ui/src/components/ShipCode.tsx new file mode 100644 index 00000000..8afd50c6 --- /dev/null +++ b/ui/src/components/ShipCode.tsx @@ -0,0 +1,23 @@ +import React, { FunctionComponent, useState } from 'react'; +import useAccessKey from '@/state/code'; +import { useCopy } from '@/logic/utils'; + +export const ShipCode: FunctionComponent = () => { + const { code } = useAccessKey(); + const [show, setShow] = useState(false); + + const { didCopy, doCopy } = useCopy(code); + + return show ? ( +
+
{code}
+ +
+ ) : ( + + ); +}; diff --git a/ui/src/logic/utils.ts b/ui/src/logic/utils.ts index 865139f8..59f0ec9d 100644 --- a/ui/src/logic/utils.ts +++ b/ui/src/logic/utils.ts @@ -10,6 +10,8 @@ import { findLast } from 'lodash'; import { hsla, parseToHsla, parseToRgba } from 'color2k'; import _ from 'lodash'; import { differenceInDays, endOfToday, format } from 'date-fns'; +import { useCopyToClipboard } from 'usehooks-ts'; +import { useCallback, useState } from 'react'; export const useMockData = import.meta.env.MODE === 'mock'; @@ -150,3 +152,28 @@ export async function asyncWithDefault( return def; } } + +export function useCopy(copied: string) { + const [didCopy, setDidCopy] = useState(false); + const [, copy] = useCopyToClipboard(); + + const doCopy = useCallback(async () => { + let success = false; + success = await copy(copied); + setDidCopy(success); + + let timeout: NodeJS.Timeout; + if (success) { + timeout = setTimeout(() => { + setDidCopy(false); + }, 2000); + } + + return () => { + setDidCopy(false); + clearTimeout(timeout); + }; + }, [copied, copy]); + + return { doCopy, didCopy }; +} diff --git a/ui/src/preferences/about-system/AboutSystem.tsx b/ui/src/preferences/about-system/AboutSystem.tsx index 6bb0181d..106cad8d 100644 --- a/ui/src/preferences/about-system/AboutSystem.tsx +++ b/ui/src/preferences/about-system/AboutSystem.tsx @@ -14,6 +14,7 @@ import { usePike, useLag } from '../../state/kiln'; import useVereState from '../../state/vere'; import { disableDefault, pluralize } from '@/logic/utils'; import { UpdatePreferences } from './UpdatePreferences'; +import { ShipCode } from '@/components/ShipCode'; function getHash(pike: Pike): string { const parts = pike.hash.split('.'); @@ -30,9 +31,9 @@ export const AboutSystem = () => { const lag = useLag(); const vere = useVereState.getState(); - const {isLatest, vereVersion, latestVereVersion, loaded} = vere; + const { isLatest, vereVersion, latestVereVersion, loaded } = vere; - const runtimeUpToDate = (!loaded || isLatest) + const runtimeUpToDate = !loaded || isLatest; return ( <> @@ -57,7 +58,10 @@ export const AboutSystem = () => {

System update failed because your runtime was out of date.

-

Your runtime version is {vereVersion}, the latest runtime version is {latestVereVersion}.

+

+ Your runtime version is {vereVersion}, the latest runtime + version is {latestVereVersion}. +

Update your runtime or contact your hosting provider.

Once your runtime is up to date, click retry below.