aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-02-13 00:29:25 +0000
committerMohamedBassem <me@mbassem.com>2024-02-13 00:33:58 +0000
commit33c9e8bca54d753c7ea976dd178db0cd5408c218 (patch)
tree56099acd736a37845a7fce3a3ded0d399dbf5777
parentb00d2b360d8000edcd9bfa82673ca322a9ac6d1a (diff)
downloadkarakeep-33c9e8bca54d753c7ea976dd178db0cd5408c218.tar.zst
feature: A usable, yet ugly browser extension
-rw-r--r--.github/workflows/main.yml3
-rw-r--r--packages/browser-extension/.eslintrc.cjs3
-rw-r--r--packages/browser-extension/manifest.json8
-rw-r--r--packages/browser-extension/postcss.config.js2
-rw-r--r--packages/browser-extension/src/App.tsx5
-rw-r--r--packages/browser-extension/src/NotConfiguredPage.tsx16
-rw-r--r--packages/browser-extension/src/OptionsPage.tsx (renamed from packages/browser-extension/src/SettingsPage.tsx)15
-rw-r--r--packages/browser-extension/src/SavePage.tsx61
-rw-r--r--packages/browser-extension/src/main.tsx21
-rw-r--r--packages/browser-extension/tailwind.config.js10
-rw-r--r--packages/web/app/api/trpc/[trpc]/route.ts7
-rw-r--r--packages/web/next.config.mjs29
12 files changed, 123 insertions, 57 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index cc8a45d0..68fe9fb3 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -32,3 +32,6 @@ jobs:
- name: Build web app
working-directory: packages/web
run: yarn run build
+ - name: Build the browser extension
+ working-directory: packages/browser-extension
+ run: yarn build
diff --git a/packages/browser-extension/.eslintrc.cjs b/packages/browser-extension/.eslintrc.cjs
new file mode 100644
index 00000000..450106a4
--- /dev/null
+++ b/packages/browser-extension/.eslintrc.cjs
@@ -0,0 +1,3 @@
+module.exports = {
+ ignorePatterns: ["dist/"],
+};
diff --git a/packages/browser-extension/manifest.json b/packages/browser-extension/manifest.json
index bf3293ca..d7930ee8 100644
--- a/packages/browser-extension/manifest.json
+++ b/packages/browser-extension/manifest.json
@@ -6,7 +6,9 @@
"action": {
"default_popup": "index.html"
},
- "permissions": [
- "storage"
- ]
+ "options_ui": {
+ "page": "index.html#options",
+ "open_in_tab": false
+ },
+ "permissions": ["storage", "activeTab", "tabs"]
}
diff --git a/packages/browser-extension/postcss.config.js b/packages/browser-extension/postcss.config.js
index 2e7af2b7..2aa7205d 100644
--- a/packages/browser-extension/postcss.config.js
+++ b/packages/browser-extension/postcss.config.js
@@ -3,4 +3,4 @@ export default {
tailwindcss: {},
autoprefixer: {},
},
-}
+};
diff --git a/packages/browser-extension/src/App.tsx b/packages/browser-extension/src/App.tsx
index 21fec92f..8a2c4920 100644
--- a/packages/browser-extension/src/App.tsx
+++ b/packages/browser-extension/src/App.tsx
@@ -1,4 +1,3 @@
-import { redirect } from "react-router-dom";
import SavePage from "./SavePage";
import usePluginSettings from "./settings";
import { useNavigate } from "react-router-dom";
@@ -12,7 +11,7 @@ function App() {
}
if (!settings.apiKey || !settings.address) {
- navigate("/settings");
+ navigate("/notconfigured");
return;
}
@@ -21,7 +20,7 @@ function App() {
<SavePage settings={settings} />
<hr />
<div className="flex justify-end">
- <button className="w-2/6" onClick={() => navigate("/settings")}>
+ <button className="w-2/6" onClick={() => navigate("/options")}>
Settings
</button>
</div>
diff --git a/packages/browser-extension/src/NotConfiguredPage.tsx b/packages/browser-extension/src/NotConfiguredPage.tsx
new file mode 100644
index 00000000..ef73f149
--- /dev/null
+++ b/packages/browser-extension/src/NotConfiguredPage.tsx
@@ -0,0 +1,16 @@
+import { useNavigate } from "react-router-dom";
+
+export default function NotConfiguredPage() {
+ const navigate = useNavigate();
+ return (
+ <div className="flex flex-col space-y-2">
+ <span>To use the plugin, you need to configure it first.</span>
+ <button
+ className="bg-black text-white"
+ onClick={() => navigate("/options")}
+ >
+ Configure
+ </button>
+ </div>
+ );
+}
diff --git a/packages/browser-extension/src/SettingsPage.tsx b/packages/browser-extension/src/OptionsPage.tsx
index bae870ac..e3a34bd9 100644
--- a/packages/browser-extension/src/SettingsPage.tsx
+++ b/packages/browser-extension/src/OptionsPage.tsx
@@ -1,9 +1,7 @@
import { useRef } from "react";
import usePluginSettings from "./settings";
-import { useNavigate } from "react-router-dom";
-export default function SettingsPage() {
- const navigate = useNavigate();
+export default function OptionsPage() {
const [settings, setSettings, _1, _2, _3] = usePluginSettings();
const apiKeyRef = useRef<HTMLInputElement>(null);
@@ -36,18 +34,9 @@ export default function SettingsPage() {
className="h-8 flex-1 rounded-lg border border-gray-300 p-2"
/>
</div>
- <button
- className="rounded-lg border border-gray-200"
- onClick={onSave}
- >
+ <button className="rounded-lg border border-gray-200" onClick={onSave}>
Save
</button>
- <button
- className="rounded-lg border border-gray-200"
- onClick={() => navigate("/")}
- >
- Back
- </button>
</div>
);
}
diff --git a/packages/browser-extension/src/SavePage.tsx b/packages/browser-extension/src/SavePage.tsx
index c66cc0ad..e202aea5 100644
--- a/packages/browser-extension/src/SavePage.tsx
+++ b/packages/browser-extension/src/SavePage.tsx
@@ -5,23 +5,49 @@ export default function SavePage({ settings }: { settings: Settings }) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | undefined>(undefined);
- async function runFetch() {
- const resp = await fetch(
- `${settings.address}/api/trpc/bookmarks.bookmarkLink`,
- {
- method: "POST",
- },
- );
+ useEffect(() => {
+ async function runFetch() {
+ let currentUrl;
+ const [currentTab] = await chrome.tabs.query({
+ active: true,
+ lastFocusedWindow: true,
+ });
+ if (currentTab?.url) {
+ currentUrl = currentTab.url;
+ } else {
+ setError("Couldn't find the URL of the current tab");
+ setLoading(false);
+ return;
+ }
- if (!resp.ok) {
- setError("Something went wrong: " + (await resp.json()));
- }
- setLoading(false);
- }
+ try {
+ const resp = await fetch(
+ `${settings.address}/api/trpc/bookmarks.bookmarkLink`,
+ {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${settings.apiKey}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ type: "link",
+ url: currentUrl,
+ }),
+ },
+ );
+ if (!resp.ok) {
+ setError(
+ "Something went wrong: " + JSON.stringify(await resp.json()),
+ );
+ }
+ } catch (e) {
+ setError("Message: " + (e as Error).message);
+ }
- useEffect(() => {
+ setLoading(false);
+ }
runFetch();
- }, []);
+ }, [settings]);
if (loading) {
return <div>Loading ...</div>;
@@ -31,10 +57,5 @@ export default function SavePage({ settings }: { settings: Settings }) {
return <div className="text-red-500">{error} ...</div>;
}
- return (
- <div>
- SAVED!
- <button onClick={runFetch}> Reload </button>
- </div>
- );
+ return <div>SAVED!</div>;
}
diff --git a/packages/browser-extension/src/main.tsx b/packages/browser-extension/src/main.tsx
index 298ab388..c4f5a2d9 100644
--- a/packages/browser-extension/src/main.tsx
+++ b/packages/browser-extension/src/main.tsx
@@ -2,27 +2,28 @@ import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
-import { createBrowserRouter, Navigate, RouterProvider } from "react-router-dom";
-import SettingsPage from "./SettingsPage.tsx";
+import { createHashRouter, RouterProvider } from "react-router-dom";
+import OptionsPage from "./OptionsPage.tsx";
+import NotConfiguredPage from "./NotConfiguredPage.tsx";
-const router = createBrowserRouter([
- {
- path: "/index.html",
- element: <Navigate to="/" />,
- },
+const router = createHashRouter([
{
path: "/",
element: <App />,
},
{
- path: "/settings",
- element: <SettingsPage />,
+ path: "/notconfigured",
+ element: <NotConfiguredPage />,
+ },
+ {
+ path: "/options",
+ element: <OptionsPage />,
},
]);
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
- <div className="p-4 w-96">
+ <div className="w-96 p-4">
<RouterProvider router={router} />
</div>
</React.StrictMode>,
diff --git a/packages/browser-extension/tailwind.config.js b/packages/browser-extension/tailwind.config.js
index d37737fc..1c0c7c87 100644
--- a/packages/browser-extension/tailwind.config.js
+++ b/packages/browser-extension/tailwind.config.js
@@ -1,12 +1,10 @@
/** @type {import('tailwindcss').Config} */
-export default {
- content: [
- "./index.html",
- "./src/**/*.{js,ts,jsx,tsx}",
- ],
+const tailwindConfig = {
+ content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
-}
+};
+export default tailwindConfig;
diff --git a/packages/web/app/api/trpc/[trpc]/route.ts b/packages/web/app/api/trpc/[trpc]/route.ts
index e04539a9..aea9bc70 100644
--- a/packages/web/app/api/trpc/[trpc]/route.ts
+++ b/packages/web/app/api/trpc/[trpc]/route.ts
@@ -8,6 +8,13 @@ const handler = (req: Request) =>
endpoint: "/api/trpc",
req,
router: appRouter,
+ onError: ({ path, error }) => {
+ if (process.env.NODE_ENV === "development") {
+ console.error(`❌ tRPC failed on ${path}`);
+ }
+ console.error(error);
+ },
+
createContext: async (opts) => {
// TODO: This is a hack until we offer a proper REST API instead of the trpc based one.
// Check if the request has an Authorization token, if it does, assume that API key authentication is requested.
diff --git a/packages/web/next.config.mjs b/packages/web/next.config.mjs
index 4678774e..f2ed7754 100644
--- a/packages/web/next.config.mjs
+++ b/packages/web/next.config.mjs
@@ -1,4 +1,31 @@
/** @type {import('next').NextConfig} */
-const nextConfig = {};
+const nextConfig = {
+ async headers() {
+ return [
+ {
+ // Routes this applies to
+ source: "/api/(.*)",
+ // Headers
+ headers: [
+ // Allow for specific domains to have access or * for all
+ {
+ key: "Access-Control-Allow-Origin",
+ value: "*",
+ },
+ // Allows for specific methods accepted
+ {
+ key: "Access-Control-Allow-Methods",
+ value: "GET, POST, PUT, DELETE, OPTIONS",
+ },
+ // Allows for specific headers accepted (These are a few standard ones)
+ {
+ key: "Access-Control-Allow-Headers",
+ value: "Content-Type, Authorization",
+ },
+ ],
+ },
+ ];
+ },
+};
export default nextConfig;