Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@
"description": "WebDAV is only used as a backup solution and does not support auto-sync, history rollback, and other features.",
"backupTo": "Backup to WebDAV",
"syncFrom": "Sync from WebDAV",
"testConnection": "Test Connection",
"serverUrl": "WebDAV Server URL",
"serverUrlDesc": "Enter the URL of the WebDAV server No path included, e.g: https://dav.example.com http://192.8.8.88:9999",
"serverUrlPlaceholder": "https://dav.example.com",
Expand All @@ -327,6 +328,7 @@
"success": "Success",
"failed": "Failed",
"error": {
"testFailed": "Connection test failed",
"pathNotFound": "Path not found or server inaccessible.",
"createDirFailed": "Failed to create directory",
"connectionTimeOut": "Connection timed out, please check network or server."
Expand Down
2 changes: 2 additions & 0 deletions messages/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@
"description": "WebDAVはバックアップソリューションとしてのみ使用され、自動同期や履歴ロールバックなどの機能はサポートしていません。",
"backupTo": "WebDAVにバックアップ",
"syncFrom": "WebDAVから同期",
"testConnection": "接続テスト",
"serverUrl": "WebDAVサーバーURL",
"serverUrlDesc": "WebDAVサーバーのURLを入力してくださいパスを含まない、例:https://dav.example.com http://192.8.8.88:9999 ",
"serverUrlPlaceholder": "https://dav.example.com",
Expand All @@ -325,6 +326,7 @@
"success": "成功",
"failed": "失敗",
"error": {
"testFailed": "接続テストに失敗しました",
"pathNotFound": "パスが存在しないか、サーバーにアクセスできません。",
"createDirFailed": "ディレクトリの作成に失敗しました",
"connectionTimeOut": "接続タイムアウト、ネットワークまたはサーバーを確認してください。"
Expand Down
2 changes: 2 additions & 0 deletions messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@
"description": "WebDAV 仅作为备用备份方案,不支持自动同步、历史回滚等功能。",
"backupTo": "备份至 WebDAV",
"syncFrom": "从 WebDAV 同步",
"testConnection": "测试连接",
"serverUrl": "WebDAV 服务器地址",
"serverUrlDesc": "输入WebDAV服务器的URL不含路径,例如:https://dav.example.com http://192.8.8.88:9999 ",
"serverUrlPlaceholder": "https://dav.example.com",
Expand All @@ -319,6 +320,7 @@
"success": "成功",
"failed": "失败",
"error": {
"testFailed": "连接测试失败",
"pathNotFound": "路径不存在或服务器无法访问。",
"createDirFailed": "创建目录失败",
"connectionTimeOut": "连接超时,请检查网络或服务器。"
Expand Down
8 changes: 6 additions & 2 deletions src/app/core/setting/ai/check.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { debounce } from "lodash-es"
// 检测当前 AI 的可用性
export function AiCheck() {
const [state, setState] = useState<'ok' | 'error' | 'checking' | 'init'>('init')
const { currentAi, aiModelList } = useSettingStore()
const { currentAi, aiModelList, aiAutoCheck } = useSettingStore()
const t = useTranslations('settings.ai')
const abortControllerRef = useRef<AbortController | null>(null)
const debouncedCheckRef = useRef<ReturnType<typeof debounce> | null>(null)
Expand Down Expand Up @@ -171,12 +171,16 @@ export function AiCheck() {

useEffect(() => {
const model = aiModelList.find(item => item.key === currentAi)
if (!aiAutoCheck) {
setState('init')
return
}
if (model?.model) {
debouncedCheckRef.current?.()
} else {
setState('init')
}
}, [aiModelList, currentAi])
}, [aiModelList, currentAi, aiAutoCheck])

// 组件卸载时清理资源
useEffect(() => {
Expand Down
14 changes: 13 additions & 1 deletion src/app/core/setting/ai/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { v4 } from 'uuid';
import { confirm } from '@tauri-apps/plugin-dialog';
import { AiCheck } from "./check";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import CreateConfig from "./create";
import { Badge } from "@/components/ui/badge";
Expand All @@ -34,7 +35,9 @@ export default function AiPage() {
currentAi,
setCurrentAi,
aiModelList,
setAiModelList
setAiModelList,
aiAutoCheck,
setAiAutoCheck
} = useSettingStore()
const [apiKey, setApiKey] = useState<string>('')
const [baseURL, setBaseURL] = useState<string>('')
Expand Down Expand Up @@ -256,6 +259,15 @@ export default function AiPage() {
</div>
</FormItem>
</SettingRow>
{/* 自动连通性检测开关 */}
<SettingRow>
<FormItem title={t('autoCheckTitle', { default: '自动连通性检测' })} desc={t('autoCheckDesc', { default: '关闭后不会在切换或修改配置时自动请求测试' })}>
<div className="flex items-center gap-3">
<Switch checked={aiAutoCheck} onCheckedChange={(v) => setAiAutoCheck(!!v)} />
<span className="text-sm text-muted-foreground">{aiAutoCheck ? t('enabled', { default: '已开启' }) : t('disabled', { default: '已关闭' })}</span>
</div>
</FormItem>
</SettingRow>
{/* 模型名称 */}
<SettingRow>
<FormItem title={t('modelTitle')} desc={t('modelTitleDesc')}>
Expand Down
25 changes: 25 additions & 0 deletions src/app/core/setting/backupSync/webdav-sync.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default function WebdavSync() {
syncState,
backupState,
createWebDAVDir,
testConnection,
} = useWebDAVStore();
const { loadFileTree } = useArticleStore()

Expand Down Expand Up @@ -108,6 +109,15 @@ export default function WebdavSync() {

const [isCreating, setIsCreating] = useState(false);

const handleTestConnection = async () => {
const ok = await testConnection();
if (ok) {
toast({ title: t("success"), description: t("connectionState.success") });
} else {
toast({ variant: "destructive", title: t("error.testFailed", { default: "Test failed" } as any), description: t("error.connectionTimeOut") });
}
};

const handleCreateDirectory = async () => {
if (!path.trim()) {
toast({
Expand Down Expand Up @@ -158,6 +168,21 @@ export default function WebdavSync() {
>
{t(`connectionState.${connectionState}`)}
</Badge>
<Button
variant="outline"
size="sm"
className="ml-auto"
onClick={handleTestConnection}
disabled={
connectionState === WebDAVConnectionState.checking ||
!url.trim() || !username.trim() || !password.trim()
}
>
{connectionState === WebDAVConnectionState.checking && (
<LoaderCircle className="animate-spin" />
)}
{t("testConnection")}
</Button>
</CardTitle>
<CardDescription>{t("description")}</CardDescription>
</CardHeader>
Expand Down
13 changes: 13 additions & 0 deletions src/stores/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ interface SettingState {
tesseractList: string
setTesseractList: (tesseractList: string) => void

// AI 设置 - 自动连通性检测
aiAutoCheck: boolean
setAiAutoCheck: (aiAutoCheck: boolean) => Promise<void>

// Github 相关设置
githubUsername: string
setGithubUsername: (githubUsername: string) => Promise<void>
Expand Down Expand Up @@ -281,6 +285,15 @@ const useSettingStore = create<SettingState>((set, get) => ({
tesseractList: 'eng,chi_sim',
setTesseractList: (tesseractList) => set({ tesseractList }),

// AI 设置 - 自动连通性检测(默认开启)
aiAutoCheck: true,
setAiAutoCheck: async (aiAutoCheck: boolean) => {
const store = await Store.load('store.json');
await store.set('aiAutoCheck', aiAutoCheck)
await store.save()
set({ aiAutoCheck })
},

githubUsername: '',
setGithubUsername: async (githubUsername) => {
set({ githubUsername })
Expand Down
61 changes: 2 additions & 59 deletions src/stores/webdav.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { create } from 'zustand'
import { Store } from '@tauri-apps/plugin-store'
import { invoke } from '@tauri-apps/api/core'

export enum WebDAVConnectionState {
Expand Down Expand Up @@ -37,18 +36,6 @@ interface WebDAVState {
syncFromWebDAV: () => Promise<string>
}

// 防抖函数
function simpleDebounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: NodeJS.Timeout | null = null

return (...args: Parameters<T>) => {
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => func(...args), wait)
}
}

const useWebDAVStore = create<WebDAVState>((set, get) => {
let isTestingInProgress = false
Expand Down Expand Up @@ -101,56 +88,25 @@ const useWebDAVStore = create<WebDAVState>((set, get) => {
}
}

// 使用防抖
const debouncedTest = simpleDebounce(performConnectionTest, 300)

return {
url: '',
setUrl: async (url: string) => {
set({ url })
try {
const store = await Store.load('store.json')
await store.set('webdavUrl', url)
} catch (error) {
console.error('Failed to save URL:', error)
}
debouncedTest()
},

username: '',
setUsername: async (username: string) => {
set({ username })
try {
const store = await Store.load('store.json')
await store.set('webdavUsername', username)
} catch (error) {
console.error('Failed to save username:', error)
}
debouncedTest()
},

password: '',
setPassword: async (password: string) => {
set({ password })
try {
const store = await Store.load('store.json')
await store.set('webdavPassword', password)
} catch (error) {
console.error('Failed to save password:', error)
}
debouncedTest()
},

path: '',
setPath: async (path: string) => {
set({ path })
try {
const store = await Store.load('store.json')
await store.set('webdavPath', path)
} catch (error) {
console.error('Failed to save path:', error)
}
debouncedTest()
},

connectionState: WebDAVConnectionState.fail,
Expand All @@ -163,21 +119,8 @@ const useWebDAVStore = create<WebDAVState>((set, get) => {
},

initWebDAVData: async () => {
try {
const store = await Store.load('store.json')
const url = await store.get<string>('webdavUrl') || ''
const username = await store.get<string>('webdavUsername') || ''
const password = await store.get<string>('webdavPassword') || ''
const path = await store.get<string>('webdavPath') || ''

set({ url, username, password, path })

if (url && username && password) {
setTimeout(() => performConnectionTest(), 100)
}
} catch (error) {
console.error('Failed to load WebDAV data:', error)
}
// 不再从本地存储加载连接参数,也不做自动连测
set({ url: '', username: '', password: '', path: '' })
},

backupState: false,
Expand Down