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