mirror of
https://github.com/aykhans/slash-e.git
synced 2025-04-17 04:43:13 +00:00
feat: add default visibility setting to extension
This commit is contained in:
parent
c6821a7090
commit
e2c7b8c7b9
@ -1,8 +1,9 @@
|
|||||||
import { Button, IconButton, Input, Modal, ModalDialog } from "@mui/joy";
|
import { Button, IconButton, Input, Modal, ModalDialog } from "@mui/joy";
|
||||||
import { useStorage } from "@plasmohq/storage/hook";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
|
import { useStorageContext } from "@/context";
|
||||||
import useShortcutStore from "@/store/shortcut";
|
import useShortcutStore from "@/store/shortcut";
|
||||||
|
import type { Visibility } from "@/types/proto/api/v1/common";
|
||||||
import { Shortcut } from "@/types/proto/api/v1/shortcut_service";
|
import { Shortcut } from "@/types/proto/api/v1/shortcut_service";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
|
||||||
@ -13,8 +14,7 @@ interface State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CreateShortcutButton = () => {
|
const CreateShortcutButton = () => {
|
||||||
const [instanceUrl] = useStorage("domain");
|
const context = useStorageContext();
|
||||||
const [accessToken] = useStorage("access_token");
|
|
||||||
const shortcutStore = useShortcutStore();
|
const shortcutStore = useShortcutStore();
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
name: "",
|
name: "",
|
||||||
@ -101,13 +101,14 @@ const CreateShortcutButton = () => {
|
|||||||
try {
|
try {
|
||||||
const tags = tag.split(" ").filter(Boolean);
|
const tags = tag.split(" ").filter(Boolean);
|
||||||
await shortcutStore.createShortcut(
|
await shortcutStore.createShortcut(
|
||||||
instanceUrl,
|
context.instanceUrl,
|
||||||
accessToken,
|
context.accessToken,
|
||||||
Shortcut.fromPartial({
|
Shortcut.fromPartial({
|
||||||
name: state.name,
|
name: state.name,
|
||||||
title: state.title,
|
title: state.title,
|
||||||
link: state.link,
|
link: state.link,
|
||||||
tags,
|
tags,
|
||||||
|
visibility: context.defaultVisibility as Visibility,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
toast.success("Shortcut created successfully");
|
toast.success("Shortcut created successfully");
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
import { IconButton } from "@mui/joy";
|
import { IconButton } from "@mui/joy";
|
||||||
import { useStorage } from "@plasmohq/storage/hook";
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
|
import { useStorageContext } from "@/context";
|
||||||
import useShortcutStore from "@/store/shortcut";
|
import useShortcutStore from "@/store/shortcut";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
|
||||||
const PullShortcutsButton = () => {
|
const PullShortcutsButton = () => {
|
||||||
const [instanceUrl] = useStorage("domain");
|
const context = useStorageContext();
|
||||||
const [accessToken] = useStorage("access_token");
|
|
||||||
const shortcutStore = useShortcutStore();
|
const shortcutStore = useShortcutStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (instanceUrl && accessToken) {
|
if (context.instanceUrl && context.accessToken) {
|
||||||
handlePullShortcuts(true);
|
handlePullShortcuts(true);
|
||||||
}
|
}
|
||||||
}, [instanceUrl, accessToken]);
|
}, [context]);
|
||||||
|
|
||||||
const handlePullShortcuts = async (silence = false) => {
|
const handlePullShortcuts = async (silence = false) => {
|
||||||
try {
|
try {
|
||||||
await shortcutStore.fetchShortcutList(instanceUrl, accessToken);
|
await shortcutStore.fetchShortcutList(context.instanceUrl, context.accessToken);
|
||||||
if (!silence) {
|
if (!silence) {
|
||||||
toast.success("Shortcuts pulled");
|
toast.success("Shortcuts pulled");
|
||||||
}
|
}
|
||||||
|
27
frontend/extension/src/context/context.ts
Normal file
27
frontend/extension/src/context/context.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { createContext, useContext } from "react";
|
||||||
|
import { Visibility } from "@/types/proto/api/v1/common";
|
||||||
|
|
||||||
|
interface Context {
|
||||||
|
instanceUrl?: string;
|
||||||
|
accessToken?: string;
|
||||||
|
defaultVisibility: string;
|
||||||
|
setInstanceUrl: (instanceUrl: string) => void;
|
||||||
|
setAccessToken: (accessToken: string) => void;
|
||||||
|
setDefaultVisibility: (visibility: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StorageContext = createContext<Context>({
|
||||||
|
instanceUrl: undefined,
|
||||||
|
accessToken: undefined,
|
||||||
|
defaultVisibility: Visibility.PRIVATE,
|
||||||
|
setInstanceUrl: () => {},
|
||||||
|
setAccessToken: () => {},
|
||||||
|
setDefaultVisibility: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const useStorageContext = () => {
|
||||||
|
const context = useContext(StorageContext);
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useStorageContext;
|
4
frontend/extension/src/context/index.ts
Normal file
4
frontend/extension/src/context/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import useStorageContext from "./context";
|
||||||
|
import StorageContextProvider from "./provider";
|
||||||
|
|
||||||
|
export { useStorageContext, StorageContextProvider };
|
58
frontend/extension/src/context/provider.tsx
Normal file
58
frontend/extension/src/context/provider.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { Storage } from "@plasmohq/storage";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Visibility } from "@/types/proto/api/v1/common";
|
||||||
|
import { StorageContext } from "./context";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StorageContextProvider = ({ children }: Props) => {
|
||||||
|
const storage = new Storage();
|
||||||
|
const [instanceUrl, setInstanceUrl] = useState<string | undefined>(undefined);
|
||||||
|
const [accessToken, setAccessToken] = useState<string | undefined>(undefined);
|
||||||
|
const [defaultVisibility, setDefaultVisibility] = useState<Visibility>(Visibility.PRIVATE);
|
||||||
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const domain = await storage.get("domain");
|
||||||
|
const accessToken = await storage.get("access_token");
|
||||||
|
const defaultVisibility = (await storage.get("default_visibility")) as Visibility;
|
||||||
|
|
||||||
|
setInstanceUrl(domain);
|
||||||
|
setAccessToken(accessToken);
|
||||||
|
setDefaultVisibility(defaultVisibility);
|
||||||
|
setIsInitialized(true);
|
||||||
|
})();
|
||||||
|
|
||||||
|
storage.watch({
|
||||||
|
domain: (c) => {
|
||||||
|
setInstanceUrl(c.newValue);
|
||||||
|
},
|
||||||
|
access_token: (c) => {
|
||||||
|
setAccessToken(c.newValue);
|
||||||
|
},
|
||||||
|
default_visibility: (c) => {
|
||||||
|
setDefaultVisibility(c.newValue as Visibility);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StorageContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceUrl,
|
||||||
|
accessToken,
|
||||||
|
defaultVisibility,
|
||||||
|
setInstanceUrl: (instanceUrl: string) => storage.set("domain", instanceUrl),
|
||||||
|
setAccessToken: (accessToken: string) => storage.set("access_token", accessToken),
|
||||||
|
setDefaultVisibility: (visibility: Visibility) => storage.set("default_visibility", visibility),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isInitialized && children}
|
||||||
|
</StorageContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StorageContextProvider;
|
@ -1,14 +1,15 @@
|
|||||||
import { Button, CssVarsProvider, Divider, Input, Select, Option } from "@mui/joy";
|
import { Button, CssVarsProvider, Divider, Input, Select, Option } from "@mui/joy";
|
||||||
import { useStorage } from "@plasmohq/storage/hook";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Toaster, toast } from "react-hot-toast";
|
import { Toaster, toast } from "react-hot-toast";
|
||||||
import Icon from "./components/Icon";
|
import Icon from "./components/Icon";
|
||||||
import Logo from "./components/Logo";
|
import Logo from "./components/Logo";
|
||||||
import PullShortcutsButton from "./components/PullShortcutsButton";
|
import PullShortcutsButton from "./components/PullShortcutsButton";
|
||||||
import ShortcutsContainer from "./components/ShortcutsContainer";
|
import ShortcutsContainer from "./components/ShortcutsContainer";
|
||||||
|
import { StorageContextProvider, useStorageContext } from "./context";
|
||||||
import useColorTheme from "./hooks/useColorTheme";
|
import useColorTheme from "./hooks/useColorTheme";
|
||||||
import useShortcutStore from "./store/shortcut";
|
import useShortcutStore from "./store/shortcut";
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
import { Visibility } from "./types/proto/api/v1/common";
|
||||||
|
|
||||||
interface SettingState {
|
interface SettingState {
|
||||||
domain: string;
|
domain: string;
|
||||||
@ -32,22 +33,21 @@ const colorThemeOptions = [
|
|||||||
|
|
||||||
const IndexOptions = () => {
|
const IndexOptions = () => {
|
||||||
const { colorTheme, setColorTheme } = useColorTheme();
|
const { colorTheme, setColorTheme } = useColorTheme();
|
||||||
const [domain, setDomain] = useStorage<string>("domain", (v) => (v ? v : ""));
|
const context = useStorageContext();
|
||||||
const [accessToken, setAccessToken] = useStorage<string>("access_token", (v) => (v ? v : ""));
|
|
||||||
const [settingState, setSettingState] = useState<SettingState>({
|
const [settingState, setSettingState] = useState<SettingState>({
|
||||||
domain,
|
domain: context.instanceUrl || "",
|
||||||
accessToken,
|
accessToken: context.accessToken || "",
|
||||||
});
|
});
|
||||||
const shortcutStore = useShortcutStore();
|
const shortcutStore = useShortcutStore();
|
||||||
const shortcuts = shortcutStore.getShortcutList();
|
const shortcuts = shortcutStore.getShortcutList();
|
||||||
const isInitialized = domain && accessToken;
|
const isInitialized = context.instanceUrl && context.accessToken;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSettingState({
|
setSettingState({
|
||||||
domain,
|
domain: context.instanceUrl || "",
|
||||||
accessToken,
|
accessToken: context.accessToken || "",
|
||||||
});
|
});
|
||||||
}, [domain, accessToken]);
|
}, [context]);
|
||||||
|
|
||||||
const setPartialSettingState = (partialSettingState: Partial<SettingState>) => {
|
const setPartialSettingState = (partialSettingState: Partial<SettingState>) => {
|
||||||
setSettingState((prevState) => ({
|
setSettingState((prevState) => ({
|
||||||
@ -57,8 +57,8 @@ const IndexOptions = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveSetting = () => {
|
const handleSaveSetting = () => {
|
||||||
setDomain(settingState.domain);
|
context.setInstanceUrl(settingState.domain);
|
||||||
setAccessToken(settingState.accessToken);
|
context.setAccessToken(settingState.accessToken);
|
||||||
toast.success("Setting saved");
|
toast.success("Setting saved");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -66,6 +66,10 @@ const IndexOptions = () => {
|
|||||||
setColorTheme(colorTheme as any);
|
setColorTheme(colorTheme as any);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDefaultVisibilitySelect = (value: Visibility) => {
|
||||||
|
context.setDefaultVisibility(value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full px-4">
|
<div className="w-full px-4">
|
||||||
<div className="w-full flex flex-row justify-center items-center">
|
<div className="w-full flex flex-row justify-center items-center">
|
||||||
@ -92,10 +96,10 @@ const IndexOptions = () => {
|
|||||||
<div className="w-full flex flex-col justify-start items-start mb-4">
|
<div className="w-full flex flex-col justify-start items-start mb-4">
|
||||||
<div className="mb-2 text-base w-full flex flex-row justify-between items-center">
|
<div className="mb-2 text-base w-full flex flex-row justify-between items-center">
|
||||||
<span className="dark:text-gray-400">Instance URL</span>
|
<span className="dark:text-gray-400">Instance URL</span>
|
||||||
{domain !== "" && (
|
{context.instanceUrl !== "" && (
|
||||||
<a
|
<a
|
||||||
className="text-sm flex flex-row justify-start items-center underline text-blue-600 hover:opacity-80"
|
className="text-sm flex flex-row justify-start items-center underline text-blue-600 hover:opacity-80"
|
||||||
href={domain}
|
href={context.instanceUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<span className="mr-1">Go to my Slash</span>
|
<span className="mr-1">Go to my Slash</span>
|
||||||
@ -133,9 +137,9 @@ const IndexOptions = () => {
|
|||||||
|
|
||||||
<Divider className="!my-6" />
|
<Divider className="!my-6" />
|
||||||
|
|
||||||
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Preference</p>
|
<p className="text-base font-semibold leading-6 mb-2 text-gray-900 dark:text-gray-500">Preference</p>
|
||||||
|
|
||||||
<div className="w-full flex flex-row justify-between items-center">
|
<div className="w-full flex flex-row justify-between items-center mb-2">
|
||||||
<div className="flex flex-row justify-start items-center gap-x-1">
|
<div className="flex flex-row justify-start items-center gap-x-1">
|
||||||
<span className="dark:text-gray-400">Color Theme</span>
|
<span className="dark:text-gray-400">Color Theme</span>
|
||||||
</div>
|
</div>
|
||||||
@ -149,6 +153,17 @@ const IndexOptions = () => {
|
|||||||
})}
|
})}
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full flex flex-row justify-between items-center">
|
||||||
|
<div className="flex flex-row justify-start items-center gap-x-1">
|
||||||
|
<span className="dark:text-gray-400">Default Visibility</span>
|
||||||
|
</div>
|
||||||
|
<Select defaultValue={context.defaultVisibility} onChange={(_, value) => handleDefaultVisibilitySelect(value as Visibility)}>
|
||||||
|
<Option value={Visibility.PRIVATE}>Private</Option>
|
||||||
|
<Option value={Visibility.WORKSPACE}>Workspace</Option>
|
||||||
|
<Option value={Visibility.PUBLIC}>Public</Option>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isInitialized && (
|
{isInitialized && (
|
||||||
@ -170,10 +185,12 @@ const IndexOptions = () => {
|
|||||||
|
|
||||||
const Options = () => {
|
const Options = () => {
|
||||||
return (
|
return (
|
||||||
<CssVarsProvider>
|
<StorageContextProvider>
|
||||||
<IndexOptions />
|
<CssVarsProvider>
|
||||||
<Toaster position="top-center" />
|
<IndexOptions />
|
||||||
</CssVarsProvider>
|
<Toaster position="top-center" />
|
||||||
|
</CssVarsProvider>
|
||||||
|
</StorageContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Button, CssVarsProvider, Divider, IconButton } from "@mui/joy";
|
import { Button, CssVarsProvider, Divider, IconButton } from "@mui/joy";
|
||||||
import { useStorage } from "@plasmohq/storage/hook";
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Toaster } from "react-hot-toast";
|
import { Toaster } from "react-hot-toast";
|
||||||
import CreateShortcutButton from "@/components/CreateShortcutButton";
|
import CreateShortcutButton from "@/components/CreateShortcutButton";
|
||||||
@ -7,24 +6,24 @@ import Icon from "@/components/Icon";
|
|||||||
import Logo from "@/components/Logo";
|
import Logo from "@/components/Logo";
|
||||||
import PullShortcutsButton from "@/components/PullShortcutsButton";
|
import PullShortcutsButton from "@/components/PullShortcutsButton";
|
||||||
import ShortcutsContainer from "@/components/ShortcutsContainer";
|
import ShortcutsContainer from "@/components/ShortcutsContainer";
|
||||||
|
import { StorageContextProvider, useStorageContext } from "./context";
|
||||||
import useColorTheme from "./hooks/useColorTheme";
|
import useColorTheme from "./hooks/useColorTheme";
|
||||||
import useShortcutStore from "./store/shortcut";
|
import useShortcutStore from "./store/shortcut";
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
|
||||||
const IndexPopup = () => {
|
const IndexPopup = () => {
|
||||||
useColorTheme();
|
useColorTheme();
|
||||||
const [instanceUrl] = useStorage<string>("domain", "");
|
const context = useStorageContext();
|
||||||
const [accessToken] = useStorage<string>("access_token", "");
|
|
||||||
const shortcutStore = useShortcutStore();
|
const shortcutStore = useShortcutStore();
|
||||||
const shortcuts = shortcutStore.getShortcutList();
|
const shortcuts = shortcutStore.getShortcutList();
|
||||||
const isInitialized = instanceUrl && accessToken;
|
const isInitialized = context.instanceUrl && context.accessToken;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isInitialized) {
|
if (!isInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
shortcutStore.fetchShortcutList(instanceUrl, accessToken);
|
shortcutStore.fetchShortcutList(context.instanceUrl, context.accessToken);
|
||||||
}, [isInitialized]);
|
}, [isInitialized]);
|
||||||
|
|
||||||
const handleSettingButtonClick = () => {
|
const handleSettingButtonClick = () => {
|
||||||
@ -86,7 +85,7 @@ const IndexPopup = () => {
|
|||||||
<div className="flex flex-row justify-end items-center">
|
<div className="flex flex-row justify-end items-center">
|
||||||
<a
|
<a
|
||||||
className="text-sm flex flex-row justify-start items-center underline text-blue-600 hover:opacity-80"
|
className="text-sm flex flex-row justify-start items-center underline text-blue-600 hover:opacity-80"
|
||||||
href={instanceUrl}
|
href={context.instanceUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<span className="mr-1">Go to my Slash</span>
|
<span className="mr-1">Go to my Slash</span>
|
||||||
@ -117,10 +116,12 @@ const IndexPopup = () => {
|
|||||||
|
|
||||||
const Popup = () => {
|
const Popup = () => {
|
||||||
return (
|
return (
|
||||||
<CssVarsProvider>
|
<StorageContextProvider>
|
||||||
<IndexPopup />
|
<CssVarsProvider>
|
||||||
<Toaster position="top-center" />
|
<IndexPopup />
|
||||||
</CssVarsProvider>
|
<Toaster position="top-center" />
|
||||||
|
</CssVarsProvider>
|
||||||
|
</StorageContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user