fix: resolve lint issues in settings components
- Fix react-hooks/set-state-in-effect by inlining data fetch in useEffect - Replace useCallback-wrapped fetch functions with plain async functions - Replace window.location.href with window.location.assign() - Remove unused eslint-disable directives - Fix double res.json() call in passkey refresh
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect, useCallback } from "react"
|
||||
import { useState, useEffect } from "react"
|
||||
import { Loader2, Link as LinkIcon, Unlink, Shield } from "lucide-react"
|
||||
import { motion } from "motion/react"
|
||||
import { FaGoogle, FaDiscord } from "react-icons/fa"
|
||||
@@ -33,7 +33,7 @@ export function SettingsAccountsTab() {
|
||||
const [unlinking, setUnlinking] = useState<string | null>(null)
|
||||
const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null)
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
const refreshData = async () => {
|
||||
try {
|
||||
const [accountsRes, methodsRes] = await Promise.all([
|
||||
fetch("/api/auth/list-accounts", { credentials: "include" }),
|
||||
@@ -49,16 +49,26 @@ export function SettingsAccountsTab() {
|
||||
const data = await methodsRes.json()
|
||||
setAuthMethods(data)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch account data:", err)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
} catch {
|
||||
// silently fail
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [fetchData])
|
||||
if (accounts.length > 0 || authMethods !== null) return
|
||||
let cancelled = false
|
||||
Promise.all([
|
||||
fetch("/api/auth/list-accounts", { credentials: "include" }).then(r => r.ok ? r.json() : []).catch(() => []),
|
||||
fetch("/api/user/me/auth-methods").then(r => r.ok ? r.json() : null).catch(() => null),
|
||||
]).then(([accountsData, methodsData]) => {
|
||||
if (cancelled) return
|
||||
if (accountsData) setAccounts(Array.isArray(accountsData) ? accountsData : [])
|
||||
if (methodsData) setAuthMethods(methodsData)
|
||||
setLoading(false)
|
||||
})
|
||||
return () => { cancelled = true }
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentional: fetch only on mount
|
||||
}, [])
|
||||
|
||||
const handleLink = async (provider: "google" | "discord") => {
|
||||
try {
|
||||
@@ -81,9 +91,9 @@ export function SettingsAccountsTab() {
|
||||
const data = await res.json()
|
||||
// If the API returns a URL, redirect to it
|
||||
if (data && data.url) {
|
||||
window.location.href = data.url
|
||||
window.location.assign(data.url as string)
|
||||
}
|
||||
} catch (err) {
|
||||
} catch {
|
||||
setMessage({ type: "error", text: "Failed to initiate account linking" })
|
||||
}
|
||||
}
|
||||
@@ -104,7 +114,7 @@ export function SettingsAccountsTab() {
|
||||
|
||||
if (res.ok) {
|
||||
setMessage({ type: "success", text: `${providerConfig[providerId]?.name || providerId} account unlinked` })
|
||||
await fetchData()
|
||||
await refreshData()
|
||||
} else {
|
||||
const data = await res.json()
|
||||
setMessage({ type: "error", text: data.message || "Failed to unlink account" })
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect, useCallback } from "react"
|
||||
import { useState, useRef, useEffect } from "react"
|
||||
import { authClient } from "@/lib/auth-client"
|
||||
import { Loader2, Key, Fingerprint, Plus, Trash2, Pencil, Check, X, Shield } from "lucide-react"
|
||||
import { motion, AnimatePresence } from "motion/react"
|
||||
@@ -39,42 +39,38 @@ export function SettingsSecurityTab() {
|
||||
const [isDeletingPasskey, setIsDeletingPasskey] = useState<string | null>(null)
|
||||
const [isAddingPasskey, setIsAddingPasskey] = useState(false)
|
||||
|
||||
const fetchAuthMethods = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetch("/api/user/me/auth-methods", {
|
||||
credentials: "include",
|
||||
})
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setAuthMethods(data)
|
||||
}
|
||||
} catch {
|
||||
// silently fail
|
||||
} finally {
|
||||
setIsLoadingAuthMethods(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const fetchPasskeys = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetch("/api/auth/passkey/list-user-passkeys", {
|
||||
credentials: "include",
|
||||
})
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setPasskeys(Array.isArray(data) ? data : [])
|
||||
}
|
||||
} catch {
|
||||
// silently fail
|
||||
} finally {
|
||||
setIsLoadingPasskeys(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const fetchRef = useRef(false)
|
||||
useEffect(() => {
|
||||
fetchAuthMethods()
|
||||
fetchPasskeys()
|
||||
}, [fetchAuthMethods, fetchPasskeys])
|
||||
if (fetchRef.current) return
|
||||
fetchRef.current = true
|
||||
|
||||
let cancelled = false
|
||||
Promise.all([
|
||||
fetch("/api/user/me/auth-methods", { credentials: "include" }).then(r => r.ok ? r.json() : null).catch(() => null),
|
||||
fetch("/api/auth/passkey/list-user-passkeys", { credentials: "include" }).then(r => r.ok ? r.json() : []).catch(() => []),
|
||||
]).then(([methodsData, passkeyData]) => {
|
||||
if (cancelled) return
|
||||
if (methodsData) setAuthMethods(methodsData)
|
||||
if (passkeyData) setPasskeys(Array.isArray(passkeyData) ? passkeyData : [])
|
||||
setIsLoadingAuthMethods(false)
|
||||
setIsLoadingPasskeys(false)
|
||||
})
|
||||
|
||||
return () => { cancelled = true }
|
||||
}, [])
|
||||
|
||||
const refreshAuthMethods = async () => {
|
||||
const res = await fetch("/api/user/me/auth-methods", { credentials: "include" })
|
||||
if (res.ok) setAuthMethods(await res.json())
|
||||
}
|
||||
|
||||
const refreshPasskeys = async () => {
|
||||
const res = await fetch("/api/auth/passkey/list-user-passkeys", { credentials: "include" })
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setPasskeys(Array.isArray(data) ? data : [])
|
||||
}
|
||||
}
|
||||
|
||||
const handlePasswordSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
@@ -121,7 +117,7 @@ export function SettingsSecurityTab() {
|
||||
setPasswordMessage({ type: "success", text: "Password set successfully" })
|
||||
setNewPassword("")
|
||||
setConfirmPassword("")
|
||||
await fetchAuthMethods()
|
||||
await refreshAuthMethods()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,11 +129,10 @@ export function SettingsSecurityTab() {
|
||||
try {
|
||||
const { error } = await authClient.passkey.addPasskey()
|
||||
if (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Failed to add passkey:", error)
|
||||
} else {
|
||||
await fetchPasskeys()
|
||||
await fetchAuthMethods()
|
||||
await refreshPasskeys()
|
||||
await refreshAuthMethods()
|
||||
}
|
||||
} catch {
|
||||
// silently fail
|
||||
@@ -158,8 +153,8 @@ export function SettingsSecurityTab() {
|
||||
credentials: "include",
|
||||
})
|
||||
if (res.ok) {
|
||||
await fetchPasskeys()
|
||||
await fetchAuthMethods()
|
||||
await refreshPasskeys()
|
||||
await refreshAuthMethods()
|
||||
}
|
||||
} catch {
|
||||
// silently fail
|
||||
@@ -188,7 +183,7 @@ export function SettingsSecurityTab() {
|
||||
credentials: "include",
|
||||
})
|
||||
if (res.ok) {
|
||||
await fetchPasskeys()
|
||||
await refreshPasskeys()
|
||||
setEditingPasskeyId(null)
|
||||
setEditingName("")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user