mirror of
https://github.com/aykhans/slash-e.git
synced 2025-04-16 04:13:12 +00:00
chore: redirect to instance url
This commit is contained in:
parent
7c9798b6b1
commit
764d776524
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "slash-extension",
|
||||
"displayName": "Slash",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"description": "An open source, self-hosted bookmarks and link sharing platform. Save and share your links very easily.",
|
||||
"scripts": {
|
||||
"dev": "plasmo dev",
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Storage } from "@plasmohq/storage";
|
||||
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
||||
|
||||
const storage = new Storage();
|
||||
const urlRegex = /https?:\/\/s\/(.+)/;
|
||||
@ -13,12 +12,8 @@ chrome.webRequest.onBeforeRequest.addListener(
|
||||
|
||||
const shortcutName = getShortcutNameFromUrl(param.url);
|
||||
if (shortcutName) {
|
||||
const shortcuts = (await storage.getItem<Shortcut[]>("shortcuts")) || [];
|
||||
const shortcut = shortcuts.find((shortcut) => shortcut.name === shortcutName);
|
||||
if (!shortcut) {
|
||||
return;
|
||||
}
|
||||
return chrome.tabs.update({ url: shortcut.link });
|
||||
const instanceUrl = (await storage.getItem<string>("domain")) || "";
|
||||
return chrome.tabs.update({ url: `${instanceUrl}/s/${shortcutName}` });
|
||||
}
|
||||
})();
|
||||
},
|
||||
|
@ -1,34 +1,22 @@
|
||||
import { Button, IconButton, Input, Modal, ModalDialog } from "@mui/joy";
|
||||
import { useStorage } from "@plasmohq/storage/hook";
|
||||
import axios from "axios";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import useShortcutStore from "@/store/shortcut";
|
||||
import { Visibility } from "@/types/proto/api/v2/common";
|
||||
import { CreateShortcutResponse, OpenGraphMetadata } from "@/types/proto/api/v2/shortcut_service";
|
||||
import { Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
||||
import Icon from "./Icon";
|
||||
|
||||
const generateName = (length = 8) => {
|
||||
let result = "";
|
||||
const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
const charactersLength = characters.length;
|
||||
let counter = 0;
|
||||
while (counter < length) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
counter += 1;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
interface State {
|
||||
name: string;
|
||||
title: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
const CreateShortcutsButton = () => {
|
||||
const [domain] = useStorage("domain");
|
||||
const CreateShortcutButton = () => {
|
||||
const [instanceUrl] = useStorage("domain");
|
||||
const [accessToken] = useStorage("access_token");
|
||||
const [shortcuts, setShortcuts] = useStorage("shortcuts");
|
||||
const shortcutStore = useShortcutStore();
|
||||
const [state, setState] = useState<State>({
|
||||
name: "",
|
||||
title: "",
|
||||
@ -54,7 +42,7 @@ const CreateShortcutsButton = () => {
|
||||
const tab = tabs[0];
|
||||
setState((state) => ({
|
||||
...state,
|
||||
name: generateName(),
|
||||
name: "",
|
||||
title: tab.title || "",
|
||||
link: tab.url || "",
|
||||
}));
|
||||
@ -94,25 +82,16 @@ const CreateShortcutsButton = () => {
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const {
|
||||
data: { shortcut },
|
||||
} = await axios.post<CreateShortcutResponse>(
|
||||
`${domain}/api/v2/shortcuts`,
|
||||
{
|
||||
await shortcutStore.createShortcut(
|
||||
instanceUrl,
|
||||
accessToken,
|
||||
Shortcut.fromPartial({
|
||||
name: state.name,
|
||||
title: state.title,
|
||||
link: state.link,
|
||||
visibility: Visibility.PRIVATE,
|
||||
ogMetadata: OpenGraphMetadata.fromPartial({}),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
}
|
||||
visibility: Visibility.PUBLIC,
|
||||
})
|
||||
);
|
||||
|
||||
setShortcuts([shortcut, ...shortcuts]);
|
||||
toast.success("Shortcut created successfully");
|
||||
setShowModal(false);
|
||||
} catch (error: any) {
|
||||
@ -171,4 +150,4 @@ const CreateShortcutsButton = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateShortcutsButton;
|
||||
export default CreateShortcutButton;
|
@ -1,32 +1,24 @@
|
||||
import { IconButton } from "@mui/joy";
|
||||
import { useStorage } from "@plasmohq/storage/hook";
|
||||
import axios from "axios";
|
||||
import { useEffect } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { ListShortcutsResponse } from "@/types/proto/api/v2/shortcut_service";
|
||||
import useShortcutStore from "@/store/shortcut";
|
||||
import Icon from "./Icon";
|
||||
|
||||
const PullShortcutsButton = () => {
|
||||
const [domain] = useStorage("domain");
|
||||
const [instanceUrl] = useStorage("domain");
|
||||
const [accessToken] = useStorage("access_token");
|
||||
const [, setShortcuts] = useStorage("shortcuts");
|
||||
const shortcutStore = useShortcutStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (domain && accessToken) {
|
||||
if (instanceUrl && accessToken) {
|
||||
handlePullShortcuts(true);
|
||||
}
|
||||
}, [domain, accessToken]);
|
||||
}, [instanceUrl, accessToken]);
|
||||
|
||||
const handlePullShortcuts = async (silence = false) => {
|
||||
try {
|
||||
const {
|
||||
data: { shortcuts },
|
||||
} = await axios.get<ListShortcutsResponse>(`${domain}/api/v2/shortcuts`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
});
|
||||
setShortcuts(shortcuts);
|
||||
await shortcutStore.fetchShortcutList(instanceUrl, accessToken);
|
||||
if (!silence) {
|
||||
toast.success("Shortcuts pulled");
|
||||
}
|
||||
|
@ -1,16 +1,24 @@
|
||||
import { useStorage } from "@plasmohq/storage/hook";
|
||||
import classNames from "classnames";
|
||||
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
||||
import useShortcutStore from "@/store/shortcut";
|
||||
import Icon from "./Icon";
|
||||
import ShortcutView from "./ShortcutView";
|
||||
|
||||
const ShortcutsContainer = () => {
|
||||
const [shortcuts] = useStorage<Shortcut[]>("shortcuts", (v) => (v ? v : []));
|
||||
const shortcuts = useShortcutStore().getShortcutList();
|
||||
|
||||
return (
|
||||
<div className={classNames("w-full grid grid-cols-2 gap-2")}>
|
||||
{shortcuts.map((shortcut) => {
|
||||
return <ShortcutView key={shortcut.id} shortcut={shortcut} />;
|
||||
})}
|
||||
<div>
|
||||
<div className="w-full flex flex-row justify-start items-center mb-4">
|
||||
<a className="bg-blue-100 dark:bg-blue-500 dark:opacity-70 py-2 px-3 rounded-full border dark:border-blue-600 flex flex-row justify-start items-center cursor-pointer shadow">
|
||||
<Icon.AlertCircle className="w-4 h-auto" />
|
||||
<span className="mx-1 text-sm">Please make sure you have signed in your instance.</span>
|
||||
</a>
|
||||
</div>
|
||||
<div className={classNames("w-full grid grid-cols-2 gap-2")}>
|
||||
{shortcuts.map((shortcut) => {
|
||||
return <ShortcutView key={shortcut.id} shortcut={shortcut} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -2,12 +2,12 @@ import { Button, CssVarsProvider, Divider, Input, Select, Option } from "@mui/jo
|
||||
import { useStorage } from "@plasmohq/storage/hook";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Toaster, toast } from "react-hot-toast";
|
||||
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
||||
import Icon from "./components/Icon";
|
||||
import Logo from "./components/Logo";
|
||||
import PullShortcutsButton from "./components/PullShortcutsButton";
|
||||
import ShortcutsContainer from "./components/ShortcutsContainer";
|
||||
import useColorTheme from "./hooks/useColorTheme";
|
||||
import useShortcutStore from "./store/shortcut";
|
||||
import "./style.css";
|
||||
|
||||
interface SettingState {
|
||||
@ -38,7 +38,8 @@ const IndexOptions = () => {
|
||||
domain,
|
||||
accessToken,
|
||||
});
|
||||
const [shortcuts] = useStorage<Shortcut[]>("shortcuts", []);
|
||||
const shortcutStore = useShortcutStore();
|
||||
const shortcuts = shortcutStore.getShortcutList();
|
||||
const isInitialized = domain && accessToken;
|
||||
|
||||
useEffect(() => {
|
||||
@ -66,7 +67,7 @@ const IndexOptions = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="w-full px-4">
|
||||
<div className="w-full flex flex-row justify-center items-center">
|
||||
<a
|
||||
className="bg-yellow-100 dark:bg-yellow-500 dark:opacity-70 mt-12 py-2 px-3 rounded-full border dark:border-yellow-600 flex flex-row justify-start items-center cursor-pointer shadow hover:underline hover:text-blue-600"
|
||||
@ -93,7 +94,7 @@ const IndexOptions = () => {
|
||||
<span className="dark:text-gray-400">Instance URL</span>
|
||||
{domain !== "" && (
|
||||
<a
|
||||
className="text-sm flex flex-row justify-start items-center dark:text-gray-400 hover:underline hover:text-blue-600"
|
||||
className="text-sm flex flex-row justify-start items-center underline text-blue-600 hover:opacity-80"
|
||||
href={domain}
|
||||
target="_blank"
|
||||
>
|
||||
|
@ -1,21 +1,31 @@
|
||||
import { Button, CssVarsProvider, Divider, IconButton } from "@mui/joy";
|
||||
import { useStorage } from "@plasmohq/storage/hook";
|
||||
import { useEffect } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import CreateShortcutsButton from "@/components/CreateShortcutsButton";
|
||||
import CreateShortcutButton from "@/components/CreateShortcutButton";
|
||||
import Icon from "@/components/Icon";
|
||||
import Logo from "@/components/Logo";
|
||||
import PullShortcutsButton from "@/components/PullShortcutsButton";
|
||||
import ShortcutsContainer from "@/components/ShortcutsContainer";
|
||||
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
||||
import useColorTheme from "./hooks/useColorTheme";
|
||||
import useShortcutStore from "./store/shortcut";
|
||||
import "./style.css";
|
||||
|
||||
const IndexPopup = () => {
|
||||
useColorTheme();
|
||||
const [domain] = useStorage<string>("domain", "");
|
||||
const [instanceUrl] = useStorage<string>("domain", "");
|
||||
const [accessToken] = useStorage<string>("access_token", "");
|
||||
const [shortcuts] = useStorage<Shortcut[]>("shortcuts", []);
|
||||
const isInitialized = domain && accessToken;
|
||||
const shortcutStore = useShortcutStore();
|
||||
const shortcuts = shortcutStore.getShortcutList();
|
||||
const isInitialized = instanceUrl && accessToken;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
shortcutStore.fetchShortcutList(instanceUrl, accessToken);
|
||||
}, [isInitialized]);
|
||||
|
||||
const handleSettingButtonClick = () => {
|
||||
chrome.runtime.openOptionsPage();
|
||||
@ -41,7 +51,7 @@ const IndexPopup = () => {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div>{isInitialized && <CreateShortcutsButton />}</div>
|
||||
<div>{isInitialized && <CreateShortcutButton />}</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full mt-4">
|
||||
@ -75,8 +85,8 @@ const IndexPopup = () => {
|
||||
</div>
|
||||
<div className="flex flex-row justify-end items-center">
|
||||
<a
|
||||
className="text-sm flex flex-row justify-start items-center text-gray-500 dark:text-gray-400 hover:underline hover:text-blue-600"
|
||||
href={domain}
|
||||
className="text-sm flex flex-row justify-start items-center underline text-blue-600 hover:opacity-80"
|
||||
href={instanceUrl}
|
||||
target="_blank"
|
||||
>
|
||||
<span className="mr-1">Go to my Slash</span>
|
||||
|
55
frontend/extension/src/store/shortcut.ts
Normal file
55
frontend/extension/src/store/shortcut.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import axios from "axios";
|
||||
import { create } from "zustand";
|
||||
import { combine } from "zustand/middleware";
|
||||
import { CreateShortcutResponse, ListShortcutsResponse, Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
||||
|
||||
interface State {
|
||||
shortcutMapById: Record<number, Shortcut>;
|
||||
}
|
||||
|
||||
const getDefaultState = (): State => {
|
||||
return {
|
||||
shortcutMapById: {},
|
||||
};
|
||||
};
|
||||
|
||||
const useShortcutStore = create(
|
||||
combine(getDefaultState(), (set, get) => ({
|
||||
fetchShortcutList: async (instanceUrl: string, accessToken: string) => {
|
||||
const {
|
||||
data: { shortcuts },
|
||||
} = await axios.get<ListShortcutsResponse>(`${instanceUrl}/api/v2/shortcuts`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
});
|
||||
const shortcutMap = get().shortcutMapById;
|
||||
shortcuts.forEach((shortcut) => {
|
||||
shortcutMap[shortcut.id] = shortcut;
|
||||
});
|
||||
set({ shortcutMapById: shortcutMap });
|
||||
return shortcuts;
|
||||
},
|
||||
getShortcutList: () => {
|
||||
return Object.values(get().shortcutMapById);
|
||||
},
|
||||
createShortcut: async (instanceUrl: string, accessToken: string, create: Shortcut) => {
|
||||
const {
|
||||
data: { shortcut },
|
||||
} = await axios.post<CreateShortcutResponse>(`${instanceUrl}/api/v2/shortcuts`, create, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
});
|
||||
if (!shortcut) {
|
||||
throw new Error(`Failed to create shortcut`);
|
||||
}
|
||||
const shortcutMap = get().shortcutMapById;
|
||||
shortcutMap[shortcut.id] = shortcut;
|
||||
set({ shortcutMapById: shortcutMap });
|
||||
return shortcut;
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
export default useShortcutStore;
|
Loading…
x
Reference in New Issue
Block a user