-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
commit bdba677 Author: 老J <[email protected]> Date: Wed Jul 31 13:27:04 2024 +0800 v1.4.0 commit c7e656b Author: 老J <[email protected]> Date: Wed Jul 31 13:24:39 2024 +0800 Catch the error when using the items commit 9c18d82 Author: 老J <[email protected]> Date: Thu Jul 25 14:25:27 2024 +0800 Fix error on Safari: Failed to load resource: The operation couldn’t be completed. (WebKitBlobResource error 1.) commit 69838c2 Author: 老J <[email protected]> Date: Tue Jul 23 19:48:32 2024 +0800 Empty history. commit fe3ead9 Author: 老J <[email protected]> Date: Sun Jul 21 17:48:12 2024 +0800 Set the history item. to file link area. commit d95e654 Author: 老J <[email protected]> Date: Sun Jul 21 11:11:36 2024 +0800 Write history items to clipboard. commit 1b0d054 Author: 老J <[email protected]> Date: Fri Jul 19 10:32:09 2024 +0800 Pin/unpin history items. commit 07ee495 Author: 老J <[email protected]> Date: Thu Jul 18 19:53:34 2024 +0800 Delete item from history IndexedDB commit 592d72e Author: 老J <[email protected]> Date: Thu Jul 18 08:57:28 2024 +0800 Replace the group buttons with the ellipsis button. commit d72f1fe Author: 老J <[email protected]> Date: Wed Jul 17 18:42:17 2024 +0800 Display history items from IndexedDB commit 9eb3cf2 Author: 老J <[email protected]> Date: Tue Jul 16 20:10:22 2024 +0800 Add history item to IndexedDB commit 698831f Author: 老J <[email protected]> Date: Fri Jul 12 15:55:54 2024 +0800 Put clipboard data into indexedDB. commit 05cb4f5 Author: 老J <[email protected]> Date: Tue Jul 9 17:24:11 2024 +0800 Made some adjustments to the page. commit 5510b8d Author: 老J <[email protected]> Date: Tue Jul 9 16:27:27 2024 +0800 Add static pages of clipboard history feature.
- Loading branch information
Showing
16 changed files
with
640 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
import { useTranslations } from "next-intl"; | ||
import { | ||
EllipsisHorizontalIcon, | ||
LockClosedIcon, | ||
} from "@heroicons/react/24/solid"; | ||
import Image from "next/image"; | ||
import { useEffect, useRef, useState } from "react"; | ||
import { HistoryItemEntity } from "@/models/history"; | ||
import moment from "moment/min/moment-with-locales"; | ||
import { db } from "@/models/db"; | ||
import { Log, LogLevel } from "@/lib/log"; | ||
import { clipboardWriteBlob, clipboardWriteBlobPromise } from "@/lib/clipboard"; | ||
import { browserName } from "react-device-detect"; | ||
import { FileInfo } from "@/lib/clipboard"; | ||
|
||
export default function HistoryItem({ | ||
item, | ||
addLog, | ||
updateFileLink, | ||
}: { | ||
item: HistoryItemEntity; | ||
addLog: (log: Log) => void; | ||
updateFileLink: (fileInfo: FileInfo) => void; | ||
}) { | ||
const t = useTranslations("SyncClipboard"); | ||
const ulRef = useRef<HTMLUListElement>(null); | ||
|
||
const [text, setText] = useState<string>(""); | ||
useEffect(() => { | ||
setText(""); | ||
if (item.type == "text") { | ||
const parseText = async () => { | ||
if (item.dataArrayBuffer) { | ||
setText( | ||
await new Blob([item.dataArrayBuffer], { | ||
type: item.dataType, | ||
}).text(), | ||
); | ||
} | ||
}; | ||
parseText(); | ||
} | ||
}, [item]); | ||
|
||
return ( | ||
<tr> | ||
<td className="w-4 p-2 opacity-50"> | ||
{item.pin == "true" ? ( | ||
<LockClosedIcon className="w-4 h-4" /> | ||
) : ( | ||
<span className="text-sm">{item.index}</span> | ||
)} | ||
</td> | ||
<td className="h-14 p-2 w-auto"> | ||
{item.type == "text" && ( | ||
<p className="line-clamp-1 opacity-70 break-all">{text}</p> | ||
)} | ||
{item.type == "screenshot" && ( | ||
<div className="relative h-full"> | ||
<Image | ||
className="object-left object-contain" | ||
src={ | ||
item.dataArrayBuffer | ||
? (window.URL || window.webkitURL).createObjectURL( | ||
new Blob([item.dataArrayBuffer], { type: item.dataType }), | ||
) | ||
: "" | ||
} | ||
alt="user's screenshot" | ||
fill | ||
/> | ||
</div> | ||
)} | ||
{item.type == "file" && ( | ||
<p className="line-clamp-1 opacity-70 break-all"> | ||
{"[" + t("file") + "]" + item.fileName} | ||
</p> | ||
)} | ||
</td> | ||
<td className="w-min pr-1 whitespace-pre p-2 text-xs text-nowrap break-keep opacity-50 text-right"> | ||
{moment(item.createdAt).fromNow()} | ||
</td> | ||
<td className="w-9 px-0"> | ||
<div className="dropdown dropdown-end"> | ||
<div tabIndex={0} role="button" className="btn btn-ghost btn-xs"> | ||
<EllipsisHorizontalIcon className="h-4 w-4" /> | ||
</div> | ||
<ul | ||
tabIndex={0} | ||
className="dropdown-content menu bg-base-100 rounded-box z-[1] w-28 p-2 shadow-lg border border-base-300" | ||
ref={ulRef} | ||
> | ||
<li> | ||
<a | ||
className="hover:bg-base-300 opacity-70" | ||
onClick={async () => { | ||
try { | ||
ulRef.current && ulRef.current.blur(); | ||
if (item.type == "file") { | ||
updateFileLink({ | ||
fileName: item.fileName ?? "", | ||
fileURL: ( | ||
window.URL || window.webkitURL | ||
).createObjectURL(item.data), | ||
}); | ||
addLog({ | ||
message: t("logs.autoDownload"), | ||
level: LogLevel.Success, | ||
}); | ||
return; | ||
} | ||
|
||
if (item.type == "text" || item.type == "screenshot") { | ||
if (browserName.includes("Safari")) { | ||
await clipboardWriteBlobPromise(item.data); | ||
addLog({ | ||
message: t("logs.writeSuccess"), | ||
level: LogLevel.Success, | ||
}); | ||
return; | ||
} | ||
|
||
await clipboardWriteBlob(item.data); | ||
addLog({ | ||
message: t("logs.writeSuccess"), | ||
level: LogLevel.Success, | ||
}); | ||
return; | ||
} | ||
} catch (err) { | ||
addLog({ message: String(err), level: LogLevel.Error }); | ||
} | ||
}} | ||
> | ||
{t("history.use")} | ||
</a> | ||
</li> | ||
{item.pin == "true" && ( | ||
<li> | ||
<a | ||
className="hover:bg-base-300 opacity-70" | ||
onClick={() => { | ||
db.history.update(item, { pin: "false" }); | ||
}} | ||
> | ||
Unpin | ||
</a> | ||
</li> | ||
)} | ||
{item.pin == "false" && ( | ||
<li> | ||
<a | ||
className="hover:bg-base-300 opacity-70" | ||
onClick={async () => { | ||
const count = await db.history | ||
.where("pin") | ||
.equals("true") | ||
.count(); | ||
if (count >= 10) { | ||
ulRef.current && ulRef.current.blur(); | ||
addLog({ | ||
message: t("logs.pinLimit"), | ||
level: LogLevel.Warn, | ||
}); | ||
return; | ||
} | ||
db.history.update(item, { pin: "true" }); | ||
}} | ||
> | ||
Pin | ||
</a> | ||
</li> | ||
)} | ||
<li> | ||
<a | ||
className="hover:bg-base-300 opacity-70" | ||
onClick={() => { | ||
item.createdAt && db.history.delete(item.createdAt); | ||
ulRef.current && ulRef.current.blur(); | ||
}} | ||
> | ||
{t("history.delete")} | ||
</a> | ||
</li> | ||
</ul> | ||
</div> | ||
</td> | ||
</tr> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { useTranslations } from "next-intl"; | ||
import HistoryItem from "./history-item"; | ||
import { useLiveQuery } from "dexie-react-hooks"; | ||
import { db } from "@/models/db"; | ||
import { Log } from "@/lib/log"; | ||
import { FileInfo } from "@/lib/clipboard"; | ||
|
||
export default function History({ | ||
addLog, | ||
updateFileLink, | ||
}: { | ||
addLog: (log: Log) => void; | ||
updateFileLink: (fileInfo: FileInfo) => void; | ||
}) { | ||
const t = useTranslations("SyncClipboard.history"); | ||
const items = useLiveQuery(() => db.history.reverse().toArray()); | ||
if (!items) return null; | ||
|
||
return ( | ||
<div className="pb-4"> | ||
<div className="pb-2 text-base font-bold">{t("title")}</div> | ||
<div className="pb-2 text-sm opacity-70">{t("subTitle")}</div> | ||
<div className="bg-base-100 rounded-box mb-2 px-2 border border-base-300"> | ||
<table className="table"> | ||
<tbody> | ||
{items.length == 0 && ( | ||
<tr> | ||
<td className="w-4 p-2 opacity-50 text-center">{t("empty")}</td> | ||
</tr> | ||
)} | ||
{items.map( | ||
(item) => | ||
item.pin == "true" && ( | ||
<HistoryItem | ||
key={item.createdAt} | ||
item={item} | ||
addLog={addLog} | ||
updateFileLink={updateFileLink} | ||
/> | ||
), | ||
)} | ||
{items.map( | ||
(item) => | ||
item.pin == "false" && ( | ||
<HistoryItem | ||
key={item.createdAt} | ||
item={item} | ||
addLog={addLog} | ||
updateFileLink={updateFileLink} | ||
/> | ||
), | ||
)} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.