chore(code):Add cache room ID feature, no need to manually input room ID

This commit is contained in:
david_bai
2025-10-23 20:47:49 +08:00
parent 0d308515a7
commit 5ca89d71ad
12 changed files with 213 additions and 3 deletions
+1
View File
@@ -202,6 +202,7 @@ const ClipboardApp = () => {
shareMessage={shareMessage}
currentValidatedShareRoomId={shareRoomId}
handleLeaveSenderRoom={handleLeaveSenderRoom}
putMessageInMs={putMessageInMs}
/>
) : (
<RetrieveTabPanel
@@ -1,15 +1,17 @@
import React, { useCallback } from "react";
import React, { useCallback, useEffect, useState } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
ReadClipboardButton,
WriteClipboardButton,
} from "@/components/common/clipboard_btn";
import Tooltip from "@/components/Tooltip";
import FileListDisplay from "@/components/ClipboardApp/FileListDisplay";
import type { Messages } from "@/types/messages";
import type { FileMeta } from "@/types/webrtc";
import { useFileTransferStore } from "@/stores/fileTransferStore";
import { getCachedId, setCachedId } from "@/lib/roomIdCache";
interface RetrieveTabPanelProps {
messages: Messages;
@@ -60,6 +62,12 @@ export function RetrieveTabPanel({
isReceiverInRoom,
} = useFileTransferStore();
// Cached ID state
const [hasCachedId, setHasCachedId] = useState<boolean>(false);
useEffect(() => {
setHasCachedId(!!getCachedId());
}, []);
const onLocationPick = useCallback(async (): Promise<boolean> => {
if (!messages) return false; // Should not happen if panel is rendered
if (!window.showDirectoryPicker) {
@@ -111,6 +119,46 @@ export function RetrieveTabPanel({
title={messages.text.ClipboardApp.html.readClipboard_dis}
onRead={setRetrieveRoomIdInput}
/>
{/* Save/Use Cached ID Button placed after Paste button */}
<Tooltip
content={
hasCachedId
? messages.text.ClipboardApp.html.useCachedId_tips
: messages.text.ClipboardApp.html.saveId_tips
}
>
<span className="inline-block">
<Button
className="w-full sm:w-auto px-4"
variant="outline"
onClick={() => {
if (hasCachedId) {
const cached = getCachedId();
if (cached) {
setRetrieveRoomIdInput(cached);
}
} else {
const trimmed = retrieveRoomIdInput.trim();
if (trimmed.length >= 8) {
setCachedId(trimmed);
setHasCachedId(true);
putMessageInMs(
messages.text.ClipboardApp.saveId_success,
false
);
}
}
}}
disabled={
!hasCachedId && retrieveRoomIdInput.trim().length < 8
}
>
{hasCachedId
? messages.text.ClipboardApp.html.useCachedId_dis
: messages.text.ClipboardApp.html.saveId_dis}
</Button>
</span>
</Tooltip>
<Input
aria-label="Retrieve Room ID"
value={retrieveRoomIdInput}
@@ -2,6 +2,7 @@ import React, { useState, useEffect, useCallback } from "react";
import dynamic from "next/dynamic";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import Tooltip from "@/components/Tooltip";
import {
ReadClipboardButton,
WriteClipboardButton,
@@ -13,6 +14,7 @@ import type { Messages } from "@/types/messages";
import type { CustomFile, FileMeta } from "@/types/webrtc";
import { useFileTransferStore } from "@/stores/fileTransferStore";
import { getCachedId, setCachedId } from "@/lib/roomIdCache";
// Dynamically import the RichTextEditor
const RichTextEditor = dynamic(
@@ -39,6 +41,11 @@ interface SendTabPanelProps {
shareMessage: string;
currentValidatedShareRoomId: string;
handleLeaveSenderRoom: () => void; // New prop for leaving room
putMessageInMs: (
message: string,
isShareEnd?: boolean,
displayTimeMs?: number
) => void;
}
export function SendTabPanel({
@@ -53,6 +60,7 @@ export function SendTabPanel({
shareMessage,
currentValidatedShareRoomId,
handleLeaveSenderRoom,
putMessageInMs,
}: SendTabPanelProps) {
// Get the status from the store
const {
@@ -69,12 +77,18 @@ export function SendTabPanel({
);
// State to track ID generation mode (false = will show simple next, true = will show random next)
const [isSimpleIdMode, setIsSimpleIdMode] = useState<boolean>(true);
// Cached ID state
const [hasCachedId, setHasCachedId] = useState<boolean>(false);
// When the validatedShareRoomId from the parent component changes (e.g., after initial fetch), synchronize the local input field's value
useEffect(() => {
setInputFieldValue(currentValidatedShareRoomId);
}, [currentValidatedShareRoomId]);
useEffect(() => {
setHasCachedId(!!getCachedId());
}, []);
const handleInputChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
@@ -127,6 +141,31 @@ export function SendTabPanel({
setIsSimpleIdMode(!isSimpleIdMode);
}, [isSimpleIdMode, processRoomIdInput, setInputFieldValue]);
// Save/Use cached ID button handlers
const isSaveEnabled = (inputFieldValue || "").trim().length >= 8;
const handleSaveOrUseCachedId = useCallback(() => {
if (hasCachedId) {
const cached = getCachedId();
if (cached) {
setInputFieldValue(cached);
}
return;
}
// Save current input to cache
const trimmed = (inputFieldValue || "").trim();
if (trimmed.length >= 8) {
setCachedId(trimmed);
setHasCachedId(true);
// Notify via messages on sender side
putMessageInMs(messages.text.ClipboardApp.saveId_success, true);
}
}, [
hasCachedId,
inputFieldValue,
putMessageInMs,
messages.text.ClipboardApp.saveId_success,
]);
return (
<div id="send-panel" role="tabpanel" aria-labelledby="send-tab">
<div className="mb-3 text-sm text-gray-600">
@@ -183,6 +222,27 @@ export function SendTabPanel({
? messages.text.ClipboardApp.html.generateRandomId_tips
: messages.text.ClipboardApp.html.generateSimpleId_tips}
</Button>
{/* Save/Use Cached ID Button in between */}
<Tooltip
content={
hasCachedId
? messages.text.ClipboardApp.html.useCachedId_tips
: messages.text.ClipboardApp.html.saveId_tips
}
>
<span className="inline-block">
<Button
className="w-full sm:w-auto px-4"
variant="outline"
onClick={handleSaveOrUseCachedId}
disabled={!hasCachedId && !isSaveEnabled}
>
{hasCachedId
? messages.text.ClipboardApp.html.useCachedId_dis
: messages.text.ClipboardApp.html.saveId_dis}
</Button>
</span>
</Tooltip>
<Button
className="w-full sm:w-auto px-4"
onClick={() => joinRoom(true, inputFieldValue.trim())}
+8
View File
@@ -336,7 +336,15 @@ export const de: Messages = {
readClipboard_dis: "Raum-ID einfügen",
retrieveRoomId_placeholder: "Raum-ID eingeben",
RetrieveMethodTitle: "Abrufmethode",
// New: cached ID utils
saveId_dis: "ID speichern",
useCachedId_dis: "Gespeicherte ID verwenden",
saveId_tips: "Aktuelle ID für spätere schnelle Nutzung speichern",
useCachedId_tips:
"Zuvor gespeicherte ID verwenden, um Tippen zu vermeiden",
},
// New: cache messages
saveId_success: "Erfolgreich im Cache gespeichert",
},
home: {
h1: "Kostenloses sicheres Online-Clipboard & Dateiübertragungstool",
+8
View File
@@ -327,7 +327,15 @@ export const en: Messages = {
readClipboard_dis: "Paste RoomID",
retrieveRoomId_placeholder: "Enter RoomID",
RetrieveMethodTitle: "Retrieve method",
// New: cached ID utils
saveId_dis: "Save ID",
useCachedId_dis: "Use cached ID",
saveId_tips: "Save current ID for quick reuse later",
useCachedId_tips:
"Quickly use the previously saved ID to avoid typing",
},
// New: cache messages
saveId_success: "Saved to cache",
},
home: {
h1: "Free Secure Online Clipboard & File Transfer Tool",
+8
View File
@@ -331,7 +331,15 @@ export const es: Messages = {
readClipboard_dis: "Pegar ID de Sala",
retrieveRoomId_placeholder: "Ingresa ID de Sala",
RetrieveMethodTitle: "Método de recuperación",
// New: cached ID utils
saveId_dis: "Guardar ID",
useCachedId_dis: "Usar ID en caché",
saveId_tips: "Guarda el ID actual para reutilizarlo rápidamente",
useCachedId_tips:
"Usa el ID guardado previamente para evitar escribir",
},
// New: cache messages
saveId_success: "Guardado en caché",
},
home: {
h1: "Herramienta Gratuita de Portapapeles y Transferencia de Archivos en Línea Segura",
+9
View File
@@ -338,7 +338,16 @@ export const fr: Messages = {
readClipboard_dis: "Coller l'ID de salle",
retrieveRoomId_placeholder: "Entrez l'ID de salle",
RetrieveMethodTitle: "Méthode de récupération",
// New: cached ID utils
saveId_dis: "Enregistrer lID",
useCachedId_dis: "Utiliser lID en cache",
saveId_tips:
"Enregistrez lID actuel pour une réutilisation rapide",
useCachedId_tips:
"Utilisez lID précédemment enregistré pour éviter la saisie",
},
// New: cache messages
saveId_success: "Enregistré dans le cache",
},
home: {
h1: "Outil gratuit de transfert de fichiers et de presse-papiers en ligne sécurisé",
+7
View File
@@ -323,7 +323,14 @@ export const ja: Messages = {
readClipboard_dis: "ルームIDを貼り付け",
retrieveRoomId_placeholder: "ルームIDを入力",
RetrieveMethodTitle: "取得方法",
// New: cached ID utils
saveId_dis: "ID を保存",
useCachedId_dis: "保存済みIDを使用",
saveId_tips: "現在のIDを保存して次回すぐに使えるようにします",
useCachedId_tips: "以前に保存したIDを使って入力を省略できます",
},
// New: cache messages
saveId_success: "キャッシュに保存しました",
},
home: {
h1: "無料で安全なオンラインクリップボード&ファイル転送ツール",
+9
View File
@@ -321,7 +321,16 @@ export const ko: Messages = {
readClipboard_dis: "방 ID 붙여넣기",
retrieveRoomId_placeholder: "방 ID 입력",
RetrieveMethodTitle: "검색 방법",
// New: cached ID utils
saveId_dis: "ID 저장",
useCachedId_dis: "저장된 ID 사용",
saveId_tips:
"현재 ID를 저장하여 다음에 빠르게 사용할 수 있어요",
useCachedId_tips:
"이전에 저장한 ID를 사용하여 입력을 줄입니다",
},
// New: cache messages
saveId_success: "캐시에 저장되었습니다",
},
home: {
h1: "무료 보안 온라인 클립보드 및 파일 전송 도구",
+10 -2
View File
@@ -283,7 +283,8 @@ export const zh: Messages = {
noFilesForFolderMsg: "在文件夹 '{folderName}' 中未找到文件。",
zipError: "创建 ZIP 文件时出错。",
fileNotFoundMsg: "未找到要下载的文件 '{fileName}'。",
confirmLeaveWhileTransferring: "传输将中断,已设置保存目录时可续传。确定退出?",
confirmLeaveWhileTransferring:
"传输将中断,已设置保存目录时可续传。确定退出?",
leaveWhileTransferringSuccess: "已退出房间,传输已中断",
html: {
senderTab: "发送",
@@ -296,7 +297,7 @@ export const zh: Messages = {
inputRoomIdprompt: "您的房间ID(可编辑):",
joinRoomBtn: "加入房间",
generateSimpleId_tips: "简单ID",
generateRandomId_tips: "随机ID",
generateRandomId_tips: "随机ID",
readClipboardToRoomId: "粘贴房间ID",
enterRoomID_placeholder: "输入房间ID",
retrieveMethod: "接收方式",
@@ -307,7 +308,14 @@ export const zh: Messages = {
readClipboard_dis: "粘贴房间ID",
retrieveRoomId_placeholder: "输入房间ID",
RetrieveMethodTitle: "接收方式",
// New: cached ID utils
saveId_dis: "保存ID",
useCachedId_dis: "使用缓存ID",
saveId_tips: "保存ID后,下次可以快捷使用该ID",
useCachedId_tips: "可以快捷使用之前保存的ID,避免手动输入",
},
// New: cache messages
saveId_success: "缓存成功",
},
home: {
h1: "免费安全的在线剪贴板与文件传输工具",
+37
View File
@@ -0,0 +1,37 @@
// Utilities to cache a single room ID in browser localStorage
// Works on client only; no-ops on server.
const CACHED_KEY = "pd_cached_room_id_v1";
function isClient() {
return typeof window !== "undefined";
}
export function getCachedId(): string | null {
if (!isClient()) return null;
try {
const v = window.localStorage.getItem(CACHED_KEY);
return v && v.trim() ? v : null;
} catch (_) {
return null;
}
}
export function setCachedId(id: string): void {
if (!isClient()) return;
try {
window.localStorage.setItem(CACHED_KEY, id);
} catch (_) {
// ignore
}
}
export function clearCachedId(): void {
if (!isClient()) return;
try {
window.localStorage.removeItem(CACHED_KEY);
} catch (_) {
// ignore
}
}
+7
View File
@@ -254,6 +254,11 @@ export type ClipboardAppHtml = {
readClipboard_dis: string;
retrieveRoomId_placeholder: string;
RetrieveMethodTitle: string;
// New: cached ID utilities
saveId_dis: string;
useCachedId_dis: string;
saveId_tips: string;
useCachedId_tips: string;
};
export type ClipboardApp = {
@@ -274,6 +279,8 @@ export type ClipboardApp = {
fileNotFoundMsg?: string;
confirmLeaveWhileTransferring: string;
leaveWhileTransferringSuccess: string;
// New: cache messages
saveId_success: string;
};
export type Home = {