refactor(desktop): 라이선스 UI 빅뱅 — 키 입력 제거, 구독 기반 전환

- LicenseTab/LicenseModal: LemonSqueezy 키 입력 삭제, Payple 구독 UI로 전환
- useLicenseState 훅: 라이선스+클라우드 인증 공용 상태 관리 추출
- PREMIUM_MODEL_LIMITS: 3곳 중복 → @d3ro/core/constants 단일 소스
- i18n: LemonSqueezy 전용 키 18개 삭제, 구독 관련 키 13개 추가 (12 locale)
- DashboardPage: dashboard.model* → license.model* 키 통일
This commit is contained in:
윤찬 2026-04-12 19:54:04 +09:00
parent 68881bf0d0
commit 996def683b
17 changed files with 633 additions and 537 deletions

View file

@ -0,0 +1,87 @@
// src/renderer/hooks/useLicenseState.ts
// LicenseTab/LicenseModal 공용 — 라이선스 + 클라우드 인증 상태 관리
import { useState, useEffect, useCallback } from 'react'
import type {
LicenseInfo,
LicenseTier,
UsageQuota,
TierComparison,
} from '@d3ro/core/types'
interface CloudState {
authenticated: boolean
userEmail: string | null
}
export interface LicenseState {
licenseInfo: LicenseInfo | null
usage: UsageQuota[]
comparison: TierComparison[]
cloud: CloudState
currentTier: LicenseTier
isFree: boolean
isPro: boolean
handleUpgrade: (tier: 'pro' | 'pro_plus') => void
handleOpenBilling: () => void
openCloudSettings: () => void
}
export function useLicenseState(): LicenseState {
const [licenseInfo, setLicenseInfo] = useState<LicenseInfo | null>(null)
const [usage, setUsage] = useState<UsageQuota[]>([])
const [comparison, setComparison] = useState<TierComparison[]>([])
const [cloud, setCloud] = useState<CloudState>({ authenticated: false, userEmail: null })
const loadData = useCallback(() => {
window.electronAPI.license.getInfo().then((r) => {
if (r.success) setLicenseInfo(r.data)
})
window.electronAPI.license.getAllUsage().then((r) => {
if (r.success) setUsage(r.data)
})
window.electronAPI.license.getTierComparison().then((r) => {
if (r.success) setComparison(r.data)
})
window.electronAPI.cloudSync.getState().then((r) => {
if (r.success) setCloud({ authenticated: r.data.authenticated, userEmail: r.data.userEmail })
})
}, [])
useEffect(() => {
loadData()
const unsubTier = window.electronAPI.license.onTierChanged(() => loadData())
const unsubAuth = window.electronAPI.cloudSync.onAuthChanged((payload) => {
setCloud({ authenticated: payload.user !== null, userEmail: payload.user?.email ?? null })
loadData()
})
return () => { unsubTier(); unsubAuth() }
}, [loadData])
const currentTier = licenseInfo?.tier ?? 'free'
const handleUpgrade = useCallback((tier: 'pro' | 'pro_plus') => {
window.electronAPI.license.openBilling({ tier })
}, [])
const handleOpenBilling = useCallback(() => {
window.electronAPI.license.openBilling({ tier: 'pro' })
}, [])
const openCloudSettings = useCallback(() => {
window.dispatchEvent(new CustomEvent('d3ro:open-settings', { detail: { tab: 'cloud' } }))
}, [])
return {
licenseInfo,
usage,
comparison,
cloud,
currentTier,
isFree: currentTier === 'free',
isPro: currentTier === 'pro',
handleUpgrade,
handleOpenBilling,
openCloudSettings,
}
}