feat: options general page

This commit is contained in:
DIYgod
2023-12-13 03:00:35 +08:00
parent 51f055de47
commit 62c09d74a6
13 changed files with 483 additions and 13 deletions
+10 -4
View File
@@ -3,7 +3,7 @@
"message": "Easily find and subscribe to RSS and RSSHub."
},
"accessKey": {
"message": "Access key"
"message": "Access key for the current RSSHub instance"
},
"about": {
"message": "About"
@@ -27,7 +27,7 @@
"message": "RSSHub for the current page"
},
"customRSSHubDomain": {
"message": "Custom RSSHub domain name"
"message": "RSSHub instance"
},
"currentSiteRSSHub": {
"message": "RSSHub for the current site"
@@ -69,7 +69,7 @@
"message": "Generate access code"
},
"hotKey": {
"message": "Hot key"
"message": "Hotkey"
},
"joinRSSHub": {
"message": "Join <a href=\"https://docs.rsshub.app\" target=\"_blank\">RSSHub</a> to make everything RSSible!"
@@ -84,7 +84,7 @@
"message": "One-click subscription"
},
"notificationsAndReminders": {
"message": "Notifications and reminders"
"message": "Badge reminder"
},
"requiredAccessKey": {
"message": "Required, please enter the correct access key"
@@ -113,6 +113,12 @@
"settings": {
"message": "Settings"
},
"RSSHubRelatedSettings": {
"message": "RSSHub Related Settings"
},
"quickSubscription": {
"message": "Quick Subscriptions"
},
"showCornerBadge": {
"message": "Show corner badge"
},
+9 -3
View File
@@ -6,7 +6,7 @@
"message": "关于"
},
"accessKey": {
"message": "访问密钥"
"message": "当前 RSSHub 实例的访问密钥"
},
"clickToSet": {
"message": "点此设置"
@@ -27,7 +27,7 @@
"message": "适用于当前页面的 RSSHub"
},
"customRSSHubDomain": {
"message": "自定义 RSSHub 域名"
"message": "RSSHub 实例"
},
"currentSiteRSSHub": {
"message": "适用于当前网站的 RSSHub"
@@ -81,7 +81,7 @@
"message": "本地阅读器"
},
"notificationsAndReminders": {
"message": "通知与提醒"
"message": "角标提醒"
},
"oneclickSubscription": {
"message": "一键订阅"
@@ -113,6 +113,12 @@
"settings": {
"message": "设置"
},
"RSSHubRelatedSettings": {
"message": "RSSHub 相关设置"
},
"quickSubscription": {
"message": "快速订阅"
},
"showCornerBadge": {
"message": "角标提醒"
},
+2
View File
@@ -13,7 +13,9 @@
"@iconify-json/mingcute": "^1.1.13",
"@plasmohq/messaging": "^0.6.1",
"@plasmohq/storage": "^1.9.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"lodash": "^4.17.21",
+167 -1
View File
@@ -14,9 +14,15 @@ dependencies:
'@plasmohq/storage':
specifier: ^1.9.0
version: 1.9.0(react@18.2.0)
'@radix-ui/react-label':
specifier: ^2.0.2
version: 2.0.2(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot':
specifier: ^1.0.2
version: 1.0.2(@types/react@18.2.37)(react@18.2.0)
'@radix-ui/react-switch':
specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
class-variance-authority:
specifier: ^0.7.0
version: 0.7.0
@@ -2399,6 +2405,12 @@ packages:
config-chain: 1.1.13
dev: false
/@radix-ui/primitive@1.0.1:
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
dependencies:
'@babel/runtime': 7.23.5
dev: false
/@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.37)(react@18.2.0):
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
peerDependencies:
@@ -2413,6 +2425,62 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/react-context@1.0.1(@types/react@18.2.37)(react@18.2.0):
resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@types/react': 18.2.37
react: 18.2.0
dev: false
/@radix-ui/react-label@2.0.2(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.37
'@types/react-dom': 18.2.15
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.37)(react@18.2.0)
'@types/react': 18.2.37
'@types/react-dom': 18.2.15
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-slot@1.0.2(@types/react@18.2.37)(react@18.2.0):
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
peerDependencies:
@@ -2428,6 +2496,105 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.37)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.37)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.37)(react@18.2.0)
'@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.37)(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.37)(react@18.2.0)
'@types/react': 18.2.37
'@types/react-dom': 18.2.15
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.37)(react@18.2.0):
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@types/react': 18.2.37
react: 18.2.0
dev: false
/@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.37)(react@18.2.0):
resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.37)(react@18.2.0)
'@types/react': 18.2.37
react: 18.2.0
dev: false
/@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.37)(react@18.2.0):
resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@types/react': 18.2.37
react: 18.2.0
dev: false
/@radix-ui/react-use-previous@1.0.1(@types/react@18.2.37)(react@18.2.0):
resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@types/react': 18.2.37
react: 18.2.0
dev: false
/@radix-ui/react-use-size@1.0.1(@types/react@18.2.37)(react@18.2.0):
resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.37)(react@18.2.0)
'@types/react': 18.2.37
react: 18.2.0
dev: false
/@remix-run/router@1.13.1:
resolution: {integrity: sha512-so+DHzZKsoOcoXrILB4rqDkMDy7NLMErRdOxvzvOKb507YINKUP4Di+shbTZDhSE/pBZ+vr7XGIpcOO0VLSA+Q==}
engines: {node: '>=14.0.0'}
@@ -2879,7 +3046,6 @@ packages:
resolution: {integrity: sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==}
dependencies:
'@types/react': 18.2.37
dev: true
/@types/react@18.2.37:
resolution: {integrity: sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==}
+79
View File
@@ -0,0 +1,79 @@
import * as React from "react"
import { cn } from "~/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn(
"text-xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
+25
View File
@@ -0,0 +1,25 @@
import * as React from "react"
import { cn } from "~/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }
+26
View File
@@ -0,0 +1,26 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "~/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }
+29
View File
@@ -0,0 +1,29 @@
"use client"
import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"
import { cn } from "~/lib/utils"
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName
export { Switch }
+1 -1
View File
@@ -47,7 +47,7 @@ export async function getConfig() {
return Object.assign({}, defaultConfig, await storage.get("config"));
}
export function saveConfig(config: typeof defaultConfig) {
export function setConfig(config: typeof defaultConfig) {
if (!config.rsshubDomain) {
config.rsshubDomain = defaultConfig.rsshubDomain;
}
+55
View File
@@ -0,0 +1,55 @@
export const quickSubscriptions = [{
name: "Miniflux",
projectUrl: "https://miniflux.app",
key: "miniflux",
domainKey: "minifluxDomain",
}, {
name: "Inoreader",
projectUrl: "https://www.inoreader.com/",
key: "inoreader",
domainKey: "inoreaderDomain",
}, {
name: "Feedly",
projectUrl: "https://feedly.com/",
key: "feedly",
}, {
name: "FreshRSS",
projectUrl: "https://freshrss.org",
key: "freshrss",
domainKey: "freshrssDomain",
}, {
name: "Tiny Tiny RSS",
projectUrl: "https://tt-rss.org/",
key: "ttrss",
domainKey: "ttrssDomain",
}, {
name: "Nextcloud News",
projectUrl: "https://apps.nextcloud.com/apps/news",
key: "nextcloudnews",
domainKey: "nextcloudnewsDomain",
}, {
name: "Feedbin",
projectUrl: "https://feedbin.com/",
key: "feedbin",
domainKey: "feedbinDomain",
}, {
name: "The Old Reader",
projectUrl: "https://theoldreader.com/",
key: "theoldreader",
}, {
name: "Feeds.Pub",
projectUrl: "https://feeds.pub/",
key: "feedspub",
}, {
name: "BazQux Reader",
projectUrl: "https://bazqux.com/",
key: "bazqux",
}, {
name: "CheckChan",
projectUrl: "https://ckc.ftqq.com",
key: "checkchan",
domainKey: "checkchanBase",
}, {
name: chrome.i18n.getMessage("localReader"),
key: "local",
}]
+1 -1
View File
@@ -17,7 +17,7 @@ function Siderbar() {
}]
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-[calc(100vh-80px)] sticky top-10">
<div>
<div className="px-4 flex items-center space-x-2 text-orange-500 text-xl font-bold mb-8">
<img className="w-10 h-10" src={RSSHubIcon} /><span>RSSHub Radar</span>
+2 -2
View File
@@ -5,9 +5,9 @@ import Siderbar from "./Siderbar"
function Options() {
return (
<div className="max-w-screen-lg mx-auto text-base py-10 h-screen">
<div className="max-w-screen-lg mx-auto text-base py-10">
<MemoryRouter>
<div className="flex space-x-10 h-full">
<div className="flex space-x-20 h-full">
<Siderbar />
<div className="flex-1">
<Routing />
+77 -1
View File
@@ -1,7 +1,83 @@
import { useState } from "react"
import { Input } from "~/lib/components/Input"
import { Label } from "~/lib/components/Label"
import { Button } from "~/lib/components/Button"
import { defaultConfig, getConfig, setConfig } from "~/lib/config"
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "~/lib/components/Card"
import { Switch } from "~/lib/components/Switch"
import { quickSubscriptions } from "~/lib/quick-subscriptions"
function General() {
const [config, setConfig] = useState(defaultConfig)
getConfig().then(setConfig)
return (
<div>
<h1>General</h1>
<h1 className="text-3xl font-medium leading-10 mb-6 text-orange-500 border-b pb-4">{chrome.i18n.getMessage("general")}</h1>
<div className="space-y-4">
<Card>
<CardHeader>
<CardTitle>{chrome.i18n.getMessage("settings")}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid w-full items-center gap-2">
<Label htmlFor="notificationsAndReminders">{chrome.i18n.getMessage("notificationsAndReminders")}</Label>
<Switch id="notificationsAndReminders" />
</div>
<div className="grid w-full items-center gap-2">
<Label>{chrome.i18n.getMessage("hotKey")}</Label>
<Button variant="secondary" onClick={() => {
chrome.tabs.create({
url: 'chrome://extensions/shortcuts'
});
}}>{chrome.i18n.getMessage("clickToSet")}</Button>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>{chrome.i18n.getMessage("RSSHubRelatedSettings")}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid w-full items-center gap-2">
<Label htmlFor="customRSSHubDomain">{chrome.i18n.getMessage("customRSSHubDomain")}</Label>
<Input id="customRSSHubDomain" value={config.rsshubDomain}/>
</div>
<div className="grid w-full items-center gap-2">
<Label htmlFor="accessKey" className="flex items-center">
<span>{chrome.i18n.getMessage("accessKey")}</span>
<a className="h-[14px] ml-1" target="_blank" href="https://docs.rsshub.app/install/config#access-keycode">
<i className="icon-[mingcute--question-line]"></i>
</a>
</Label>
<Input type="password" id="accessKey" value={config.rsshubAccessControl.accessKey}/>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>{chrome.i18n.getMessage("quickSubscription")}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{quickSubscriptions.map((quickSubscription) => (
<div className="flex items-center space-x-2 h-10">
<Switch id={quickSubscription.key} />
<Label className="w-28" htmlFor={quickSubscription.key}>{quickSubscription.name}</Label>
{quickSubscription.domainKey ? (
<Input className="flex-1" id={quickSubscription.domainKey} value={config.submitto[quickSubscription.domainKey]}/>
) : (
<Input className="flex-1" disabled id={quickSubscription.domainKey} value={config.submitto[quickSubscription.domainKey]}/>
)}
</div>
))}
</CardContent>
</Card>
</div>
</div>
)
}