mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-09 12:28:26 +02:00
improve editing and saving app settings
This commit is contained in:
parent
0cc405954f
commit
39ef920289
5
.changeset/slimy-peas-sit.md
Normal file
5
.changeset/slimy-peas-sit.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Improve editing and saving app settings
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
@ -7,7 +8,6 @@ import {
|
|||||||
AccordionIcon,
|
AccordionIcon,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { useState } from "react";
|
|
||||||
import { clearCacheData, deleteDatabase } from "../../services/db";
|
import { clearCacheData, deleteDatabase } from "../../services/db";
|
||||||
|
|
||||||
export default function DatabaseSettings() {
|
export default function DatabaseSettings() {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useFormContext } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
Flex,
|
Flex,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -10,41 +11,11 @@ import {
|
|||||||
AccordionIcon,
|
AccordionIcon,
|
||||||
FormHelperText,
|
FormHelperText,
|
||||||
Input,
|
Input,
|
||||||
InputProps,
|
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { AppSettings } from "../../services/user-app-settings";
|
||||||
import useAppSettings from "../../hooks/use-app-settings";
|
|
||||||
|
|
||||||
function ColorPicker({ value, onPickColor, ...props }: { onPickColor?: (color: string) => void } & InputProps) {
|
|
||||||
const [tmpColor, setTmpColor] = useState(value);
|
|
||||||
const ref = useRef<HTMLInputElement>();
|
|
||||||
|
|
||||||
useEffect(() => setTmpColor(value), [value]);
|
|
||||||
useEffect(() => {
|
|
||||||
if (ref.current) {
|
|
||||||
ref.current.onchange = () => {
|
|
||||||
if (onPickColor && ref.current?.value) {
|
|
||||||
onPickColor(ref.current.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Input
|
|
||||||
{...props}
|
|
||||||
ref={ref}
|
|
||||||
value={tmpColor}
|
|
||||||
onChange={(e) => {
|
|
||||||
setTmpColor(e.target.value);
|
|
||||||
if (props.onChange) props.onChange(e);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DisplaySettings() {
|
export default function DisplaySettings() {
|
||||||
const { blurImages, colorMode, primaryColor, updateSettings, showContentWarning } = useAppSettings();
|
const { register } = useFormContext<AppSettings>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
@ -60,14 +31,10 @@ export default function DisplaySettings() {
|
|||||||
<Flex direction="column" gap="4">
|
<Flex direction="column" gap="4">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<FormLabel htmlFor="use-dark-theme" mb="0">
|
<FormLabel htmlFor="colorMode" mb="0">
|
||||||
Use dark theme
|
Use dark theme
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Switch
|
<Switch id="colorMode" {...register("colorMode")} />
|
||||||
id="use-dark-theme"
|
|
||||||
isChecked={colorMode === "dark"}
|
|
||||||
onChange={(v) => updateSettings({ colorMode: v.target.checked ? "dark" : "light" })}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
<span>Enables hacker mode</span>
|
<span>Enables hacker mode</span>
|
||||||
@ -75,17 +42,10 @@ export default function DisplaySettings() {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<FormLabel htmlFor="primary-color" mb="0">
|
<FormLabel htmlFor="primaryColor" mb="0">
|
||||||
Primary Color
|
Primary Color
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<ColorPicker
|
<Input id="primaryColor" type="color" maxW="120" size="sm" {...register("primaryColor")} />
|
||||||
id="primary-color"
|
|
||||||
type="color"
|
|
||||||
value={primaryColor}
|
|
||||||
onPickColor={(color) => updateSettings({ primaryColor: color })}
|
|
||||||
maxW="120"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
<span>The primary color of the theme</span>
|
<span>The primary color of the theme</span>
|
||||||
@ -93,14 +53,10 @@ export default function DisplaySettings() {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<FormLabel htmlFor="blur-images" mb="0">
|
<FormLabel htmlFor="blurImages" mb="0">
|
||||||
Blur images from strangers
|
Blur images from strangers
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Switch
|
<Switch id="blurImages" {...register("blurImages")} />
|
||||||
id="blur-images"
|
|
||||||
isChecked={blurImages}
|
|
||||||
onChange={(v) => updateSettings({ blurImages: v.target.checked })}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
<span>Enabled: blur images for people you aren't following</span>
|
<span>Enabled: blur images for people you aren't following</span>
|
||||||
@ -111,31 +67,12 @@ export default function DisplaySettings() {
|
|||||||
<FormLabel htmlFor="show-content-warning" mb="0">
|
<FormLabel htmlFor="show-content-warning" mb="0">
|
||||||
Show content warning
|
Show content warning
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Switch
|
<Switch id="show-content-warning" {...register("showContentWarning")} />
|
||||||
id="show-content-warning"
|
|
||||||
isChecked={showContentWarning}
|
|
||||||
onChange={(v) => updateSettings({ showContentWarning: v.target.checked })}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
<span>Enabled: shows a warning for notes with NIP-36 Content Warning</span>
|
<span>Enabled: shows a warning for notes with NIP-36 Content Warning</span>
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl>
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<FormLabel htmlFor="show-ads" mb="0">
|
|
||||||
Show Ads
|
|
||||||
</FormLabel>
|
|
||||||
<Switch
|
|
||||||
id="show-ads"
|
|
||||||
isChecked={false}
|
|
||||||
onChange={(v) => alert("Sorry, that feature will never be finished.")}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<FormHelperText>
|
|
||||||
<span>Enabled: shows ads so I can steal your data</span>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</AccordionPanel>
|
</AccordionPanel>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
@ -1,38 +1,56 @@
|
|||||||
import { Button, Flex, Accordion, Link } from "@chakra-ui/react";
|
import { Button, Flex, Accordion, Link } from "@chakra-ui/react";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
import accountService from "../../services/account";
|
import { GithubIcon, ToolsIcon } from "../../components/icons";
|
||||||
import { GithubIcon, LogoutIcon, ToolsIcon } from "../../components/icons";
|
|
||||||
import LightningSettings from "./lightning-settings";
|
import LightningSettings from "./lightning-settings";
|
||||||
import DatabaseSettings from "./database-settings";
|
import DatabaseSettings from "./database-settings";
|
||||||
import DisplaySettings from "./display-settings";
|
import DisplaySettings from "./display-settings";
|
||||||
import PerformanceSettings from "./performance-settings";
|
import PerformanceSettings from "./performance-settings";
|
||||||
import PrivacySettings from "./privacy-settings";
|
import PrivacySettings from "./privacy-settings";
|
||||||
|
import useAppSettings from "../../hooks/use-app-settings";
|
||||||
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
|
|
||||||
export default function SettingsView() {
|
export default function SettingsView() {
|
||||||
|
const { updateSettings, ...settings } = useAppSettings();
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
mode: "onBlur",
|
||||||
|
values: settings,
|
||||||
|
});
|
||||||
|
|
||||||
|
const saveSettings = form.handleSubmit(async (values) => {
|
||||||
|
await updateSettings(values);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" pt="2" pb="2" overflow="auto">
|
<Flex direction="column" pt="2" pb="2" overflow="auto">
|
||||||
<Accordion defaultIndex={[0]} allowMultiple>
|
<form onSubmit={saveSettings}>
|
||||||
<DisplaySettings />
|
<FormProvider {...form}>
|
||||||
|
<Accordion defaultIndex={[0]} allowMultiple>
|
||||||
<PerformanceSettings />
|
<DisplaySettings />
|
||||||
|
<PerformanceSettings />
|
||||||
<PrivacySettings />
|
<PrivacySettings />
|
||||||
|
<LightningSettings />
|
||||||
<LightningSettings />
|
<DatabaseSettings />
|
||||||
|
</Accordion>
|
||||||
<DatabaseSettings />
|
</FormProvider>
|
||||||
</Accordion>
|
<Flex gap="4" padding="4" alignItems="center">
|
||||||
<Flex gap="2" padding="4" alignItems="center">
|
<Button as={RouterLink} to="/tools" leftIcon={<ToolsIcon />}>
|
||||||
<Button leftIcon={<LogoutIcon />} onClick={() => accountService.logout()}>
|
Tools
|
||||||
Logout
|
</Button>
|
||||||
</Button>
|
<Link isExternal href="https://github.com/hzrd149/nostrudel">
|
||||||
<Button as={RouterLink} to="/tools" leftIcon={<ToolsIcon />}>
|
<GithubIcon /> Github
|
||||||
Tools
|
</Link>
|
||||||
</Button>
|
<Button
|
||||||
<Link isExternal href="https://github.com/hzrd149/nostrudel" ml="auto">
|
ml="auto"
|
||||||
<GithubIcon /> Github
|
isLoading={form.formState.isLoading}
|
||||||
</Link>
|
isDisabled={!form.formState.isDirty}
|
||||||
</Flex>
|
colorScheme="brand"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Save Settings
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</form>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,18 +11,12 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import appSettings, { replaceSettings } from "../../services/app-settings";
|
|
||||||
import useSubject from "../../hooks/use-subject";
|
|
||||||
import { LightningIcon } from "../../components/icons";
|
import { LightningIcon } from "../../components/icons";
|
||||||
import { LightningPayMode } from "../../services/user-app-settings";
|
import { AppSettings } from "../../services/user-app-settings";
|
||||||
import useAppSettings from "../../hooks/use-app-settings";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
export default function LightningSettings() {
|
export default function LightningSettings() {
|
||||||
const { lightningPayMode, zapAmounts, updateSettings } = useAppSettings();
|
const { register } = useFormContext<AppSettings>();
|
||||||
|
|
||||||
const [zapInput, setZapInput] = useState(zapAmounts.join(","));
|
|
||||||
useEffect(() => setZapInput(zapAmounts.join(",")), [zapAmounts.join(",")]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
@ -37,14 +31,10 @@ export default function LightningSettings() {
|
|||||||
<AccordionPanel>
|
<AccordionPanel>
|
||||||
<Flex direction="column" gap="4">
|
<Flex direction="column" gap="4">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<FormLabel htmlFor="lightning-payment-mode" mb="0">
|
<FormLabel htmlFor="lightningPayMode" mb="0">
|
||||||
Payment mode
|
Payment mode
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Select
|
<Select id="lightningPayMode" {...register("lightningPayMode")}>
|
||||||
id="lightning-payment-mode"
|
|
||||||
value={lightningPayMode}
|
|
||||||
onChange={(e) => updateSettings({ lightningPayMode: e.target.value as LightningPayMode })}
|
|
||||||
>
|
|
||||||
<option value="prompt">Prompt</option>
|
<option value="prompt">Prompt</option>
|
||||||
<option value="webln">WebLN</option>
|
<option value="webln">WebLN</option>
|
||||||
<option value="external">External</option>
|
<option value="external">External</option>
|
||||||
@ -64,18 +54,20 @@ export default function LightningSettings() {
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Input
|
<Input
|
||||||
id="zap-amounts"
|
id="zap-amounts"
|
||||||
value={zapInput}
|
autoComplete="off"
|
||||||
onChange={(e) => setZapInput(e.target.value)}
|
{...register("zapAmounts", {
|
||||||
onBlur={() => {
|
setValueAs: (value: number[] | string) => {
|
||||||
const amounts = zapInput
|
if (Array.isArray(value)) {
|
||||||
.split(",")
|
return Array.from(value).join(",");
|
||||||
.map((v) => parseInt(v))
|
} else {
|
||||||
.filter(Boolean)
|
return value
|
||||||
.sort((a, b) => a - b);
|
.split(",")
|
||||||
|
.map((v) => parseInt(v))
|
||||||
updateSettings({ zapAmounts: amounts });
|
.filter(Boolean)
|
||||||
setZapInput(amounts.join(","));
|
.sort((a, b) => a - b);
|
||||||
}}
|
}
|
||||||
|
},
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
<span>Comma separated list of custom zap amounts</span>
|
<span>Comma separated list of custom zap amounts</span>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useFormContext } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
Flex,
|
Flex,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -11,16 +12,13 @@ import {
|
|||||||
FormHelperText,
|
FormHelperText,
|
||||||
Input,
|
Input,
|
||||||
Link,
|
Link,
|
||||||
|
FormErrorMessage,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import useAppSettings from "../../hooks/use-app-settings";
|
import { AppSettings } from "../../services/user-app-settings";
|
||||||
import { useEffect, useState } from "react";
|
import { safeUrl } from "../../helpers/parse";
|
||||||
|
|
||||||
export default function PerformanceSettings() {
|
export default function PerformanceSettings() {
|
||||||
const { autoShowMedia, proxyUserMedia, showReactions, showSignatureVerification, updateSettings, imageProxy } =
|
const { register, formState } = useFormContext<AppSettings>();
|
||||||
useAppSettings();
|
|
||||||
|
|
||||||
const [proxyInput, setProxyInput] = useState(imageProxy);
|
|
||||||
useEffect(() => setProxyInput(imageProxy), [imageProxy]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
@ -39,11 +37,7 @@ export default function PerformanceSettings() {
|
|||||||
<FormLabel htmlFor="proxy-user-media" mb="0">
|
<FormLabel htmlFor="proxy-user-media" mb="0">
|
||||||
Proxy user media
|
Proxy user media
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Switch
|
<Switch id="proxy-user-media" {...register("proxyUserMedia")} />
|
||||||
id="proxy-user-media"
|
|
||||||
isChecked={proxyUserMedia}
|
|
||||||
onChange={(v) => updateSettings({ proxyUserMedia: v.target.checked })}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
<span>Enabled: Use media.nostr.band to get smaller profile pictures (saves ~50Mb of data)</span>
|
<span>Enabled: Use media.nostr.band to get smaller profile pictures (saves ~50Mb of data)</span>
|
||||||
@ -52,24 +46,17 @@ export default function PerformanceSettings() {
|
|||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<FormLabel htmlFor="image-proxy" mb="0">
|
<FormLabel htmlFor="imageProxy" mb="0">
|
||||||
Image proxy service
|
Image proxy service
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Input
|
<Input
|
||||||
id="image-proxy"
|
id="imageProxy"
|
||||||
type="url"
|
type="url"
|
||||||
value={proxyInput}
|
{...register("imageProxy", {
|
||||||
onChange={(e) => setProxyInput(e.target.value)}
|
setValueAs: (v) => safeUrl(v) || v,
|
||||||
onBlur={() => {
|
})}
|
||||||
try {
|
|
||||||
const url = proxyInput ? new URL(proxyInput).toString() : "";
|
|
||||||
if (url !== imageProxy) {
|
|
||||||
updateSettings({ imageProxy: url });
|
|
||||||
setProxyInput(url);
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
{formState.errors.imageProxy && <FormErrorMessage>{formState.errors.imageProxy.message}</FormErrorMessage>}
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
<span>
|
<span>
|
||||||
A URL to an instance of{" "}
|
A URL to an instance of{" "}
|
||||||
@ -81,40 +68,28 @@ export default function PerformanceSettings() {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<FormLabel htmlFor="auto-show-embeds" mb="0">
|
<FormLabel htmlFor="autoShowMedia" mb="0">
|
||||||
Automatically show media
|
Show embeds
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Switch
|
<Switch id="autoShowMedia" {...register("autoShowMedia")} />
|
||||||
id="auto-show-embeds"
|
|
||||||
isChecked={autoShowMedia}
|
|
||||||
onChange={(v) => updateSettings({ autoShowMedia: v.target.checked })}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<FormHelperText>Disabled: Images and videos will show expandable buttons</FormHelperText>
|
<FormHelperText>Disabled: Embeds will show an expandable button</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<FormLabel htmlFor="show-reactions" mb="0">
|
<FormLabel htmlFor="showReactions" mb="0">
|
||||||
Show reactions
|
Show reactions
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Switch
|
<Switch id="showReactions" {...register("showReactions")} />
|
||||||
id="show-reactions"
|
|
||||||
isChecked={showReactions}
|
|
||||||
onChange={(v) => updateSettings({ showReactions: v.target.checked })}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<FormHelperText>Enabled: Show reactions on notes</FormHelperText>
|
<FormHelperText>Enabled: Show reactions on notes</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<FormLabel htmlFor="show-sig-verify" mb="0">
|
<FormLabel htmlFor="showSignatureVerification" mb="0">
|
||||||
Show signature verification
|
Show signature verification
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Switch
|
<Switch id="showSignatureVerification" {...register("showSignatureVerification")} />
|
||||||
id="show-sig-verify"
|
|
||||||
isChecked={showSignatureVerification}
|
|
||||||
onChange={(v) => updateSettings({ showSignatureVerification: v.target.checked })}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<FormHelperText>Enabled: show signature verification on notes</FormHelperText>
|
<FormHelperText>Enabled: show signature verification on notes</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
Flex,
|
Flex,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
Switch,
|
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionPanel,
|
AccordionPanel,
|
||||||
AccordionButton,
|
AccordionButton,
|
||||||
@ -11,12 +10,10 @@ import {
|
|||||||
FormHelperText,
|
FormHelperText,
|
||||||
Input,
|
Input,
|
||||||
Link,
|
Link,
|
||||||
Button,
|
|
||||||
FormErrorMessage,
|
FormErrorMessage,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import useAppSettings from "../../hooks/use-app-settings";
|
import { useFormContext } from "react-hook-form";
|
||||||
import { useForm } from "react-hook-form";
|
import { AppSettings } from "../../services/user-app-settings";
|
||||||
import { useAsync } from "react-use";
|
|
||||||
|
|
||||||
async function validateInvidiousUrl(url?: string) {
|
async function validateInvidiousUrl(url?: string) {
|
||||||
if (!url) return true;
|
if (!url) return true;
|
||||||
@ -29,21 +26,7 @@ async function validateInvidiousUrl(url?: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function PrivacySettings() {
|
export default function PrivacySettings() {
|
||||||
const { youtubeRedirect, twitterRedirect, redditRedirect, corsProxy, updateSettings } = useAppSettings();
|
const { register, formState } = useFormContext<AppSettings>();
|
||||||
|
|
||||||
const { register, handleSubmit, formState } = useForm({
|
|
||||||
mode: "onBlur",
|
|
||||||
defaultValues: {
|
|
||||||
youtubeRedirect,
|
|
||||||
twitterRedirect,
|
|
||||||
redditRedirect,
|
|
||||||
corsProxy,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const save = handleSubmit(async (values) => {
|
|
||||||
await updateSettings(values);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
@ -56,88 +39,76 @@ export default function PrivacySettings() {
|
|||||||
</AccordionButton>
|
</AccordionButton>
|
||||||
</h2>
|
</h2>
|
||||||
<AccordionPanel>
|
<AccordionPanel>
|
||||||
<form onSubmit={save}>
|
<Flex direction="column" gap="4">
|
||||||
<Flex direction="column" gap="4">
|
<FormControl isInvalid={!!formState.errors.twitterRedirect}>
|
||||||
<FormControl isInvalid={!!formState.errors.twitterRedirect}>
|
<FormLabel>Nitter instance</FormLabel>
|
||||||
<FormLabel>Nitter instance</FormLabel>
|
<Input type="url" placeholder="https://nitter.net/" {...register("twitterRedirect")} />
|
||||||
<Input type="url" placeholder="https://nitter.net/" {...register("twitterRedirect")} />
|
{formState.errors.twitterRedirect && (
|
||||||
{formState.errors.twitterRedirect && (
|
<FormErrorMessage>{formState.errors.twitterRedirect.message}</FormErrorMessage>
|
||||||
<FormErrorMessage>{formState.errors.twitterRedirect.message}</FormErrorMessage>
|
)}
|
||||||
)}
|
<FormHelperText>
|
||||||
<FormHelperText>
|
Nitter is a privacy focused UI for twitter.{" "}
|
||||||
Nitter is a privacy focused UI for twitter.{" "}
|
<Link href="https://github.com/zedeus/nitter/wiki/Instances" isExternal color="blue.500">
|
||||||
<Link href="https://github.com/zedeus/nitter/wiki/Instances" isExternal color="blue.500">
|
Nitter instances
|
||||||
Nitter instances
|
</Link>
|
||||||
</Link>
|
</FormHelperText>
|
||||||
</FormHelperText>
|
</FormControl>
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormControl isInvalid={!!formState.errors.youtubeRedirect}>
|
<FormControl isInvalid={!!formState.errors.youtubeRedirect}>
|
||||||
<FormLabel>Invidious instance</FormLabel>
|
<FormLabel>Invidious instance</FormLabel>
|
||||||
<Input
|
<Input
|
||||||
type="url"
|
type="url"
|
||||||
placeholder="Invidious instance url"
|
placeholder="Invidious instance url"
|
||||||
{...register("youtubeRedirect", {
|
{...register("youtubeRedirect", {
|
||||||
validate: validateInvidiousUrl,
|
validate: validateInvidiousUrl,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
{formState.errors.youtubeRedirect && (
|
{formState.errors.youtubeRedirect && (
|
||||||
<FormErrorMessage>{formState.errors.youtubeRedirect.message}</FormErrorMessage>
|
<FormErrorMessage>{formState.errors.youtubeRedirect.message}</FormErrorMessage>
|
||||||
)}
|
)}
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
Invidious is a privacy focused UI for youtube.{" "}
|
Invidious is a privacy focused UI for youtube.{" "}
|
||||||
<Link href="https://docs.invidious.io/instances" isExternal color="blue.500">
|
<Link href="https://docs.invidious.io/instances" isExternal color="blue.500">
|
||||||
Invidious instances
|
Invidious instances
|
||||||
</Link>
|
</Link>
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormControl isInvalid={!!formState.errors.redditRedirect}>
|
<FormControl isInvalid={!!formState.errors.redditRedirect}>
|
||||||
<FormLabel>Teddit / Libreddit instance</FormLabel>
|
<FormLabel>Teddit / Libreddit instance</FormLabel>
|
||||||
<Input type="url" placeholder="https://nitter.net/" {...register("redditRedirect")} />
|
<Input type="url" placeholder="https://nitter.net/" {...register("redditRedirect")} />
|
||||||
{formState.errors.redditRedirect && (
|
{formState.errors.redditRedirect && (
|
||||||
<FormErrorMessage>{formState.errors.redditRedirect.message}</FormErrorMessage>
|
<FormErrorMessage>{formState.errors.redditRedirect.message}</FormErrorMessage>
|
||||||
)}
|
)}
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
Libreddit and Teddit are both privacy focused UIs for reddit.{" "}
|
Libreddit and Teddit are both privacy focused UIs for reddit.{" "}
|
||||||
<Link
|
<Link
|
||||||
href="https://github.com/libreddit/libreddit-instances/blob/master/instances.md"
|
href="https://github.com/libreddit/libreddit-instances/blob/master/instances.md"
|
||||||
isExternal
|
isExternal
|
||||||
color="blue.500"
|
color="blue.500"
|
||||||
>
|
>
|
||||||
Libreddit instances
|
Libreddit instances
|
||||||
</Link>
|
</Link>
|
||||||
{", "}
|
{", "}
|
||||||
<Link href="https://codeberg.org/teddit/teddit#instances" isExternal color="blue.500">
|
<Link href="https://codeberg.org/teddit/teddit#instances" isExternal color="blue.500">
|
||||||
Teddit instances
|
Teddit instances
|
||||||
</Link>
|
</Link>
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormControl isInvalid={!!formState.errors.corsProxy}>
|
<FormControl isInvalid={!!formState.errors.corsProxy}>
|
||||||
<FormLabel>CORS Proxy</FormLabel>
|
<FormLabel>CORS Proxy</FormLabel>
|
||||||
<Input type="url" placeholder="https://cors.example.com/" {...register("corsProxy")} />
|
<Input type="url" placeholder="https://cors.example.com/" {...register("corsProxy")} />
|
||||||
{formState.errors.corsProxy && <FormErrorMessage>{formState.errors.corsProxy.message}</FormErrorMessage>}
|
{formState.errors.corsProxy && <FormErrorMessage>{formState.errors.corsProxy.message}</FormErrorMessage>}
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
This is used as a fallback when verifying NIP-05 ids and fetching open-graph metadata. URL to an
|
This is used as a fallback when verifying NIP-05 ids and fetching open-graph metadata. URL to an instance
|
||||||
instance of{" "}
|
of{" "}
|
||||||
<Link href="https://github.com/Rob--W/cors-anywhere" isExternal color="blue.500">
|
<Link href="https://github.com/Rob--W/cors-anywhere" isExternal color="blue.500">
|
||||||
cors-anywhere
|
cors-anywhere
|
||||||
</Link>
|
</Link>
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
</Flex>
|
||||||
<Button
|
|
||||||
colorScheme="brand"
|
|
||||||
ml="auto"
|
|
||||||
isLoading={formState.isSubmitting}
|
|
||||||
type="submit"
|
|
||||||
isDisabled={!formState.isDirty}
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</form>
|
|
||||||
</AccordionPanel>
|
</AccordionPanel>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user