v0.1.45 - 로그인 유지 옵션과 랜딩 문구 정리
This commit is contained in:
15
src/App.vue
15
src/App.vue
@@ -59,6 +59,7 @@ const authForm = reactive({
|
||||
nickname: '',
|
||||
email: '',
|
||||
password: '',
|
||||
rememberSession: false,
|
||||
})
|
||||
const goalForm = reactive({
|
||||
title: '',
|
||||
@@ -1062,6 +1063,7 @@ function resetAuthForm() {
|
||||
authForm.nickname = ''
|
||||
authForm.email = ''
|
||||
authForm.password = ''
|
||||
authForm.rememberSession = false
|
||||
}
|
||||
|
||||
function resetGoalForm() {
|
||||
@@ -1099,13 +1101,14 @@ function updateAuthField({ field, value }) {
|
||||
authForm[field] = value
|
||||
}
|
||||
|
||||
async function applyAuthSuccess(data) {
|
||||
async function applyAuthSuccess(data, persist = false) {
|
||||
authToken.value = data.token
|
||||
currentUser.value = data.user
|
||||
setSyncFeedback('cloud', '클라우드 동기화 연결됨')
|
||||
persistAuthState({
|
||||
token: data.token,
|
||||
user: data.user,
|
||||
persist,
|
||||
})
|
||||
await loadGoals()
|
||||
await hydratePlannerRecordsFromApi()
|
||||
@@ -1131,7 +1134,7 @@ async function submitAuthForm() {
|
||||
password: authForm.password,
|
||||
})
|
||||
|
||||
await applyAuthSuccess(result)
|
||||
await applyAuthSuccess(result, authMode.value === 'login' && authForm.rememberSession)
|
||||
} catch (error) {
|
||||
authMessage.value = toUserFacingApiError(error, '인증 처리 중 문제가 발생했습니다.')
|
||||
} finally {
|
||||
@@ -1156,6 +1159,7 @@ async function restoreAuthSession() {
|
||||
persistAuthState({
|
||||
token: savedAuth.token,
|
||||
user: result.user,
|
||||
persist: savedAuth.persist,
|
||||
})
|
||||
await loadGoals()
|
||||
await hydratePlannerRecordsFromApi()
|
||||
@@ -2015,12 +2019,13 @@ onBeforeUnmount(() => {
|
||||
>
|
||||
<div class="mx-auto flex max-w-2xl flex-col gap-6 text-center">
|
||||
<div>
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.28em] text-stone-500">회원 전용 플래너</p>
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.28em] text-stone-500">MEMBERS ONLY</p>
|
||||
<h2 class="mt-3 text-3xl font-semibold tracking-[-0.05em] text-stone-900 sm:text-4xl">
|
||||
10 Minute Planner
|
||||
오늘을 흘려보내지 않는<br>
|
||||
10분의 기록
|
||||
</h2>
|
||||
<p class="mx-auto mt-3 max-w-xl text-sm leading-7 text-stone-600 sm:text-base">
|
||||
로그인하면 날짜별 플래너, 통계, 목표를 내 계정 기준으로 이어서 사용할 수 있습니다.
|
||||
할 일, 집중 시간, 짧은 코멘트를 한 장의 다이어리로 남기고 내일의 첫 작업까지 이어가세요.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const emit = defineEmits([
|
||||
function updateField(field, event) {
|
||||
emit('update:field', {
|
||||
field,
|
||||
value: event.target.value,
|
||||
value: event.target.type === 'checkbox' ? event.target.checked : event.target.value,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -98,6 +98,24 @@ function updateField(field, event) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label
|
||||
v-if="mode === 'login'"
|
||||
class="flex items-start gap-3 rounded-2xl border border-stone-300/70 bg-white/55 px-4 py-3 text-left"
|
||||
>
|
||||
<input
|
||||
:checked="form.rememberSession"
|
||||
type="checkbox"
|
||||
class="mt-1 h-4 w-4 shrink-0 accent-stone-900"
|
||||
@change="updateField('rememberSession', $event)"
|
||||
/>
|
||||
<span>
|
||||
<span class="block text-xs font-bold tracking-[0.12em] text-stone-800">로그인 유지</span>
|
||||
<span class="mt-1 block text-[11px] font-semibold leading-5 tracking-[0.02em] text-stone-500">
|
||||
체크하지 않으면 브라우저를 닫을 때 로그인 정보가 사라집니다.
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<p
|
||||
v-if="message"
|
||||
class="rounded-2xl border border-stone-300 bg-white/80 px-4 py-3 text-sm font-semibold leading-6 text-stone-700"
|
||||
|
||||
@@ -28,29 +28,39 @@ async function request(path, { method = 'GET', token, body } = {}) {
|
||||
|
||||
export function readAuthState() {
|
||||
if (typeof window === 'undefined') {
|
||||
return { token: '', user: null }
|
||||
return { token: '', user: null, persist: false }
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(window.localStorage.getItem(AUTH_STORAGE_KEY) ?? '{"token":"","user":null}')
|
||||
const localState = JSON.parse(window.localStorage.getItem(AUTH_STORAGE_KEY) ?? 'null')
|
||||
|
||||
if (localState?.token) {
|
||||
return { ...localState, persist: true }
|
||||
}
|
||||
|
||||
const sessionState = JSON.parse(window.sessionStorage.getItem(AUTH_STORAGE_KEY) ?? 'null')
|
||||
|
||||
if (sessionState?.token) {
|
||||
return { ...sessionState, persist: false }
|
||||
}
|
||||
|
||||
return { token: '', user: null, persist: false }
|
||||
} catch (error) {
|
||||
console.warn('저장된 인증 상태를 불러오지 못했습니다.', error)
|
||||
return { token: '', user: null }
|
||||
return { token: '', user: null, persist: false }
|
||||
}
|
||||
}
|
||||
|
||||
export function persistAuthState({ token, user }) {
|
||||
export function persistAuthState({ token, user, persist = false }) {
|
||||
if (typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
window.localStorage.setItem(
|
||||
AUTH_STORAGE_KEY,
|
||||
JSON.stringify({
|
||||
token,
|
||||
user,
|
||||
}),
|
||||
)
|
||||
const targetStorage = persist ? window.localStorage : window.sessionStorage
|
||||
const unusedStorage = persist ? window.sessionStorage : window.localStorage
|
||||
|
||||
unusedStorage.removeItem(AUTH_STORAGE_KEY)
|
||||
targetStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify({ token, user }))
|
||||
}
|
||||
|
||||
export function clearAuthState() {
|
||||
@@ -59,6 +69,7 @@ export function clearAuthState() {
|
||||
}
|
||||
|
||||
window.localStorage.removeItem(AUTH_STORAGE_KEY)
|
||||
window.sessionStorage.removeItem(AUTH_STORAGE_KEY)
|
||||
}
|
||||
|
||||
export async function signup({ email, password, nickname }) {
|
||||
|
||||
Reference in New Issue
Block a user