feat(desktop): remember login credentials
Desktop Client Build / Resolve Build Metadata (push) Successful in 49s
Frontend CI / Frontend (push) Successful in 5m47s
Desktop Client Build / Build Desktop Client (push) Successful in 42m28s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 2m38s
Desktop Client Build / Resolve Build Metadata (push) Successful in 49s
Frontend CI / Frontend (push) Successful in 5m47s
Desktop Client Build / Build Desktop Client (push) Successful in 42m28s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 2m38s
This commit is contained in:
+10
@@ -17,6 +17,11 @@ declare global {
|
||||
keepRunningInBackground: boolean
|
||||
}
|
||||
|
||||
interface DesktopLoginCredentials {
|
||||
identifier: string
|
||||
password: string
|
||||
}
|
||||
|
||||
interface Window {
|
||||
desktopBridge: {
|
||||
app: {
|
||||
@@ -54,6 +59,11 @@ declare global {
|
||||
openExternalUrl(url: string): Promise<null>
|
||||
openSettingsWindow(): Promise<null>
|
||||
getAppSettings(): Promise<DesktopAppSettings>
|
||||
getLoginCredentials(): Promise<DesktopLoginCredentials | null>
|
||||
saveLoginCredentials(
|
||||
credentials: DesktopLoginCredentials,
|
||||
): Promise<DesktopLoginCredentials>
|
||||
clearLoginCredentials(): Promise<null>
|
||||
setAppSetting(key: keyof DesktopAppSettings, value: boolean): Promise<DesktopAppSettings>
|
||||
syncRuntimeSession(session: DesktopRuntimeSessionSyncRequest | null): Promise<null>
|
||||
rotateRuntimeClientToken(): Promise<DesktopClientRotateResponse>
|
||||
|
||||
@@ -7,15 +7,15 @@ const { apiBaseURL, error, pending, setApiBaseURL, login } = useDesktopSession()
|
||||
const menuOpen = ref(false)
|
||||
const showServerDialog = ref(false)
|
||||
const showPassword = ref(false)
|
||||
const rememberAccount = ref(!!localStorage.getItem('geo_rankly_saved_identifier'))
|
||||
const savedIdentifier =
|
||||
const legacySavedIdentifier =
|
||||
localStorage.getItem('geo_rankly_saved_identifier') ??
|
||||
localStorage.getItem('geo_rankly_saved_email') ??
|
||||
''
|
||||
const rememberCredentials = ref(!!legacySavedIdentifier)
|
||||
|
||||
const form = reactive({
|
||||
apiBaseURL: apiBaseURL.value,
|
||||
identifier: savedIdentifier,
|
||||
identifier: legacySavedIdentifier,
|
||||
password: '',
|
||||
})
|
||||
|
||||
@@ -25,8 +25,11 @@ function toggleMenu() {
|
||||
menuOpen.value = !menuOpen.value
|
||||
}
|
||||
|
||||
function onRememberChange(event: Event) {
|
||||
rememberAccount.value = (event.target as HTMLInputElement).checked
|
||||
async function onRememberChange(event: Event) {
|
||||
rememberCredentials.value = (event.target as HTMLInputElement).checked
|
||||
if (!rememberCredentials.value) {
|
||||
await clearSavedCredentials()
|
||||
}
|
||||
}
|
||||
|
||||
function handleOutsideClick(event: MouseEvent) {
|
||||
@@ -53,22 +56,58 @@ function saveServerSettings() {
|
||||
}
|
||||
|
||||
async function submitLogin() {
|
||||
localStorage.removeItem('geo_rankly_saved_password')
|
||||
if (rememberAccount.value) {
|
||||
localStorage.setItem('geo_rankly_saved_identifier', form.identifier)
|
||||
localStorage.removeItem('geo_rankly_saved_email')
|
||||
} else {
|
||||
localStorage.removeItem('geo_rankly_saved_identifier')
|
||||
localStorage.removeItem('geo_rankly_saved_email')
|
||||
}
|
||||
|
||||
await login({
|
||||
identifier: form.identifier,
|
||||
password: form.password,
|
||||
})
|
||||
await persistCredentialsPreference()
|
||||
await window.desktopBridge?.app?.setWindowMode?.('main', { source: 'login' })
|
||||
}
|
||||
|
||||
async function hydrateSavedCredentials() {
|
||||
try {
|
||||
const saved = await window.desktopBridge?.app?.getLoginCredentials?.()
|
||||
if (!saved) return
|
||||
form.identifier = saved.identifier
|
||||
form.password = saved.password
|
||||
rememberCredentials.value = true
|
||||
} catch (err) {
|
||||
console.warn('[login-view] load saved credentials failed', err)
|
||||
}
|
||||
}
|
||||
|
||||
async function clearSavedCredentials() {
|
||||
localStorage.removeItem('geo_rankly_saved_identifier')
|
||||
localStorage.removeItem('geo_rankly_saved_email')
|
||||
localStorage.removeItem('geo_rankly_saved_password')
|
||||
try {
|
||||
await window.desktopBridge?.app?.clearLoginCredentials?.()
|
||||
} catch (err) {
|
||||
console.warn('[login-view] clear saved credentials failed', err)
|
||||
}
|
||||
}
|
||||
|
||||
async function persistCredentialsPreference() {
|
||||
if (!rememberCredentials.value) {
|
||||
await clearSavedCredentials()
|
||||
return
|
||||
}
|
||||
|
||||
const identifier = form.identifier.trim()
|
||||
localStorage.setItem('geo_rankly_saved_identifier', identifier)
|
||||
localStorage.removeItem('geo_rankly_saved_email')
|
||||
localStorage.removeItem('geo_rankly_saved_password')
|
||||
|
||||
try {
|
||||
await window.desktopBridge?.app?.saveLoginCredentials?.({
|
||||
identifier,
|
||||
password: form.password,
|
||||
})
|
||||
} catch (err) {
|
||||
console.warn('[login-view] save credentials failed', err)
|
||||
}
|
||||
}
|
||||
|
||||
const lockCountdown = ref(0)
|
||||
let countdownTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
@@ -103,6 +142,7 @@ const displayError = computed(() => {
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('mousedown', handleOutsideClick)
|
||||
void hydrateSavedCredentials()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -178,8 +218,8 @@ onBeforeUnmount(() => {
|
||||
|
||||
<div class="options-row">
|
||||
<label class="check-label">
|
||||
<input type="checkbox" :checked="rememberAccount" @change="onRememberChange" />
|
||||
<span>记住账号</span>
|
||||
<input type="checkbox" :checked="rememberCredentials" @change="onRememberChange" />
|
||||
<span>记住账号和密码</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user