mirror of
https://github.com/aykhans/slash-e.git
synced 2025-04-16 04:13:12 +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 { useStorage } from "@plasmohq/storage/hook";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useStorageContext } from "@/context";
|
||||
import useShortcutStore from "@/store/shortcut";
|
||||
import type { Visibility } from "@/types/proto/api/v1/common";
|
||||
import { Shortcut } from "@/types/proto/api/v1/shortcut_service";
|
||||
import Icon from "./Icon";
|
||||
|
||||
@ -13,8 +14,7 @@ interface State {
|
||||
}
|
||||
|
||||
const CreateShortcutButton = () => {
|
||||
const [instanceUrl] = useStorage("domain");
|
||||
const [accessToken] = useStorage("access_token");
|
||||
const context = useStorageContext();
|
||||
const shortcutStore = useShortcutStore();
|
||||
const [state, setState] = useState<State>({
|
||||
name: "",
|
||||
@ -101,13 +101,14 @@ const CreateShortcutButton = () => {
|
||||
try {
|
||||
const tags = tag.split(" ").filter(Boolean);
|
||||
await shortcutStore.createShortcut(
|
||||
instanceUrl,
|
||||
accessToken,
|
||||
context.instanceUrl,
|
||||
context.accessToken,
|
||||
Shortcut.fromPartial({
|
||||
name: state.name,
|
||||
title: state.title,
|
||||
link: state.link,
|
||||
tags,
|
||||
visibility: context.defaultVisibility as Visibility,
|
||||
})
|
||||
);
|
||||
toast.success("Shortcut created successfully");
|
||||
|
@ -1,24 +1,23 @@
|
||||
import { IconButton } from "@mui/joy";
|
||||
import { useStorage } from "@plasmohq/storage/hook";
|
||||
import { useEffect } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useStorageContext } from "@/context";
|
||||
import useShortcutStore from "@/store/shortcut";
|
||||
import Icon from "./Icon";
|
||||
|
||||
const PullShortcutsButton = () => {
|
||||
const [instanceUrl] = useStorage("domain");
|
||||
const [accessToken] = useStorage("access_token");
|
||||
const context = useStorageContext();
|
||||
const shortcutStore = useShortcutStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (instanceUrl && accessToken) {
|
||||
if (context.instanceUrl && context.accessToken) {
|
||||
handlePullShortcuts(true);
|
||||
}
|
||||
}, [instanceUrl, accessToken]);
|
||||
}, [context]);
|
||||
|
||||
const handlePullShortcuts = async (silence = false) => {
|
||||
try {
|
||||
await shortcutStore.fetchShortcutList(instanceUrl, accessToken);
|
||||
await shortcutStore.fetchShortcutList(context.instanceUrl, context.accessToken);
|
||||
if (!silence) {
|
||||
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 { useStorage } from "@plasmohq/storage/hook";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Toaster, toast } from "react-hot-toast";
|
||||
import Icon from "./components/Icon";
|
||||
import Logo from "./components/Logo";
|
||||
import PullShortcutsButton from "./components/PullShortcutsButton";
|
||||
import ShortcutsContainer from "./components/ShortcutsContainer";
|
||||
import { StorageContextProvider, useStorageContext } from "./context";
|
||||
import useColorTheme from "./hooks/useColorTheme";
|
||||
import useShortcutStore from "./store/shortcut";
|
||||
import "./style.css";
|
||||
import { Visibility } from "./types/proto/api/v1/common";
|
||||
|
||||
interface SettingState {
|
||||
domain: string;
|
||||
@ -32,22 +33,21 @@ const colorThemeOptions = [
|
||||
|
||||
const IndexOptions = () => {
|
||||
const { colorTheme, setColorTheme } = useColorTheme();
|
||||
const [domain, setDomain] = useStorage<string>("domain", (v) => (v ? v : ""));
|
||||
const [accessToken, setAccessToken] = useStorage<string>("access_token", (v) => (v ? v : ""));
|
||||
const context = useStorageContext();
|
||||
const [settingState, setSettingState] = useState<SettingState>({
|
||||
domain,
|
||||
accessToken,
|
||||
domain: context.instanceUrl || "",
|
||||
accessToken: context.accessToken || "",
|
||||
});
|
||||
const shortcutStore = useShortcutStore();
|
||||
const shortcuts = shortcutStore.getShortcutList();
|
||||
const isInitialized = domain && accessToken;
|
||||
const isInitialized = context.instanceUrl && context.accessToken;
|
||||
|
||||
useEffect(() => {
|
||||
setSettingState({
|
||||
domain,
|
||||
accessToken,
|
||||
domain: context.instanceUrl || "",
|
||||
accessToken: context.accessToken || "",
|
||||
});
|
||||
}, [domain, accessToken]);
|
||||
}, [context]);
|
||||
|
||||
const setPartialSettingState = (partialSettingState: Partial<SettingState>) => {
|
||||
setSettingState((prevState) => ({
|
||||
@ -57,8 +57,8 @@ const IndexOptions = () => {
|
||||
};
|
||||
|
||||
const handleSaveSetting = () => {
|
||||
setDomain(settingState.domain);
|
||||
setAccessToken(settingState.accessToken);
|
||||
context.setInstanceUrl(settingState.domain);
|
||||
context.setAccessToken(settingState.accessToken);
|
||||
toast.success("Setting saved");
|
||||
};
|
||||
|
||||
@ -66,6 +66,10 @@ const IndexOptions = () => {
|
||||
setColorTheme(colorTheme as any);
|
||||
};
|
||||
|
||||
const handleDefaultVisibilitySelect = (value: Visibility) => {
|
||||
context.setDefaultVisibility(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full px-4">
|
||||
<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="mb-2 text-base w-full flex flex-row justify-between items-center">
|
||||
<span className="dark:text-gray-400">Instance URL</span>
|
||||
{domain !== "" && (
|
||||
{context.instanceUrl !== "" && (
|
||||
<a
|
||||
className="text-sm flex flex-row justify-start items-center underline text-blue-600 hover:opacity-80"
|
||||
href={domain}
|
||||
href={context.instanceUrl}
|
||||
target="_blank"
|
||||
>
|
||||
<span className="mr-1">Go to my Slash</span>
|
||||
@ -133,9 +137,9 @@ const IndexOptions = () => {
|
||||
|
||||
<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">
|
||||
<span className="dark:text-gray-400">Color Theme</span>
|
||||
</div>
|
||||
@ -149,6 +153,17 @@ const IndexOptions = () => {
|
||||
})}
|
||||
</Select>
|
||||
</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>
|
||||
|
||||
{isInitialized && (
|
||||
@ -170,10 +185,12 @@ const IndexOptions = () => {
|
||||
|
||||
const Options = () => {
|
||||
return (
|
||||
<CssVarsProvider>
|
||||
<IndexOptions />
|
||||
<Toaster position="top-center" />
|
||||
</CssVarsProvider>
|
||||
<StorageContextProvider>
|
||||
<CssVarsProvider>
|
||||
<IndexOptions />
|
||||
<Toaster position="top-center" />
|
||||
</CssVarsProvider>
|
||||
</StorageContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
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 CreateShortcutButton from "@/components/CreateShortcutButton";
|
||||
@ -7,24 +6,24 @@ import Icon from "@/components/Icon";
|
||||
import Logo from "@/components/Logo";
|
||||
import PullShortcutsButton from "@/components/PullShortcutsButton";
|
||||
import ShortcutsContainer from "@/components/ShortcutsContainer";
|
||||
import { StorageContextProvider, useStorageContext } from "./context";
|
||||
import useColorTheme from "./hooks/useColorTheme";
|
||||
import useShortcutStore from "./store/shortcut";
|
||||
import "./style.css";
|
||||
|
||||
const IndexPopup = () => {
|
||||
useColorTheme();
|
||||
const [instanceUrl] = useStorage<string>("domain", "");
|
||||
const [accessToken] = useStorage<string>("access_token", "");
|
||||
const context = useStorageContext();
|
||||
const shortcutStore = useShortcutStore();
|
||||
const shortcuts = shortcutStore.getShortcutList();
|
||||
const isInitialized = instanceUrl && accessToken;
|
||||
const isInitialized = context.instanceUrl && context.accessToken;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
shortcutStore.fetchShortcutList(instanceUrl, accessToken);
|
||||
shortcutStore.fetchShortcutList(context.instanceUrl, context.accessToken);
|
||||
}, [isInitialized]);
|
||||
|
||||
const handleSettingButtonClick = () => {
|
||||
@ -86,7 +85,7 @@ const IndexPopup = () => {
|
||||
<div className="flex flex-row justify-end items-center">
|
||||
<a
|
||||
className="text-sm flex flex-row justify-start items-center underline text-blue-600 hover:opacity-80"
|
||||
href={instanceUrl}
|
||||
href={context.instanceUrl}
|
||||
target="_blank"
|
||||
>
|
||||
<span className="mr-1">Go to my Slash</span>
|
||||
@ -117,10 +116,12 @@ const IndexPopup = () => {
|
||||
|
||||
const Popup = () => {
|
||||
return (
|
||||
<CssVarsProvider>
|
||||
<IndexPopup />
|
||||
<Toaster position="top-center" />
|
||||
</CssVarsProvider>
|
||||
<StorageContextProvider>
|
||||
<CssVarsProvider>
|
||||
<IndexPopup />
|
||||
<Toaster position="top-center" />
|
||||
</CssVarsProvider>
|
||||
</StorageContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user