Skip to content

Commit 6479259

Browse files
Upload prompt-file to replace prompts
1 parent 60311bb commit 6479259

File tree

7 files changed

+159
-16
lines changed

7 files changed

+159
-16
lines changed

src/background/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
initStorage,
44
updateCache,
55
getPrompts,
6+
setPrompts,
67
updatePrompt,
78
deletePrompt,
89
createPrompt,
@@ -30,6 +31,10 @@ async function handleMessage(
3031
case "getPrompts":
3132
sendResponse(getPrompts());
3233
break;
34+
case "setPrompts":
35+
await setPrompts(request.prompts);
36+
sendResponse();
37+
break;
3338
case "createPrompt":
3439
await createPrompt(request.key, request.value);
3540
sendResponse();

src/background/storage.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export function getPrompts() {
3030
return promptsCache;
3131
}
3232

33+
export async function setPrompts(prompts: { [key: string]: string }) {
34+
await updateCache(prompts);
35+
await chrome.storage.local.set({ prompts: prompts });
36+
}
37+
3338
// Add a new prompt
3439
export async function createPrompt(key: string, value: string) {
3540
const updatedPrompts = { ...promptsCache, [key]: value };

src/popup/Popup.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import { CSSTransition, TransitionGroup } from "react-transition-group";
99
import SearchPage from "./pages/SearchPage";
1010
import EditPage from "./pages/EditPage";
11+
import DataPage from "./pages/DataPage";
1112
import CreatePage from "./pages/CreatePage";
1213
import "../../styles/transitions.css";
1314

@@ -33,6 +34,7 @@ const PopupContent: React.FC = () => {
3334
<Routes location={location}>
3435
<Route path="/" element={<SearchPage />} />
3536
<Route path="/edit/:index" element={<EditPage />} />
37+
<Route path="/data" element={<DataPage />} />
3638
<Route path="/create" element={<CreatePage />} />
3739
</Routes>
3840
</CSSTransition>

src/popup/components/SearchBar.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from "react";
2-
import { HiPlus, HiDownload } from "react-icons/hi";
2+
import { HiPlus, HiOutlineDocumentText } from "react-icons/hi";
33

44
interface Props {
55
onSearch: (search: string) => void;
6-
onDownload: () => void;
6+
onData: () => void;
77
onAdd: () => void;
88
}
99

10-
const SearchBar: React.FC<Props> = ({ onSearch, onDownload, onAdd }) => {
10+
const SearchBar: React.FC<Props> = ({ onSearch, onData, onAdd }) => {
1111
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
1212
onSearch(e.target.value);
1313
};
@@ -23,9 +23,9 @@ const SearchBar: React.FC<Props> = ({ onSearch, onDownload, onAdd }) => {
2323
<div className="flex flex-row p-2">
2424
<button
2525
className="flex-none rounded p-2 text-blue-500 hover:bg-zinc-900 hover:text-blue-400"
26-
onClick={onDownload}
26+
onClick={onData}
2727
>
28-
<HiDownload size={18} />
28+
<HiOutlineDocumentText size={18} />
2929
</button>
3030
<button
3131
className="flex-none rounded p-2 text-blue-500 hover:bg-zinc-900 hover:text-blue-400"

src/popup/pages/DataPage.tsx

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import React, { useState, useRef, ChangeEvent, DragEvent } from "react";
2+
import { useNavigate } from "react-router-dom";
3+
import { usePromptContext } from "../../contexts/PromptContext";
4+
import { setPrompts } from "../../utils/message";
5+
import { HiArrowLeft, HiUpload, HiDownload } from "react-icons/hi";
6+
7+
const DataPage: React.FC = () => {
8+
const { prompts } = usePromptContext();
9+
const navigate = useNavigate();
10+
const fileRef = useRef<HTMLInputElement>(null);
11+
const [loading, setLoading] = useState(false);
12+
const [uploadStatus, setUploadStatus] = useState<{
13+
message: string;
14+
success: boolean;
15+
} | null>(null);
16+
17+
const handleBack = () => {
18+
navigate(-1);
19+
};
20+
21+
const handleDownload = () => {
22+
const blob = new Blob([JSON.stringify(prompts, null, 2)], {
23+
type: "application/json",
24+
});
25+
const url = URL.createObjectURL(blob);
26+
const link = document.createElement("a");
27+
link.href = url;
28+
link.download = "prompts.json";
29+
link.click();
30+
};
31+
32+
const handleUpload = (e: ChangeEvent<HTMLInputElement>) => {
33+
setLoading(true);
34+
if (e.target) {
35+
const file = e.target.files?.[0];
36+
if (file) {
37+
const reader = new FileReader();
38+
reader.onload = (e) => {
39+
if (e.target) {
40+
try {
41+
const newPrompts = JSON.parse(e.target.result as string);
42+
console.log(newPrompts);
43+
setPrompts(newPrompts);
44+
setUploadStatus({ message: "Upload successful!", success: true });
45+
} catch (error) {
46+
setUploadStatus({
47+
message: "Invalid JSON. Upload failed!",
48+
success: false,
49+
});
50+
}
51+
setLoading(false);
52+
}
53+
};
54+
reader.readAsText(file);
55+
} else {
56+
setLoading(false);
57+
setUploadStatus({
58+
message: "No file selected. Upload failed!",
59+
success: false,
60+
});
61+
}
62+
}
63+
};
64+
65+
const handleDrop = (e: DragEvent<HTMLDivElement>) => {
66+
e.preventDefault();
67+
handleUpload({
68+
target: { files: e.dataTransfer.files },
69+
} as ChangeEvent<HTMLInputElement>);
70+
};
71+
72+
return (
73+
<div className="w-full">
74+
<div className="flex flex-row items-center justify-start border-b border-zinc-700 p-2">
75+
<button
76+
className="rounded p-2 text-blue-500 hover:bg-zinc-900 hover:text-blue-400"
77+
onClick={handleBack}
78+
>
79+
<HiArrowLeft size={18} />
80+
</button>
81+
<h2 className="text-base text-white">Edit Data File</h2>
82+
</div>
83+
<div className="flex flex-col space-y-2 p-2">
84+
<div className="p-2 text-zinc-500">
85+
You can download your prompts as a JSON file to edit them in a text
86+
editor of your choice and then upload them back to prompster.
87+
</div>
88+
<button
89+
className="flex w-full flex-row items-center justify-center space-x-4 rounded p-2 text-blue-500 hover:bg-zinc-900 hover:text-blue-400"
90+
onClick={handleDownload}
91+
>
92+
<HiDownload size={18} />
93+
Download Prompts
94+
</button>
95+
<div
96+
onDrop={handleDrop}
97+
onDragOver={(e) => e.preventDefault()}
98+
className="flex h-[200px] cursor-pointer flex-col items-center justify-center rounded bg-zinc-900 text-blue-500 hover:text-blue-400 p-4"
99+
onClick={() => fileRef.current?.click()}
100+
>
101+
<input
102+
ref={fileRef}
103+
type="file"
104+
accept=".json"
105+
hidden
106+
onChange={handleUpload}
107+
/>
108+
<div className="flex flex-row items-center justify-center space-x-4">
109+
<HiUpload size={18} />
110+
Upload Prompts
111+
</div>
112+
<div className="p-2 text-zinc-500 text-center">
113+
Warning: Uploading a file will overwrite your current prompts.
114+
</div>
115+
{loading ? (
116+
<div className="p-2 text-zinc-500">
117+
{loading ? "Uploading..." : uploadStatus?.message}
118+
</div>
119+
) : (
120+
<div
121+
className={`p-2 ${
122+
uploadStatus?.success ? "text-green-500" : "text-red-500"
123+
}`}
124+
>
125+
{uploadStatus?.message}
126+
</div>
127+
)}
128+
</div>
129+
</div>
130+
</div>
131+
);
132+
};
133+
134+
export default DataPage;

src/popup/pages/SearchPage.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,8 @@ const SearchPage = () => {
3232
setFilteredPrompts(results);
3333
}, [search, prompts]);
3434

35-
const handleDownload = () => {
36-
const blob = new Blob([JSON.stringify(prompts)], {
37-
type: "application/json",
38-
});
39-
const url = URL.createObjectURL(blob);
40-
const a = document.createElement("a");
41-
42-
a.href = url;
43-
a.download = "prompts.json";
44-
a.click();
35+
const handleDataClick = () => {
36+
navigate("/data");
4537
};
4638

4739
const handleAddClick = () => {
@@ -56,7 +48,7 @@ const SearchPage = () => {
5648
<>
5749
<div className="fixed inset-x-0 top-0 z-10 border-zinc-700">
5850
<div className="bg-black" ref={searchBarRef}>
59-
<SearchBar onSearch={setSearch} onDownload={handleDownload} onAdd={handleAddClick} />
51+
<SearchBar onSearch={setSearch} onData={handleDataClick} onAdd={handleAddClick} />
6052
<div className="px-4 py-1 text-xs text-zinc-500">
6153
{filteredPrompts.length} of {Object.entries(prompts).length} prompts
6254
</div>

src/utils/message.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import browser from "webextension-polyfill";
22

33
type StorageAction =
44
| "getPrompts"
5+
| "setPrompts"
56
| "createPrompt"
67
| "updatePrompt"
78
| "deletePrompt";
@@ -22,6 +23,10 @@ export async function getPrompts(): Promise<{ [key: string]: string }> {
2223
return response;
2324
}
2425

26+
export async function setPrompts(prompts: { [key: string]: string }): Promise<void> {
27+
await sendStorageRequest({ action: "setPrompts", prompts: prompts });
28+
}
29+
2530
export async function createPrompt(key: string, value: string): Promise<void> {
2631
await sendStorageRequest({ action: "createPrompt", key: key, value: value });
2732
}

0 commit comments

Comments
 (0)