feat: integration settings page

This commit is contained in:
DIYgod 2024-08-29 17:40:58 +08:00
parent 09950e09e7
commit f9f19386b2
No known key found for this signature in database
10 changed files with 190 additions and 3 deletions

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#fff" fill-opacity=".01" d="M24 0v24H0V0z"/><path stroke="#10161F" stroke-linejoin="round" stroke-width="2" d="M12 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 0v4m-6 4a2 2 0 1 0 0 4 2 2 0 0 0 0-4Zm0 0a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4m0 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4Z"/></svg>

After

Width:  |  Height:  |  Size: 348 B

View File

@ -101,7 +101,7 @@ export const createDefineSettingItem =
key: K,
options: {
label: string
description?: string
description?: string | JSX.Element
onChange?: (value: T[K]) => void
hide?: boolean
},

View File

@ -0,0 +1,19 @@
import type { IntegrationSettings } from "@shared/interface/settings"
import { createSettingAtom } from "./helper"
export const createDefaultSettings = (): IntegrationSettings => ({
enableEagle: true,
enableReadwise: true,
readwiseToken: "",
})
export const {
useSettingKey: useIntegrationSettingKey,
useSettingSelector: useIntegrationSettingSelector,
setSetting: setIntegrationSetting,
clearSettings: clearIntegrationSettings,
initializeDefaultSettings: initializeDefaultIntegrationSettings,
getSettings: getIntegrationSettings,
useSettingValue: useIntegrationSettingValue,
} = createSettingAtom("integration", createDefaultSettings)

View File

@ -0,0 +1,22 @@
import { cn } from "@renderer/lib/utils"
import type { SVGProps } from "react"
export function SimpleIconsReadwise(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 50 50"
className={cn(props.className, "rounded-sm")}
{...props}
>
<g>
<path d="M50 0H0V50H50V0Z" fill="currentColor" />
<path d="M36.2225 35.7866C37.2767 37.5404 38.1366 38.1234 39.8953 38.3323V40.0861H31.2186L23.091 26.527H21.0603V35.4174C21.0603 37.7979 21.6481 38.1282 24.15 38.3371V40.0909H11.0525V38.3371C13.5544 38.1282 14.1423 37.793 14.1423 35.4174V15.5524C14.1423 13.1331 13.6322 12.8416 11.0525 12.6327V10.8789H24.5338C32.6615 10.8789 36.9075 12.589 36.9075 18.5985C36.9075 23.1457 34.6971 25.3173 30.2422 26.0655L36.2225 35.7866ZM22.2311 13.7161C21.5266 13.7161 21.0603 14.2164 21.0603 14.9695V23.6898H24.4221C27.9005 23.6898 29.9701 22.4364 29.9701 18.5985C29.9701 14.7168 27.9005 13.7161 24.5387 13.7161H22.2311Z" fill="white" />
<path d="M31.345 11.918H18.7041V23.9856H31.345V11.918Z" fill="white" />
<path d="M29.7184 14.0234C29.0334 15.2914 30.0925 21.2669 30.0925 21.2669C30.1071 21.5827 29.8933 21.8596 29.5824 21.9276C29.5824 21.9276 23.1016 21.9568 20.9543 23.7446" fill="currentColor" />
</g>
</svg>
)
}

View File

@ -1,2 +1,3 @@
export * from "./collections/eagle"
export * from "./collections/readwise"
export * from "./collections/rss3"

View File

@ -1,10 +1,11 @@
import { Button } from "@renderer/components/ui/button"
import { Checkbox } from "@renderer/components/ui/checkbox"
import { Input } from "@renderer/components/ui/input"
import { Label } from "@renderer/components/ui/label"
import { SegmentGroup, SegmentItem } from "@renderer/components/ui/segement"
import { Switch } from "@renderer/components/ui/switch"
import { cn } from "@renderer/lib/utils"
import type { ReactNode } from "react"
import type { ChangeEventHandler, ReactNode } from "react"
import { useId, useState } from "react"
export const SettingCheckbox: Component<{
@ -47,6 +48,27 @@ export const SettingSwitch: Component<{
)
}
export const SettingInput: Component<{
label: string
value: string
onChange: ChangeEventHandler<HTMLInputElement>
}> = ({ value, label, onChange, className }) => {
const id = useId()
return (
<div
className={cn("mb-1 flex items-center justify-between gap-12", className)}
>
<Label className="shrink-0" htmlFor={id}>{label}</Label>
<Input
id={id}
value={value}
onChange={onChange}
className="text-xs"
/>
</div>
)
}
export const SettingTabbedSegment: Component<{
label: string
value: string

View File

@ -7,6 +7,7 @@ import { isValidElement } from "react"
import {
SettingActionItem,
SettingDescription,
SettingInput,
SettingSwitch,
} from "./control"
import { SettingItemGroup, SettingSectionTitle } from "./section"
@ -93,7 +94,16 @@ export const createSettingBuilder =
break
}
case "string": {
return null
ControlElement = (
<SettingInput
className="mt-4"
value={settingObject[assertSetting.key] as string}
onChange={(event) =>
assertSetting.onChange(event.target.value as T[keyof T])}
label={assertSetting.label}
/>
)
break
}
default: {
return null

View File

@ -0,0 +1,90 @@
import { createSetting } from "@renderer/atoms/settings/helper"
import {
setIntegrationSetting,
useIntegrationSettingValue,
} from "@renderer/atoms/settings/integration"
import {
createDefaultSettings,
setUISetting,
useUISettingSelector,
} from "@renderer/atoms/settings/ui"
import { SimpleIconsEagle, SimpleIconsReadwise } from "@renderer/components/ui/platform-icon/icons"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@renderer/components/ui/select"
import { tipcClient } from "@renderer/lib/client"
import { useQuery } from "@tanstack/react-query"
import { SettingsTitle } from "../title"
const { defineSettingItem, SettingBuilder } = createSetting(
useIntegrationSettingValue,
setIntegrationSetting,
)
export const SettingIntegration = () => (
<>
<SettingsTitle />
<div className="mt-4">
<SettingBuilder
settings={[
{
type: "title",
value: <span className="flex items-center gap-1"><SimpleIconsEagle />Eagle</span>,
},
defineSettingItem("enableEagle", {
label: "Enable",
description: <>Display <i>Save media to Eagle</i> button when available.</>,
}),
{
type: "title",
value: <span className="flex items-center gap-1"><SimpleIconsReadwise />Readwise</span>,
},
defineSettingItem("enableReadwise", {
label: "Enable",
description: <>Display <i>Save to Readwise</i> button when available.</>,
}),
defineSettingItem("readwiseToken", {
label: "Readwise Access Token",
description: <>You can get it here: <a target="_blank" className="underline" rel="noreferrer noopener" href="https://readwise.io/access_token">readwise.io/access_token</a>.</>,
}),
]}
/>
</div>
</>
)
export const VoiceSelector = () => {
const { data } = useQuery({
queryFn: () => tipcClient?.getVoices(),
queryKey: ["voices"],
})
const voice = useUISettingSelector((state) => state.voice)
return (
<div className="-mt-1 mb-3 flex items-center justify-between">
<span className="shrink-0 text-sm font-medium">Voices</span>
<Select
defaultValue={createDefaultSettings().voice}
value={voice}
onValueChange={(value) => {
setUISetting("voice", value)
}}
>
<SelectTrigger size="sm" className="w-48">
<SelectValue />
</SelectTrigger>
<SelectContent className="h-64">
{data?.map((item) => (
<SelectItem key={item.ShortName} value={item.ShortName}>
{item.FriendlyName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)
}

View File

@ -0,0 +1,16 @@
import { SettingIntegration } from "@renderer/modules/settings/tabs/integration"
import { defineSettingPage } from "@renderer/modules/settings/utils"
const iconName = "i-mgc-department-cute-re"
const name = "Integration"
const priority = 1025
export const loader = defineSettingPage({
iconName,
name,
priority,
})
export function Component() {
return <SettingIntegration />
}

View File

@ -33,3 +33,9 @@ export interface UISettings {
// tts
voice: string
}
export interface IntegrationSettings {
enableEagle: boolean
enableReadwise: boolean
readwiseToken: string
}