refactor: codebase linting

This commit is contained in:
melvinchia3636
2026-06-10 18:46:34 +08:00
parent 4549bb6f80
commit b8ac80b9be
113 changed files with 377 additions and 214 deletions

View File

@@ -22,7 +22,8 @@ function OrdinaryColumn({
const { open } = useModalStore()
const { userData } = useAuth()
const { t } = useTranslation('common.accountSettings')
const handleOpenModifyModal = useCallback(() => {
const handleOpenModifyModal = useCallback(() => {
open(ModifyModal, {
type,
title,

View File

@@ -14,7 +14,8 @@ import QRLoginApprovalModal from '../modals/QRLoginScannerModal'
function QRLoginColumn() {
const { open } = useModalStore()
const { t } = useTranslation('common.auth')
const handleOpenScanner = useCallback(() => {
const handleOpenScanner = useCallback(() => {
open(QRCodeScanner, {
onScanned: (scannedData: string) => {
// Validate that this is a LifeForge QR login code

View File

@@ -9,7 +9,8 @@ import TwoFAEnableProcedure from './components/TwoFAEnableProcedure'
function EnableTwoFAModal({ onClose }: { onClose: () => void }) {
const { t } = useTranslation('common.accountSettings')
const { setUserData } = useAuth()
const handleSuccess = useCallback(() => {
const handleSuccess = useCallback(() => {
setUserData(userData =>
userData ? { ...userData, twoFAEnabled: true } : null
)

View File

@@ -36,7 +36,8 @@ function EntryItem({ entry }: { entry: APIKeyEntry }) {
const queryClient = useQueryClient()
const { open } = useModalStore()
const [isCopying, setIsCopying] = useState(false)
const deleteMutation = useMutation(
const deleteMutation = useMutation(
forgeAPI.apiKeys.entries.remove
.input({
id: entry.id

View File

@@ -54,7 +54,8 @@ function ModifyAPIKeyModal({
onClose: () => void
}) {
const queryClient = useQueryClient()
const mutation = useMutation(
const mutation = useMutation(
(type === 'create'
? forgeAPI.apiKeys.entries.create
: forgeAPI.apiKeys.entries.update.input({
@@ -82,7 +83,8 @@ function ModifyAPIKeyModal({
mode: 'all',
resolver: zodResolver(schema)
})
const overrideKey = useWatch({ control: form.control, name: 'overrideKey' })
const overrideKey = useWatch({ control: form.control, name: 'overrideKey' })
return (
<FormModal

View File

@@ -78,8 +78,10 @@ function AuthForm({ providers }: { providers: string[] }) {
setFormDisabled(false)
})
}, [authenticate, t])
const [loading, onSubmit] = usePromiseLoading(signIn)
const onInputKeyDown = useCallback(
const [loading, onSubmit] = usePromiseLoading(signIn)
const onInputKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
onSubmit()

View File

@@ -16,7 +16,8 @@ function SigninWithProviderButton({
}) {
const { auth } = useAuth()
const [searchParams] = useSearchParams()
const signInWithProvider = useCallback(async () => {
const signInWithProvider = useCallback(async () => {
const providerInstance = await fetch(
forgeAPI.user.oauth.getEndpoint.input({
provider

View File

@@ -41,7 +41,8 @@ const getStyle = (status: QRStatus) => {
function QRContent({ onClose }: { onClose: () => void }) {
const { t } = useTranslation('common.auth')
const { status, qrData, timeLeft, refreshSession } = useQRLoginSession({
const { status, qrData, timeLeft, refreshSession } = useQRLoginSession({
onSuccess: onClose
})

View File

@@ -43,7 +43,8 @@ export default function useQRLoginSession({
/**
* Fallback polling for WebSocket connection issues
*/
const startPolling = useCallback(
const startPolling = useCallback(
(sessionId: string) => {
if (pollingIntervalRef.current) return

View File

@@ -27,7 +27,8 @@ function UsingEmail({
const [sendOtpLoading, setSendOtpLoading] = useState(false)
const [verifyOtpLoading, setVerifyOtpLoading] = useState(false)
const [otpSent, setOtpSent] = useState(false)
const [otpCooldown, setOtpCooldown] = useState(
const [otpCooldown, setOtpCooldown] = useState(
localStorage.getItem(`otpCooldown:2fa`)
? Math.floor(
(Number(localStorage.getItem(`otpCooldown:2fa`)) -

View File

@@ -14,7 +14,8 @@ import AuthSideImage from '../components/AuthSideImage'
function LoginPage() {
const { verifyOAuth } = useAuth()
const [searchParams, setSearchParams] = useSearchParams()
useEffect(() => {
useEffect(() => {
const code = searchParams.get('code')
const state = searchParams.get('state')

View File

@@ -16,15 +16,18 @@ import forgeAPI from '@/forgeAPI'
function UserCreationPage() {
const { t } = useTranslation('common.auth')
const [formData, setFormData] = useState({
const [formData, setFormData] = useState({
email: '',
username: '',
name: '',
password: '',
confirmPassword: ''
})
const [errors, setErrors] = useState<Record<string, string>>({})
const createUserMutation = useMutation(
const [errors, setErrors] = useState<Record<string, string>>({})
const createUserMutation = useMutation(
forgeAPI.user.auth.createFirstUser.mutationOptions({
onSuccess: () => {
toast.success(t('messages.userCreated'))

View File

@@ -30,7 +30,8 @@ function BackupItem({
const queryClient = useQueryClient()
const { open } = useModalStore()
const [downloadLoading, setDownloadLoading] = useState(false)
const deleteMutation = useMutation(
const deleteMutation = useMutation(
forgeAPI.backups.remove
.input({
key: backup.key

View File

@@ -21,7 +21,8 @@ const schema = z.object({
function CreateBackupModal({ onClose }: { onClose: () => void }) {
const { t } = useTranslation('common.backups')
const queryClient = useQueryClient()
const mutation = useMutation(
const mutation = useMutation(
forgeAPI.backups.create.mutationOptions({
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['backups'] })

View File

@@ -40,14 +40,16 @@ function DashboardGrid({
}) {
const { t } = useTranslation('common.dashboard')
const { widgets } = useWidgets()
const COMPONENTS = useMemo(
const COMPONENTS = useMemo(
() =>
Object.fromEntries(
Object.entries(widgets).map(([key, value]) => [key, value.component])
),
[widgets]
)
const { width, height } = useDivSize(wrapperRef)
const { width, height } = useDivSize(wrapperRef)
const { dashboardLayout: enabledWidgets } = usePersonalization()
const { changeDashboardLayout } = useUserPersonalization()

View File

@@ -18,7 +18,8 @@ function DashboardContent() {
const { widgets } = useWidgets()
const wrapperRef = useRef<HTMLDivElement>(null)
const [canLayoutChange, setCanLayoutChange] = useState(false)
const handleManageWidget = useCallback(() => {
const handleManageWidget = useCallback(() => {
open(ManageWidgetsModal, { widgets })
}, [widgets, open])

View File

@@ -35,12 +35,15 @@ function ComponentListItem({
namespace?: string
}) {
const { t } = useTranslation([namespace ?? 'common.dashboard'])
const {
const {
dashboardLayout: enabledWidgets,
setDashboardLayout: setEnabledWidgets
} = usePersonalization()
const { changeDashboardLayout: setDashboardLayout } = useUserPersonalization()
const isEnabled = useMemo(() => {
const { changeDashboardLayout: setDashboardLayout } = useUserPersonalization()
const isEnabled = useMemo(() => {
return Object.values(
JSON.stringify(enabledWidgets) !== '{}' ? enabledWidgets : { a: [] }
).some(e => e.find(i => i.i === id) !== undefined)

View File

@@ -38,11 +38,14 @@ export function useWidgets(): WidgetContextValue {
function WidgetProvider({ children }: { children: React.ReactNode }) {
const { modules } = useFederation()
const [federatedWidgets, setFederatedWidgets] = useState<
const [federatedWidgets, setFederatedWidgets] = useState<
Record<string, WidgetEntry>
>({})
const [loading, setLoading] = useState(true)
useEffect(() => {
const [loading, setLoading] = useState(true)
useEffect(() => {
async function loadFederatedWidgets() {
setLoading(true)

View File

@@ -7,7 +7,8 @@ import { toast } from '@lifeforge/ui'
function index() {
const navigate = useNavigate()
const { t } = useTranslation('common.fetch')
useEffect(() => {
useEffect(() => {
const a = document.createElement('a')
a.href = 'https://docs.lifeforge.dev'

View File

@@ -34,7 +34,8 @@ function CategoryItem({
}) {
const { open } = useModalStore()
const { categoryTranslations, refetch } = useFederation()
const {
const {
attributes,
listeners,
setNodeRef,

View File

@@ -38,11 +38,13 @@ function ModifyCategoryModal({
const { t } = useTranslation('common.module-manager')
const languagesQuery = useQuery(forgeAPI.locales.listLanguages.queryOptions())
const { open } = useModalStore()
const [data, setData] = useState({
const [data, setData] = useState({
key: category?.key || '',
value: Object.entries(category?.value || {})
})
const [aiLoading, setAiLoading] = useState(false)
const [aiLoading, setAiLoading] = useState(false)
function handleSubmit() {
if (!data.key.trim() || data.value.some(([_, v]) => !v.trim())) {

View File

@@ -37,7 +37,8 @@ function Categories() {
const { t } = useTranslation('common.module-manager')
const { open } = useModalStore()
const { categoryTranslations, modules, refetch } = useFederation()
const [items, setItems] = useState<
const [items, setItems] = useState<
Array<{ key: string; value: Record<string, string> }>
>([])
const missingKeys = useMemo(() => {

View File

@@ -48,7 +48,8 @@ function ModuleItem({
const { open } = useModalStore()
const { refetch: refetchFederation } = useFederation()
const devModeMutation = useMutation({
const devModeMutation = useMutation({
mutationFn: () =>
forgeAPI.modules.devMode.toggle.mutate({ moduleName: module.name }),
onSuccess: () => {

View File

@@ -36,7 +36,8 @@ function Modules() {
const { language } = usePersonalization()
const queryClient = useQueryClient()
const modulesQuery = useQuery(forgeAPI.modules.list.queryOptions())
const uninstallMutation = useMutation({
const uninstallMutation = useMutation({
mutationFn: (moduleName: string) =>
forgeAPI.modules.uninstall.mutate({ moduleName }),
onSuccess: (data, moduleName) => {

View File

@@ -22,7 +22,8 @@ function BgImageSelector() {
const { t } = useTranslation('common.personalization')
const { bgImage } = usePersonalization()
const { setBgImage, setBackdropFilters } = usePersonalization()
const handleAdjustBgImage = useCallback(() => {
const handleAdjustBgImage = useCallback(() => {
open(AdjustBgImageModal, {})
}, [])
const deleteMutation = useMutation(

View File

@@ -13,14 +13,18 @@ function AdjustBgImageModal({ onClose }: { onClose: () => void }) {
const { t } = useTranslation('common.personalization')
const { backdropFilters } = usePersonalization()
const { changeBackdropFilters } = useUserPersonalization()
const [bgBlur, setBgBlur] = useState<keyof typeof BG_BLURS>(
const [bgBlur, setBgBlur] = useState<keyof typeof BG_BLURS>(
backdropFilters.blur
)
const [bgBrightness, setBgBrightness] = useState(backdropFilters.brightness)
const [overlayOpacity, setOverlayOpacity] = useState(
const [bgBrightness, setBgBrightness] = useState(backdropFilters.brightness)
const [overlayOpacity, setOverlayOpacity] = useState(
backdropFilters.overlayOpacity
)
const [bgContrast, setBgContrast] = useState(backdropFilters.contrast)
const [bgContrast, setBgContrast] = useState(backdropFilters.contrast)
const [bgSaturation, setBgSaturation] = useState(backdropFilters.saturation)
const ADJUSTMENTS_COLUMNS = [

View File

@@ -18,10 +18,12 @@ import DefaultBgTempSelector from './components/DefaultBgTempSelector'
function BgTempSelector() {
const { bgTemp } = usePersonalization()
const { changeBgTemp } = useUserPersonalization()
const [customBgTemp, setCustomBgTemp] = useState<string>(
const [customBgTemp, setCustomBgTemp] = useState<string>(
bgTemp.startsWith('#') ? bgTemp : '#000000'
)
const { t } = useTranslation(['common.personalization', 'common.buttons'])
const { t } = useTranslation(['common.personalization', 'common.buttons'])
return (
<OptionsColumn

View File

@@ -21,10 +21,12 @@ function FontFamilySelectorModal({ onClose }: { onClose: () => void }) {
const { t } = useTranslation('common.personalization')
const { fontFamily } = usePersonalization()
const { changeFontFamily } = useUserPersonalization()
const [activeTab, setActiveTab] = useState<TabType>(
const [activeTab, setActiveTab] = useState<TabType>(
fontFamily.startsWith('custom:') ? 'custom' : 'google'
)
const [selectedFont, setSelectedFont] = useState<string | null>(fontFamily)
const [selectedFont, setSelectedFont] = useState<string | null>(fontFamily)
return (
<Stack gap="md" height="100%" minHeight="80vh" minWidth="60vw">

View File

@@ -50,7 +50,8 @@ function CustomFontUploadModal({
data: { openType: 'create' | 'edit'; initialData?: CustomFont }
}) {
const queryClient = useQueryClient()
const uploadMutation = useMutation(
const uploadMutation = useMutation(
forgeAPI.user.customFonts.upload
.input({
id: openType === 'edit' && initialData ? initialData.id : undefined

View File

@@ -35,7 +35,8 @@ function CustomFontSelector({
}) {
const { t } = useTranslation('common.personalization')
const { open } = useModalStore()
const customFontsQuery = useQuery(
const customFontsQuery = useQuery(
forgeAPI.user.customFonts.list.queryOptions()
)

View File

@@ -35,7 +35,8 @@ function FontListItem({
setSelectedFont: (font: string) => void
}) {
const queryClient = useQueryClient()
const togglePinMutation = useMutation(
const togglePinMutation = useMutation(
forgeAPI.user.personalization.toggleGoogleFontsPin.mutationOptions({
onSuccess: () => {
queryClient.invalidateQueries({

View File

@@ -48,18 +48,21 @@ function GoogleFontProvider({
setSelectedFont: (font: string | null) => void
}) {
const { fontFamily } = usePersonalization()
const fontsQuery = useQuery<{
const fontsQuery = useQuery<{
enabled: boolean
items: FontFamily[]
}>(forgeAPI.user.personalization.listGoogleFonts.queryOptions())
const pinnedFontsQuery = useQuery<string[]>(
forgeAPI.user.personalization.listGoogleFontsPin.queryOptions()
)
const [selectedCategory, setSelectedCategory] = useState<string | null>(null)
const [selectedCategory, setSelectedCategory] = useState<string | null>(null)
const [searchQuery, setSearchQuery] = useState<string>('')
const [page, setPage] = useState(1)
const scrollableRef = useRef<any>(null)
const categories = useMemo(
const categories = useMemo(
() => [...new Set(fontsQuery.data?.items.map(font => font.category))],
[fontsQuery.data?.items]
)

View File

@@ -21,7 +21,8 @@ function GoogleFontSelectorContent() {
})
.queryOptions()
)
const { pinnedFontsQuery, fontsQuery } = useGoogleFont()
const { pinnedFontsQuery, fontsQuery } = useGoogleFont()
return (
<WithQuery query={apiKeyAvailable}>

View File

@@ -21,7 +21,8 @@ function FontFamilySelector() {
const { t } = useTranslation('common.personalization')
const { open } = useModalStore()
const { fontFamily } = usePersonalization()
const customFontQuery = useQuery(
const customFontQuery = useQuery(
forgeAPI.user.customFonts.get
.input({
id: fontFamily.replace('custom:', '')

View File

@@ -18,10 +18,12 @@ import DefaultThemeColorSelector from './components/DefaultThemeColorSelector'
function ThemeColorSelector() {
const { rawThemeColor: themeColor } = usePersonalization()
const { changeThemeColor } = useUserPersonalization()
const [customThemeColor, setCustomThemeColor] = useState<string>(
const [customThemeColor, setCustomThemeColor] = useState<string>(
themeColor.startsWith('#') ? themeColor : '#000000'
)
const { t } = useTranslation(['common.personalization', 'common.buttons'])
const { t } = useTranslation(['common.personalization', 'common.buttons'])
return (
<OptionsColumn

View File

@@ -50,7 +50,8 @@ function UserPersonalizationProvider({
children: React.ReactNode
}) {
const { userData, setUserData } = useAuth()
const {
const {
setFontFamily,
setTheme,
setRawThemeColor,

View File

@@ -37,7 +37,8 @@ const queryClient = new QueryClient()
function Providers() {
const { open } = useModalStore()
const providers = useMemo(
const providers = useMemo(
() =>
// IMPORTANT: The order of these providers matters!
// From top to bottom, they are nested inside one another in that order.

View File

@@ -6,7 +6,8 @@ import { SidebarTitle, usePersonalization } from '@lifeforge/ui'
function MainSidebarTitle({ title }: { title: string }) {
const { categoryTranslations } = useFederation()
const { language } = usePersonalization()
const translatedTitle = useMemo(() => {
const translatedTitle = useMemo(() => {
const key = title.toLowerCase()
const translations = categoryTranslations[key]

View File

@@ -20,7 +20,8 @@ function SidebarBottomBar() {
const navigate = useNavigate()
const { sidebarExpanded, toggleSidebar } = useMainSidebarState()
const { userData, getAvatarURL, logout } = useAuth()
const handleNavigateToAccountSettings = useCallback(() => {
const handleNavigateToAccountSettings = useCallback(() => {
if (window.innerWidth < 1024) {
toggleSidebar()
}

View File

@@ -67,7 +67,8 @@ export default function SidebarEventBanner() {
const { sidebarExpanded } = useMainSidebarState()
const { derivedThemeColor: themeColor } = usePersonalization()
const { userData } = useAuth()
const eventType = useMemo(
const eventType = useMemo(
() => getEventType(userData?.dateOfBirth),
[userData?.dateOfBirth]
)

View File

@@ -22,7 +22,8 @@ function SidebarItems({ query }: { query: string }) {
const location = useLocation()
const { sidebarExpanded, toggleSidebar } = useMainSidebarState()
const [resolvedRoutes, setResolvedRoutes] = useState(modules)
const filteredRoutes = useMemo(
const filteredRoutes = useMemo(
() =>
resolvedRoutes.filter(
e =>

View File

@@ -16,7 +16,8 @@ export function useAppRouter() {
const { auth, authLoading } = useAuth()
const { modules, loading: modulesLoading } = useFederation()
const [appRouter, setAppRouter] = useState<DataRouter | null>(null)
const loadingRouter = useMemo(
const loadingRouter = useMemo(
() => createBrowserRouter(createAuthLoadingConfig()),
[]
)

View File

@@ -7,7 +7,8 @@ import { useFederation } from '@lifeforge/federation'
function useTitleEffect() {
const { modules } = useFederation()
const location = useLocation()
useEffect(() => {
useEffect(() => {
const target =
modules
.flatMap(e => e.items)