From a34acc38ac0be89a5b7a0b995ed658ed5a18d04f Mon Sep 17 00:00:00 2001
From: JensForstmann <19289807+JensForstmann@users.noreply.github.com>
Date: Wed, 15 Nov 2023 03:10:05 +0100
Subject: [PATCH] Fix copy to clipboard
---
frontend/src/components/GameServerCard.tsx | 73 ++--------------------
frontend/src/components/MatchCard.tsx | 25 ++------
frontend/src/utils/copyToClipboard.ts | 26 ++++++++
3 files changed, 34 insertions(+), 90 deletions(-)
create mode 100644 frontend/src/utils/copyToClipboard.ts
diff --git a/frontend/src/components/GameServerCard.tsx b/frontend/src/components/GameServerCard.tsx
index 255903e..957fdaf 100644
--- a/frontend/src/components/GameServerCard.tsx
+++ b/frontend/src/components/GameServerCard.tsx
@@ -1,12 +1,9 @@
-import { Component, createSignal } from 'solid-js';
-import { IMatchResponse, IMatchUpdateDto } from '../../../common';
+import { Component } from 'solid-js';
+import { IMatchResponse } from '../../../common';
import { SvgCopyAll, SvgOpenInNew } from '../assets/Icons';
-import { createFetcher } from '../utils/fetcher';
import { t } from '../utils/locale';
import { Card } from './Card';
-import { CardMenu } from './CardMenu';
-import { Modal } from './Modal';
-import { TextInput } from './Inputs';
+import { copyToClipboard } from '../utils/copyToClipboard';
export const GameServerCard: Component<{
match: IMatchResponse;
@@ -16,11 +13,9 @@ export const GameServerCard: Component<{
const command = () =>
(props.match.serverPassword ? `password "${props.match.serverPassword}"; ` : '') +
`connect ${ipPort()}`;
- let modalRef: HTMLDialogElement | undefined;
return (
- modalRef?.showModal()]]} />
{t('Game Server')}
@@ -28,70 +23,10 @@ export const GameServerCard: Component<{
{command()}
-
-
- modalRef?.close()} />
-
);
};
-
-const GameServerChangeForm: Component<{
- match: IMatchResponse;
- onClose: () => void;
-}> = (props) => {
- const [ip, setIp] = createSignal(props.match.gameServer.ip);
- const [port, setPort] = createSignal(props.match.gameServer.port);
- const [rconPassword, setRconPassword] = createSignal(props.match.gameServer.rconPassword);
-
- const fetcher = createFetcher(props.match.tmtSecret);
- const changeGameServer = async () => {
- await fetcher('PATCH', `/api/matches/${props.match.id}`, {
- gameServer: {
- ip: ip(),
- port: port(),
- rconPassword: rconPassword(),
- },
- } as IMatchUpdateDto);
- props.onClose();
- };
-
- return (
- <>
-
- setIp(e.currentTarget.value)}
- class="bg-base-300"
- />
- setPort(parseInt(e.currentTarget.value))}
- class="bg-base-300"
- />
- setRconPassword(e.currentTarget.value)}
- class="bg-base-300"
- />
-
-
-
- {t('save')}
-
-
- {t('cancel')}
-
- >
- );
-};
diff --git a/frontend/src/components/MatchCard.tsx b/frontend/src/components/MatchCard.tsx
index 37d8a0a..728f715 100644
--- a/frontend/src/components/MatchCard.tsx
+++ b/frontend/src/components/MatchCard.tsx
@@ -1,20 +1,15 @@
import { useNavigate, useSearchParams } from '@solidjs/router';
import { Component, Show } from 'solid-js';
-import {
- getMapDraws,
- getMapScore,
- IMatchResponse,
- IMatchUpdateDto,
- TMatchState,
-} from '../../../common';
+import { IMatchResponse, IMatchUpdateDto, getMapDraws, getMapScore } from '../../../common';
import { SvgCopyAll } from '../assets/Icons';
import { createFetcher } from '../utils/fetcher';
import { t } from '../utils/locale';
import { mustConfirm } from '../utils/mustConfirm';
import { Card } from './Card';
import { CardMenu } from './CardMenu';
-import { Modal } from './Modal';
import { TextInput } from './Inputs';
+import { Modal } from './Modal';
+import { copyToClipboard } from '../utils/copyToClipboard';
export const MatchCard: Component<{
match: IMatchResponse;
@@ -29,14 +24,6 @@ export const MatchCard: Component<{
const init = () => patchMatch({ _init: true });
const setup = () => patchMatch({ _setup: true });
const revive = () => fetcher('PATCH', `/api/matches/${props.match.id}/revive`);
- const changeState = () => {
- const response = prompt(t('enter state'), 'MATCH_MAP');
- if (response) {
- patchMatch({
- state: response as TMatchState,
- });
- }
- };
const l = window.location;
const shareLink = l.protocol + '//' + l.host + l.pathname + '?secret=' + props.match.tmtSecret;
let modalRef: HTMLDialogElement | undefined;
@@ -52,7 +39,6 @@ export const MatchCard: Component<{
[t('restart election'), mustConfirm(restartElection)],
[t('init'), init],
[t('setup'), setup],
- [t('change state'), changeState],
[t('share match with token'), () => modalRef?.showModal()],
[
t('edit match'),
@@ -94,10 +80,7 @@ export const MatchCard: Component<{
class="bg-base-300 w-full"
containerClass="w-full"
/>
- navigator.clipboard.writeText(shareLink)}
- >
+ copyToClipboard(shareLink)}>
diff --git a/frontend/src/utils/copyToClipboard.ts b/frontend/src/utils/copyToClipboard.ts
new file mode 100644
index 0000000..624bd4b
--- /dev/null
+++ b/frontend/src/utils/copyToClipboard.ts
@@ -0,0 +1,26 @@
+// from https://stackoverflow.com/a/65996386
+export const copyToClipboard = async (textToCopy: string) => {
+ // Navigator clipboard api needs a secure context (https)
+ if (navigator.clipboard && window.isSecureContext) {
+ await navigator.clipboard.writeText(textToCopy);
+ } else {
+ // Use the 'out of viewport hidden text area' trick
+ const textArea = document.createElement('textarea');
+ textArea.value = textToCopy;
+
+ // Move textarea out of the viewport so it's not visible
+ textArea.style.position = 'absolute';
+ textArea.style.left = '-999999px';
+
+ document.body.prepend(textArea);
+ textArea.select();
+
+ try {
+ document.execCommand('copy');
+ } catch (error) {
+ console.error(error);
+ } finally {
+ textArea.remove();
+ }
+ }
+};