Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 5776a61
Author: 老J <hmmmbiubiubiu@gmail.com>
Date:   Fri May 31 13:43:31 2024 +0800

    v1.3.0: Add quick input textarea.

commit fc9be20
Author: 老J <hmmmbiubiubiu@gmail.com>
Date:   Fri May 31 13:39:49 2024 +0800

    Add quick input textarea.

commit af100c3
Author: 老J <hmmmbiubiubiu@gmail.com>
Date:   Fri May 31 08:18:02 2024 +0800

    Add quick input textarea.

commit dffeadf
Author: 老J <hmmmbiubiubiu@gmail.com>
Date:   Thu May 30 08:24:47 2024 +0800

    Add quick input textarea.

commit 14829c9
Author: 老J <hmmmbiubiubiu@gmail.com>
Date:   Wed May 29 13:38:31 2024 +0800

    Add quick input textarea.

commit d4602f7
Author: 老J <hmmmbiubiubiu@gmail.com>
Date:   Tue May 28 20:10:33 2024 +0800

    Add quick input area.
  • Loading branch information
llaoj committed May 31, 2024
1 parent 46d0454 commit de841d2
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 47 deletions.
Binary file modified docs/screenshot-chrome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshot-ios-safari.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions frontend/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Inter } from "next/font/google";
import "@/app/globals.css";
import { ReactNode } from "react";
import { getTranslations } from "next-intl/server";
import type { Viewport } from "next";

export async function generateMetadata({
params: { locale },
Expand All @@ -15,6 +16,13 @@ export async function generateMetadata({
};
}

export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
maximumScale: 1,
userScalable: false,
};

const inter = Inter({ subsets: ["latin"] });

export default function RootLayout({
Expand Down
52 changes: 52 additions & 0 deletions frontend/components/quick-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/solid";
import { useState, RefObject } from "react";
import { useTranslations } from "next-intl";
import { isDesktop } from "react-device-detect";

export default function QuickInput({
textareaRef,
}: {
textareaRef: RefObject<HTMLTextAreaElement>;
}) {
const [checked, setChecked] = useState<boolean>(false);
const t = useTranslations("SyncClipboard");

const onChange = async () => {
setChecked((current) => !current);
if (textareaRef.current) {
textareaRef.current.value = "";
!checked && textareaRef.current.focus();
}
};

return (
<div className="pb-4">
<label>
<input
className="peer/showTextarea absolute scale-0"
type="checkbox"
onChange={onChange}
/>
<div className="flex justify-between items-center text-sm select-none opacity-70 peer-checked/showTextarea:pb-2 cursor-pointer">
<div>
<span className="font-bold">{t("quickInput.title")}:</span>
<span>{" " + t("quickInput.subTitle")}</span>
{isDesktop && (
<span className="ml-1 text-xs opacity-50">
{t("quickInput.newline") + ": Shift+Enter"}
</span>
)}
</div>
<div className="ml-1 flex items-center">
{!checked && <ChevronDownIcon className="h-4 w-4" />}
{checked && <ChevronUpIcon className="h-4 w-4" />}
</div>
</div>
<textarea
ref={textareaRef}
className="textarea block rounded-box resize-none leading-tight w-full min-h-0 h-0 p-0 border-none peer-checked/showTextarea:border-solid peer-checked/showTextarea:textarea-bordered peer-checked/showTextarea:min-h-20 peer-checked/showTextarea:p-2 transition-all duration-400"
></textarea>
</label>
</div>
);
}
30 changes: 24 additions & 6 deletions frontend/components/sync-button.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CursorArrowRippleIcon } from "@heroicons/react/24/solid";
import { useTranslations } from "next-intl";
import { useRef, useState } from "react";
import { useShortcut } from "@/lib/use-shortcut";
import { useRef, useState, useEffect } from "react";
import { isDesktop, isMacOs } from "react-device-detect";

export default function SyncButton({
syncFunc,
Expand All @@ -18,10 +18,28 @@ export default function SyncButton({
setClicked(false);
};

useShortcut({
eventHandler: () => {
buttonRef.current?.click();
},
// shortcut
useEffect(() => {
function keyDownHandler(e: globalThis.KeyboardEvent) {
if (!isDesktop) {
return;
}
// refer: https://support.google.com/chrome/answer/157179
if (
(!e.ctrlKey &&
!e.metaKey &&
!e.altKey &&
!e.shiftKey &&
e.key === "Enter") ||
(isMacOs && e.metaKey && e.key == "1") ||
(!isMacOs && e.ctrlKey && e.key == "1")
) {
e.preventDefault();
buttonRef.current?.click();
}
}
document.addEventListener("keydown", keyDownHandler);
return () => document.removeEventListener("keydown", keyDownHandler);
});

return (
Expand Down
36 changes: 26 additions & 10 deletions frontend/components/sync-clipboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import { osName, browserName, isAndroid } from "react-device-detect";
import SyncButton from "@/components/sync-button";
import SyncShortcut from "@/components/sync-shortcut";
import QuickInput from "@/components/quick-input";

// route: /locale?ci=123&cbi=abc
// - ci: clipboard index
Expand All @@ -37,7 +38,8 @@ export default function SyncClipboard() {
const [status, setStatus] = useState<string>("");
const [dragging, setDragging] = useState(false);

const inputRef = useRef<HTMLInputElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const locale = useLocale();
const { isLoading, loggedIn } = useAuth();
const router = useRouter();
Expand Down Expand Up @@ -232,10 +234,20 @@ export default function SyncClipboard() {
};

const pushClipboard = async () => {
if (!navigator.clipboard || !navigator.clipboard.read) {
return;
let blob = null;
if (textareaRef.current && textareaRef.current.value != "") {
blob = new Blob([textareaRef.current.value], { type: "text/plain" });
addLog(t("logs.readQuickInputSuccess"));
}

if (textareaRef.current?.value == "") {
if (!navigator.clipboard || !navigator.clipboard.read) {
return;
}
blob = await clipboardRead();
addLog(t("logs.readClipboardSuccess"));
}
let blob = await clipboardRead();

if (!blob) {
addLog(t("logs.emptyClipboard"));
return;
Expand Down Expand Up @@ -265,7 +277,6 @@ export default function SyncClipboard() {
addLog(t("logs.unchanged"));
return;
}
addLog(t("logs.readSuccess"));
addLog(t("logs.uploading"));

const response = await fetch("/api/v1/clipboard", {
Expand All @@ -288,6 +299,11 @@ export default function SyncClipboard() {
addLog(body.message, Level.Error);
return;
}

if (textareaRef.current && textareaRef.current.value != "") {
textareaRef.current.value = "";
}

const xindex = response.headers.get("x-index");
if (xindex == null || xindex == "0") {
return;
Expand Down Expand Up @@ -421,7 +437,7 @@ export default function SyncClipboard() {
<SyncButton syncFunc={syncFunc} />
</div>
</div>

<QuickInput textareaRef={textareaRef} />
<div className="pb-4">
<div className="pb-2 text-sm opacity-70">
<strong>{t("syncFile.title") + ": "}</strong>
Expand Down Expand Up @@ -466,7 +482,7 @@ export default function SyncClipboard() {
if (!ensureLoggedIn()) {
return;
}
inputRef.current?.click();
fileInputRef.current?.click();
}}
>
{t("syncFile.fileInputText")}
Expand All @@ -476,10 +492,10 @@ export default function SyncClipboard() {
<input
type="file"
hidden
ref={inputRef}
ref={fileInputRef}
onChange={async () => {
if (inputRef.current?.files) {
const selectedFile = inputRef.current.files[0];
if (fileInputRef.current?.files) {
const selectedFile = fileInputRef.current.files[0];
await uploadFileHandler(selectedFile);
}
}}
Expand Down
23 changes: 0 additions & 23 deletions frontend/lib/use-shortcut.ts

This file was deleted.

10 changes: 8 additions & 2 deletions frontend/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@
"dragDropTip": "Drag and drop a file here",
"fileInputText": "Choose a file"
},
"quickInput": {
"title": "Quick input",
"subTitle": "Type text, press the button and the text synced between devices.",
"newline": "Newline"
},
"logs": {
"pressToSync": "Press the button to sync clipboard 👉",
"fetching": "Fetching...",
"upToDate": "Already up to date.",
"readSuccess": "Read data from the clipboard successfully.",
"readClipboardSuccess": "Read data from the clipboard successfully.",
"writeSuccess": "Written data to the clipboard successfully.",
"autoDownload": "File download should start shortly. if not, please click the file link below.",
"denyRead": "Not allowed to read clipboard!",
Expand All @@ -68,7 +73,8 @@
"received": "Received {type}({index}) from {clientname}.",
"unsupportedFormat": "Unsupported format: {format}.",
"emptyClipboard": "Clipboard is empty.",
"recommendedBrowsers": "On {os}, recommend using {browsers} browser."
"recommendedBrowsers": "On {os}, recommend using {browsers} browser.",
"readQuickInputSuccess": "Read text from quick input area successfully."
}
}
}
10 changes: 8 additions & 2 deletions frontend/messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@
"dragDropTip": "把文件拖拽到这里",
"fileInputText": "选择一个文件"
},
"quickInput": {
"title": "快捷输入",
"subTitle": "输入文字后按下同步按钮就能在不同设备间同步.",
"newline": "换行"
},
"logs": {
"pressToSync": "按下按钮同步剪切板 👉",
"fetching": "正在获取...",
"upToDate": "已经是最新.",
"readSuccess": "成功读取剪切板.",
"readClipboardSuccess": "成功读取剪切板.",
"writeSuccess": "数据成功写入剪切板.",
"autoDownload": "文件会很快自动下载. 如果没有, 点击下面的文件链接.",
"denyRead": "不允许读取剪切板!",
Expand All @@ -68,7 +73,8 @@
"received": "已接收 {type}({index}) 来自 {clientname}.",
"unsupportedFormat": "不支持的格式: {format}.",
"emptyClipboard": "剪切板是空的.",
"recommendedBrowsers": "在{os}上, 推荐使用{browsers}浏览器."
"recommendedBrowsers": "在{os}上, 推荐使用{browsers}浏览器.",
"readQuickInputSuccess": "成功读取快捷输入区文字."
}
}
}
4 changes: 2 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gcopy",
"version": "1.2.17",
"version": "1.3.0",
"private": true,
"scripts": {
"dev": "next dev -p 3375 --experimental-https",
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.2.17
v1.3.0

0 comments on commit de841d2

Please sign in to comment.