photo ui update
This commit is contained in:
59
src/App.css
59
src/App.css
@@ -1308,13 +1308,20 @@
|
||||
}
|
||||
|
||||
.settings-profile-name {
|
||||
margin: 0 0 0.3rem;
|
||||
margin: 0 0 0.15rem;
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
font-family: "Sniglet", system-ui;
|
||||
}
|
||||
|
||||
.settings-profile-email {
|
||||
margin: 0;
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-muted);
|
||||
font-family: "Sniglet", system-ui;
|
||||
}
|
||||
|
||||
.settings-profile-badge {
|
||||
display: inline-block;
|
||||
padding: 0.15rem 0.5rem;
|
||||
@@ -1360,8 +1367,7 @@
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto 0.5rem;
|
||||
cursor: pointer;
|
||||
margin: 0 auto 0.75rem;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -1387,30 +1393,44 @@
|
||||
font-family: "Sniglet", system-ui;
|
||||
}
|
||||
|
||||
.edit-modal-avatar-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
.edit-modal-photo-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.edit-modal-change-photo,
|
||||
.edit-modal-remove-photo {
|
||||
flex: 1;
|
||||
max-width: 130px;
|
||||
padding: 0.45rem 0.75rem;
|
||||
border-radius: 8px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
font-family: "Sniglet", system-ui;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.edit-modal-change-photo {
|
||||
background: var(--color-accent-light, #dcfce7);
|
||||
border: 1.5px solid var(--color-primary, #22c55e);
|
||||
color: var(--color-primary, #22c55e);
|
||||
}
|
||||
|
||||
.edit-modal-change-photo:hover {
|
||||
background: #bbf7d0;
|
||||
}
|
||||
|
||||
.edit-modal-remove-photo {
|
||||
display: block;
|
||||
margin: 0 auto 0.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
background: #fef2f2;
|
||||
border: 1.5px solid #fca5a5;
|
||||
color: #ef4444;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.edit-modal-remove-photo:hover {
|
||||
text-decoration: underline;
|
||||
background: #fee2e2;
|
||||
}
|
||||
|
||||
.edit-modal-save {
|
||||
@@ -1653,7 +1673,8 @@
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.875rem 1.125rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.9375rem;
|
||||
|
||||
@@ -164,7 +164,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
// Try to get existing user
|
||||
try {
|
||||
console.log('[Auth] Fetching user by email:', email)
|
||||
// console.log('[Auth] Fetching user by email:', email)
|
||||
const existingUser = await getUserByEmail(email, token) as MongoUser
|
||||
setUserId(existingUser.id)
|
||||
setMongoUser(existingUser)
|
||||
@@ -179,7 +179,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
},
|
||||
token
|
||||
) as MongoUser
|
||||
console.log('[Auth] Registered new user:', newUser.id)
|
||||
// console.log('[Auth] Registered new user:', newUser.id)
|
||||
setUserId(newUser.id)
|
||||
setMongoUser(newUser)
|
||||
syncReminderFromDb(newUser)
|
||||
|
||||
@@ -35,11 +35,11 @@ async function getFcmToken(): Promise<string | null> {
|
||||
}
|
||||
|
||||
const swReg = await navigator.serviceWorker.ready
|
||||
console.log('[FCM] Service worker ready:', swReg.active?.scriptURL)
|
||||
// console.log('[FCM] Service worker ready:', swReg.active?.scriptURL)
|
||||
|
||||
const token = await getToken(messaging, { vapidKey: VAPID_KEY, serviceWorkerRegistration: swReg })
|
||||
if (token) {
|
||||
console.log('[FCM] Token obtained:', token.slice(0, 20) + '…')
|
||||
// console.log('[FCM] Token obtained:', token.slice(0, 20) + '…')
|
||||
} else {
|
||||
console.warn('[FCM] getToken returned empty — VAPID key wrong or SW not registered?')
|
||||
}
|
||||
@@ -74,13 +74,13 @@ export async function enableReminder(
|
||||
}
|
||||
|
||||
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
console.log('[FCM] Saving token and reminder settings:', { timeStr, timezone })
|
||||
// console.log('[FCM] Saving token and reminder settings:', { timeStr, timezone })
|
||||
|
||||
await saveFcmToken(userId, fcmToken, authToken)
|
||||
console.log('[FCM] Token saved to backend')
|
||||
// console.log('[FCM] Token saved to backend')
|
||||
|
||||
await saveReminderSettings(userId, { time: timeStr, enabled: true, timezone }, authToken)
|
||||
console.log('[FCM] Reminder settings saved to backend')
|
||||
// console.log('[FCM] Reminder settings saved to backend')
|
||||
|
||||
localStorage.setItem(REMINDER_TIME_KEY, timeStr)
|
||||
localStorage.setItem(REMINDER_ENABLED_KEY, 'true')
|
||||
@@ -113,10 +113,10 @@ export async function listenForegroundMessages(): Promise<() => void> {
|
||||
const messaging = await messagingPromise
|
||||
if (!messaging) return () => {}
|
||||
|
||||
console.log('[FCM] Foreground message listener registered')
|
||||
// console.log('[FCM] Foreground message listener registered')
|
||||
|
||||
const unsubscribe = onMessage(messaging, (payload) => {
|
||||
console.log('[FCM] Foreground message received:', payload)
|
||||
// console.log('[FCM] Foreground message received:', payload)
|
||||
const title = payload.notification?.title || 'Grateful Journal 🌱'
|
||||
const body = payload.notification?.body || "You haven't written today yet."
|
||||
if (Notification.permission !== 'granted') {
|
||||
|
||||
@@ -348,6 +348,7 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
<div className="settings-profile-info">
|
||||
<h2 className="settings-profile-name">{displayName}</h2>
|
||||
{user?.email && <p className="settings-profile-email">{user.email}</p>}
|
||||
</div>
|
||||
<button type="button" className="settings-edit-btn" onClick={openEditModal} title="Edit profile">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
@@ -358,11 +359,11 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
|
||||
{/* Privacy & Security */}
|
||||
<section className="settings-section">
|
||||
<h3 className="settings-section-title">PRIVACY & SECURITY</h3>
|
||||
<div className="settings-card">
|
||||
{/* <section className="settings-section"> */}
|
||||
{/* <h3 className="settings-section-title">PRIVACY & SECURITY</h3> */}
|
||||
{/* <div className="settings-card"> */}
|
||||
{/* Passcode Lock — disabled for now, toggle is non-functional */}
|
||||
<div className="settings-item" style={{ opacity: 0.5 }}>
|
||||
{/* <div className="settings-item" style={{ opacity: 0.5 }}>
|
||||
<div className="settings-item-icon settings-item-icon-green">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||
@@ -382,7 +383,7 @@ export default function SettingsPage() {
|
||||
/>
|
||||
<span className="settings-toggle-slider" style={{ cursor: 'not-allowed' }}></span>
|
||||
</label>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* Face ID — commented out for future use */}
|
||||
{/*
|
||||
@@ -413,8 +414,8 @@ export default function SettingsPage() {
|
||||
</label>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
</section>
|
||||
{/* </div> */}
|
||||
{/* </section> */}
|
||||
|
||||
{/* App */}
|
||||
<section className="settings-section">
|
||||
@@ -477,7 +478,7 @@ export default function SettingsPage() {
|
||||
|
||||
{/* Data & Look */}
|
||||
<section className="settings-section">
|
||||
<h3 className="settings-section-title">DATA & LOOK</h3>
|
||||
<h3 className="settings-section-title">CUSTOMIZATION</h3>
|
||||
<div className="settings-card">
|
||||
{/* Export Journal — commented out for future use */}
|
||||
{/*
|
||||
@@ -580,11 +581,11 @@ export default function SettingsPage() {
|
||||
|
||||
{/* Clear Data */}
|
||||
<button type="button" className="settings-clear-btn" onClick={handleClearData}>
|
||||
<span>Clear All Data</span>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"></polyline>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||
</svg>
|
||||
<span>Clear All Data</span>
|
||||
</button>
|
||||
|
||||
{/* Sign Out */}
|
||||
@@ -650,7 +651,6 @@ export default function SettingsPage() {
|
||||
<div className="confirm-modal" onClick={(e) => e.stopPropagation()}>
|
||||
<h3 className="edit-modal-title">Edit Profile</h3>
|
||||
|
||||
<label className="edit-modal-avatar" style={{ cursor: 'pointer' }}>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
@@ -658,6 +658,7 @@ export default function SettingsPage() {
|
||||
style={{ display: 'none' }}
|
||||
onChange={handlePhotoSelect}
|
||||
/>
|
||||
<div className="edit-modal-avatar">
|
||||
{editPhotoPreview ? (
|
||||
<img src={editPhotoPreview} alt="Preview" className="edit-modal-avatar-img"
|
||||
onError={(e) => { (e.target as HTMLImageElement).style.display = 'none' }}
|
||||
@@ -667,13 +668,15 @@ export default function SettingsPage() {
|
||||
{editName.charAt(0).toUpperCase() || 'U'}
|
||||
</div>
|
||||
)}
|
||||
<div className="edit-modal-avatar-overlay">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" />
|
||||
<circle cx="12" cy="13" r="4" />
|
||||
</svg>
|
||||
</div>
|
||||
</label>
|
||||
<div className="edit-modal-photo-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="edit-modal-change-photo"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
Change photo
|
||||
</button>
|
||||
{editPhotoPreview && (
|
||||
<button
|
||||
type="button"
|
||||
@@ -683,6 +686,7 @@ export default function SettingsPage() {
|
||||
Remove photo
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<label className="confirm-modal-label" style={{ marginTop: '1rem' }}>Display Name</label>
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user