fixes
This commit is contained in:
Binary file not shown.
@@ -38,6 +38,7 @@ class UserUpdate(BaseModel):
|
|||||||
displayName: Optional[str] = None
|
displayName: Optional[str] = None
|
||||||
photoURL: Optional[str] = None
|
photoURL: Optional[str] = None
|
||||||
theme: Optional[str] = None
|
theme: Optional[str] = None
|
||||||
|
tutorial: Optional[bool] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
json_schema_extra = {
|
json_schema_extra = {
|
||||||
@@ -56,6 +57,7 @@ class User(BaseModel):
|
|||||||
createdAt: datetime
|
createdAt: datetime
|
||||||
updatedAt: datetime
|
updatedAt: datetime
|
||||||
theme: str = "light"
|
theme: str = "light"
|
||||||
|
tutorial: Optional[bool] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ async def get_user_by_email(email: str):
|
|||||||
"displayName": user.get("displayName"),
|
"displayName": user.get("displayName"),
|
||||||
"photoURL": user.get("photoURL"),
|
"photoURL": user.get("photoURL"),
|
||||||
"theme": user.get("theme", "light"),
|
"theme": user.get("theme", "light"),
|
||||||
|
"tutorial": user.get("tutorial"),
|
||||||
"createdAt": user["createdAt"].isoformat(),
|
"createdAt": user["createdAt"].isoformat(),
|
||||||
"updatedAt": user["updatedAt"].isoformat()
|
"updatedAt": user["updatedAt"].isoformat()
|
||||||
}
|
}
|
||||||
@@ -151,6 +152,7 @@ async def update_user(user_id: str, user_data: UserUpdate):
|
|||||||
"displayName": user.get("displayName"),
|
"displayName": user.get("displayName"),
|
||||||
"photoURL": user.get("photoURL"),
|
"photoURL": user.get("photoURL"),
|
||||||
"theme": user.get("theme", "light"),
|
"theme": user.get("theme", "light"),
|
||||||
|
"tutorial": user.get("tutorial"),
|
||||||
"createdAt": user["createdAt"].isoformat(),
|
"createdAt": user["createdAt"].isoformat(),
|
||||||
"updatedAt": user["updatedAt"].isoformat(),
|
"updatedAt": user["updatedAt"].isoformat(),
|
||||||
"message": "User updated successfully"
|
"message": "User updated successfully"
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ type MongoUser = {
|
|||||||
displayName?: string
|
displayName?: string
|
||||||
photoURL?: string
|
photoURL?: string
|
||||||
theme?: string
|
theme?: string
|
||||||
|
tutorial?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthContextValue = {
|
type AuthContextValue = {
|
||||||
@@ -211,8 +212,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
// Clear secret key from memory
|
// Clear secret key from memory
|
||||||
setSecretKey(null)
|
setSecretKey(null)
|
||||||
setMongoUser(null)
|
setMongoUser(null)
|
||||||
// Reset onboarding so tour shows again on next login
|
// Clear pending tour step (session state)
|
||||||
localStorage.removeItem('gj-onboarding-done')
|
|
||||||
localStorage.removeItem('gj-tour-pending-step')
|
localStorage.removeItem('gj-tour-pending-step')
|
||||||
// Keep device key and encrypted key for next login
|
// Keep device key and encrypted key for next login
|
||||||
// Do NOT clear localStorage or IndexedDB
|
// Do NOT clear localStorage or IndexedDB
|
||||||
|
|||||||
@@ -3,17 +3,8 @@ import { useNavigate } from 'react-router-dom'
|
|||||||
import { driver, type DriveStep } from 'driver.js'
|
import { driver, type DriveStep } from 'driver.js'
|
||||||
import 'driver.js/dist/driver.css'
|
import 'driver.js/dist/driver.css'
|
||||||
|
|
||||||
const ONBOARDING_KEY = 'gj-onboarding-done'
|
|
||||||
const TOUR_PENDING_KEY = 'gj-tour-pending-step'
|
const TOUR_PENDING_KEY = 'gj-tour-pending-step'
|
||||||
|
|
||||||
export function hasSeenOnboarding(): boolean {
|
|
||||||
return localStorage.getItem(ONBOARDING_KEY) === 'true'
|
|
||||||
}
|
|
||||||
|
|
||||||
export function markOnboardingDone(): void {
|
|
||||||
localStorage.setItem(ONBOARDING_KEY, 'true')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hasPendingTourStep(): string | null {
|
export function hasPendingTourStep(): string | null {
|
||||||
return localStorage.getItem(TOUR_PENDING_KEY)
|
return localStorage.getItem(TOUR_PENDING_KEY)
|
||||||
}
|
}
|
||||||
@@ -144,7 +135,6 @@ export function useOnboardingTour() {
|
|||||||
const driverObj = driver({
|
const driverObj = driver({
|
||||||
...driverDefaults(),
|
...driverDefaults(),
|
||||||
onDestroyStarted: () => {
|
onDestroyStarted: () => {
|
||||||
markOnboardingDone()
|
|
||||||
clearPendingTourStep()
|
clearPendingTourStep()
|
||||||
driverObj.destroy()
|
driverObj.destroy()
|
||||||
},
|
},
|
||||||
@@ -175,7 +165,6 @@ export function useOnboardingTour() {
|
|||||||
const driverObj = driver({
|
const driverObj = driver({
|
||||||
...driverDefaults(),
|
...driverDefaults(),
|
||||||
onDestroyStarted: () => {
|
onDestroyStarted: () => {
|
||||||
markOnboardingDone()
|
|
||||||
clearPendingTourStep()
|
clearPendingTourStep()
|
||||||
driverObj.destroy()
|
driverObj.destroy()
|
||||||
},
|
},
|
||||||
@@ -206,7 +195,6 @@ export function useOnboardingTour() {
|
|||||||
const driverObj = driver({
|
const driverObj = driver({
|
||||||
...driverDefaults(),
|
...driverDefaults(),
|
||||||
onDestroyStarted: () => {
|
onDestroyStarted: () => {
|
||||||
markOnboardingDone()
|
|
||||||
clearPendingTourStep()
|
clearPendingTourStep()
|
||||||
driverObj.destroy()
|
driverObj.destroy()
|
||||||
},
|
},
|
||||||
@@ -216,7 +204,6 @@ export function useOnboardingTour() {
|
|||||||
|
|
||||||
// Last settings step → navigate to /
|
// Last settings step → navigate to /
|
||||||
if (activeIndex === steps.length - 1) {
|
if (activeIndex === steps.length - 1) {
|
||||||
markOnboardingDone()
|
|
||||||
clearPendingTourStep()
|
clearPendingTourStep()
|
||||||
driverObj.destroy()
|
driverObj.destroy()
|
||||||
navigate('/')
|
navigate('/')
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export async function getUserByEmail(email: string, token: string) {
|
|||||||
|
|
||||||
export async function updateUserProfile(
|
export async function updateUserProfile(
|
||||||
userId: string,
|
userId: string,
|
||||||
updates: { displayName?: string; photoURL?: string; theme?: string },
|
updates: { displayName?: string; photoURL?: string; theme?: string; tutorial?: boolean },
|
||||||
token: string
|
token: string
|
||||||
) {
|
) {
|
||||||
return apiCall(`/users/${userId}`, {
|
return apiCall(`/users/${userId}`, {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
import { Link, useNavigate } from 'react-router-dom'
|
import { Link, useNavigate } from 'react-router-dom'
|
||||||
import { useState, useRef, useEffect } from 'react'
|
import { useState, useRef, useEffect } from 'react'
|
||||||
import { createEntry } from '../lib/api'
|
import { createEntry, updateUserProfile } from '../lib/api'
|
||||||
import { encryptEntry } from '../lib/crypto'
|
import { encryptEntry } from '../lib/crypto'
|
||||||
import BottomNav from '../components/BottomNav'
|
import BottomNav from '../components/BottomNav'
|
||||||
import WelcomeModal from '../components/WelcomeModal'
|
import WelcomeModal from '../components/WelcomeModal'
|
||||||
import { SaveBookAnimation } from '../components/SaveBookAnimation'
|
import { SaveBookAnimation } from '../components/SaveBookAnimation'
|
||||||
import { useOnboardingTour, hasSeenOnboarding, markOnboardingDone } from '../hooks/useOnboardingTour'
|
import { useOnboardingTour } from '../hooks/useOnboardingTour'
|
||||||
import { PageLoader } from '../components/PageLoader'
|
import { PageLoader } from '../components/PageLoader'
|
||||||
|
|
||||||
const AFFIRMATIONS = [
|
const AFFIRMATIONS = [
|
||||||
@@ -31,7 +31,7 @@ const SAVE_LEAVES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
const { user, userId, secretKey, loading } = useAuth()
|
const { user, userId, mongoUser, secretKey, loading } = useAuth()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [entry, setEntry] = useState('')
|
const [entry, setEntry] = useState('')
|
||||||
const [title, setTitle] = useState('')
|
const [title, setTitle] = useState('')
|
||||||
@@ -47,19 +47,26 @@ export default function HomePage() {
|
|||||||
|
|
||||||
// Check if onboarding should be shown after login
|
// Check if onboarding should be shown after login
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && user && userId && !hasSeenOnboarding()) {
|
if (!loading && user && userId && mongoUser && !mongoUser.tutorial) {
|
||||||
setShowWelcome(true)
|
setShowWelcome(true)
|
||||||
}
|
}
|
||||||
}, [loading, user, userId])
|
}, [loading, user, userId, mongoUser])
|
||||||
|
|
||||||
|
async function markTutorialDone() {
|
||||||
|
if (!user || !userId) return
|
||||||
|
const token = await user.getIdToken()
|
||||||
|
updateUserProfile(userId, { tutorial: true }, token).catch(console.error)
|
||||||
|
}
|
||||||
|
|
||||||
const handleStartTour = () => {
|
const handleStartTour = () => {
|
||||||
setShowWelcome(false)
|
setShowWelcome(false)
|
||||||
|
markTutorialDone()
|
||||||
startTour()
|
startTour()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSkipTour = () => {
|
const handleSkipTour = () => {
|
||||||
setShowWelcome(false)
|
setShowWelcome(false)
|
||||||
markOnboardingDone()
|
markTutorialDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default function LoginPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading || signingIn) {
|
||||||
return <PageLoader />
|
return <PageLoader />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user