Skip to content

Commit b1bc69f

Browse files
authored
feat: 图床配置增加支持使用 PicGo 服务,增加内置 SM.MS 图床支持。 (#390)
* feat: 支持 SM.MS 上传图片 * feat: SM.MS 磁盘使用情况 * feat: PicGo 服务支持
1 parent 895e44e commit b1bc69f

File tree

13 files changed

+524
-9
lines changed

13 files changed

+524
-9
lines changed

messages/en.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,20 @@
176176
"title": "Image Hosting",
177177
"desc": "Here, you can configure image hosting, uploading images to the image hosting service.",
178178
"isPrimaryBackup": "Current {type} primary image hosting method",
179-
"setPrimaryBackup": "Set as Primary Image Hosting"
179+
"setPrimaryBackup": "Set as Primary Image Hosting",
180+
"smms": {
181+
"token": {
182+
"desc": "Please create and input SM.MS Token.",
183+
"createToken": "Create Token"
184+
},
185+
"disk": "Disk Usage",
186+
"error": "Failed to get, please check network or Token is correct."
187+
},
188+
"picgo": {
189+
"desc": "PicGo server URL",
190+
"ok": "Service is running, please ensure PicGo image hosting is configured.",
191+
"error": "Service is not running, please ensure PicGo (v2.2.0+) is running, otherwise image upload will fail."
192+
}
180193
},
181194
"backupSync": {
182195
"title": "Backup Data",

messages/ja.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,20 @@
174174
"title": "画像ホスティング",
175175
"desc": "ここでは、画像ホスティングを設定できます。",
176176
"isPrimaryBackup": "現在の {type} 主要バックアップ方法",
177-
"setPrimaryBackup": "主要バックアップ方法として設定"
177+
"setPrimaryBackup": "主要バックアップ方法として設定",
178+
"smms": {
179+
"token": {
180+
"desc": "SM.MS Token を入力してください。",
181+
"createToken": "Token を作成"
182+
},
183+
"disk": "磁盘使用",
184+
"error": "取得失敗、ネットワークや Token の正しさを確認してください。"
185+
},
186+
"picgo": {
187+
"desc": "PicGo サーバー URL",
188+
"ok": "サービスが起動しています。PicGo の設定を確認してください。",
189+
"error": "サービスが起動していません。PicGo(v2.2.0+)を起動してください。"
190+
}
178191
},
179192
"backupSync": {
180193
"title": "備用方案",

messages/zh.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,20 @@
182182
"title": "图床",
183183
"desc": "在这里,你可以配置图床,将图片上传到图床。",
184184
"isPrimaryBackup": "当前 {type} 为主要图床",
185-
"setPrimaryBackup": "设为主要图床"
185+
"setPrimaryBackup": "设为主要图床",
186+
"smms": {
187+
"token": {
188+
"desc": "请创建并输入 SM.MS Token。",
189+
"createToken": "创建 Token"
190+
},
191+
"disk": "磁盘使用情况",
192+
"error": "获取失败,请检查网络或 Token 是否正确。"
193+
},
194+
"picgo": {
195+
"desc": "PicGo 服务器地址",
196+
"ok": "检测到服务正在运行,请确保 PicGo 图床已配置。",
197+
"error": "服务未运行,请确保 PicGo(需要 v2.2.0+) 应用正在运行,否则无法上传图片。"
198+
}
186199
},
187200
"backupSync": {
188201
"title": "备用方案",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@radix-ui/react-hover-card": "^1.1.2",
2525
"@radix-ui/react-label": "^2.1.0",
2626
"@radix-ui/react-popover": "^1.1.2",
27+
"@radix-ui/react-progress": "^1.1.7",
2728
"@radix-ui/react-radio-group": "^1.2.1",
2829
"@radix-ui/react-scroll-area": "^1.2.6",
2930
"@radix-ui/react-select": "^2.1.2",

pnpm-lock.yaml

Lines changed: 64 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/core/setting/imageHosting/page.tsx

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,58 @@ import { useTranslations } from 'next-intl';
44
import { SettingType } from '../components/setting-base';
55
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
66
import { GithubImageHosting } from "./github";
7+
import SMMSImageHosting from "./smms";
8+
import useImageStore from "@/stores/imageHosting";
9+
import { useEffect, useState } from "react";
10+
import { Store } from "@tauri-apps/plugin-store";
11+
import PicgoImageHosting from "./picgo";
712

813
export default function ImageHostingPage() {
914
const t = useTranslations();
15+
const { mainImageHosting, setMainImageHosting } = useImageStore()
16+
const [value, setValue] = useState(mainImageHosting)
17+
18+
async function init() {
19+
const store = await Store.load('store.json');
20+
const imageHosting = await store.get<string>('mainImageHosting')
21+
if (imageHosting) {
22+
setMainImageHosting(imageHosting)
23+
setValue(imageHosting)
24+
}
25+
}
26+
27+
useEffect(() => {
28+
init()
29+
}, [])
1030

1131
return (
1232
<SettingType id="imageHosting" icon={<ImageUp />} title={t('settings.imageHosting.title')} desc={t('settings.imageHosting.desc')}>
13-
<Tabs defaultValue="github">
14-
<TabsList className="grid grid-cols-2 w-full mb-8">
33+
<Tabs value={value} defaultValue={mainImageHosting} onValueChange={(value) => {setValue(value)}}>
34+
<TabsList className="grid grid-cols-4 w-full mb-8">
1535
<TabsTrigger value="github" className="flex items-center gap-2">
1636
Github
17-
<SquareCheckBig className="size-4" />
37+
{mainImageHosting === 'github' && <SquareCheckBig className="size-4" />}
38+
</TabsTrigger>
39+
<TabsTrigger value="smms" className="flex items-center gap-2">
40+
SM.MS
41+
{mainImageHosting === 'smms' && <SquareCheckBig className="size-4" />}
42+
</TabsTrigger>
43+
<TabsTrigger value="picgo" className="flex items-center gap-2">
44+
PicGo
45+
{mainImageHosting === 'picgo' && <SquareCheckBig className="size-4" />}
46+
</TabsTrigger>
47+
<TabsTrigger value="none" disabled>
48+
Under development...
1849
</TabsTrigger>
19-
<TabsTrigger disabled value="SM.MS">Under development...</TabsTrigger>
2050
</TabsList>
2151
<TabsContent value="github">
2252
<GithubImageHosting />
2353
</TabsContent>
24-
<TabsContent value="SM.MS">
54+
<TabsContent value="smms">
55+
<SMMSImageHosting />
56+
</TabsContent>
57+
<TabsContent value="picgo">
58+
<PicgoImageHosting />
2559
</TabsContent>
2660
</Tabs>
2761
</SettingType>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { SettingRow } from "../components/setting-base"
2+
import { FormItem } from "../components/setting-base"
3+
import { useTranslations } from 'next-intl';
4+
import { Input } from "@/components/ui/input";
5+
import { Button } from "@/components/ui/button";
6+
import { useState, useEffect } from "react";
7+
import { Store } from "@tauri-apps/plugin-store";
8+
import useImageStore from "@/stores/imageHosting";
9+
import { checkPicgoState, type PicgoImageHostingSetting } from "@/lib/imageHosting/picgo";
10+
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
11+
import { CheckCircle, LoaderCircle, XCircle } from "lucide-react"
12+
13+
const DEFAULT_URL = 'http://127.0.0.1:36677'
14+
15+
export default function PicgoImageHosting() {
16+
const t = useTranslations('settings.imageHosting');
17+
const { mainImageHosting, setMainImageHosting } = useImageStore()
18+
19+
const [loading, setLoading] = useState(false)
20+
const [picgoState, setPicgoState] = useState(false)
21+
const [url, setUrl] = useState(DEFAULT_URL)
22+
23+
async function init() {
24+
const store = await Store.load('store.json');
25+
const picgoSetting = await store.get<PicgoImageHostingSetting>('picgo')
26+
if (picgoSetting) {
27+
setUrl(picgoSetting.url)
28+
} else {
29+
await store.set('picgo', { url: DEFAULT_URL })
30+
await store.save()
31+
}
32+
}
33+
34+
async function handleCheckPicgoState() {
35+
setLoading(true)
36+
setPicgoState(false)
37+
const state = await checkPicgoState()
38+
setPicgoState(state)
39+
setLoading(false)
40+
}
41+
42+
async function handleSaveUrl(url: string) {
43+
const store = await Store.load('store.json');
44+
await store.set('picgo', { url })
45+
await store.save()
46+
setUrl(url)
47+
handleCheckPicgoState()
48+
}
49+
50+
useEffect(() => {
51+
init()
52+
handleCheckPicgoState()
53+
window.addEventListener('visibilitychange', handleCheckPicgoState)
54+
return () => {
55+
window.removeEventListener('visibilitychange', handleCheckPicgoState)
56+
}
57+
}, [])
58+
59+
return <div>
60+
<SettingRow className="mb-4">
61+
<Alert variant={picgoState ? 'default' : 'destructive'}>
62+
{
63+
loading ? <LoaderCircle className="animate-spin size-4" /> :
64+
picgoState ? <CheckCircle className="size-4 !text-green-500" /> : <XCircle className="size-4" />
65+
}
66+
<AlertTitle className="mb-1 text-base font-bold">PicGo</AlertTitle>
67+
<AlertDescription>
68+
{picgoState ? t('picgo.ok') : t('picgo.error')}
69+
</AlertDescription>
70+
</Alert>
71+
</SettingRow>
72+
<SettingRow>
73+
<FormItem title="URL" desc={t('picgo.desc')}>
74+
<Input
75+
type="text"
76+
value={url}
77+
onChange={(e) => handleSaveUrl(e.target.value)}
78+
/>
79+
</FormItem>
80+
</SettingRow>
81+
<SettingRow className="mb-4">
82+
{mainImageHosting === 'picgo' ? (
83+
<Button disabled variant="outline">
84+
{t('isPrimaryBackup', { type: 'PicGo' })}
85+
</Button>
86+
) : (
87+
<Button
88+
variant="outline"
89+
onClick={() => setMainImageHosting('picgo')}
90+
>
91+
{t('setPrimaryBackup')}
92+
</Button>
93+
)}
94+
</SettingRow>
95+
</div>
96+
}

0 commit comments

Comments
 (0)