Overview
Your Profile page is mission control for account management, achievement tracking, and privacy settings. Customize your identity, link multiple auth methods, and monitor your journaling progress.Four Main Tabs: Overview (goals + weekly reflection), Achievements, Activity (heatmap + history), Settings (profile + accounts + vault).
Profile Sidebar
User Information (app/profile/ProfilePageClient.tsx:545-652)
Profile Card
Displays:
- Avatar (from Google or uploaded image)
- Display name
- Bio (short description)
- Location (optional)
- Website link (optional)
- Wallet address (truncated: 0x1234…5678)
- ETH balance (from Wagmi)
- Member since date
Quick Stats
Your Impact:
- 📖 Total Stories
- ❤️ Total Likes
- 👁️ Total Views
- 🪙 Books Created
Stats update in real-time. Calculated on page load from Supabase queries.
Overview Tab
Writing Goals (app/profile/ProfilePageClient.tsx:690-722)
Daily Writing Streak
Tracks consecutive days with at least one story saved.Algorithm (ProfilePageClient.tsx:293-317):
- Map all stories to dates (yyyy-mm-dd format)
- Starting today, check each previous day
- Increment counter if day has stories
- Stop at first day with no activity
- Counts backward from today
- Allows “grace day” if today just started (haven’t posted yet)
- Resets to 0 if you miss a day
Monthly Stories Goal
Progress toward 10 stories per month.Calculation:
- Filters stories by current month + year
- Shows as “X/10 stories”
- Progress bar fills proportionally
Month is based on
created_at (when you saved), not story_date.Weekly Reflection (components/WeeklyReflection.tsx)
AI-Generated Weekly Summary
AI-Generated Weekly Summary
Automatic Insights:At the end of each week (Sunday), iStory generates:
- Summary of themes from week’s stories
- Emotional tone analysis
- Key moments highlighted
- Suggestions for next week
- Appears in Profile Overview tab
- Also accessible from Library “Weekly Reflections” section
- Saved to
weekly_reflectionstable
Requires at least 3 stories in past week to generate reflection.
Daily Journal Compilation (app/profile/ProfilePageClient.tsx:444-496)
Quick Book Creation
Quick Book Creation
If you recorded 2+ stories today:A special card appears:
- “Daily Recap Available”
- Shows count of today’s stories
- Compile Daily Journal button
- Bundles all today’s stories
- Creates IPFS metadata with date-specific title
- Mints NFT book (0.001 ETH fee)
- Saves to
bookstable - Updates book count stat
Achievements Tab
Achievement System (app/profile/ProfilePageClient.tsx:360-368)
First Story
Recorded your first journal entry.Unlocked: After saving 1 story.
Prolific Writer
Recorded 10+ stories.Unlocked: After saving 10 stories.
Community Star
Received 50+ total likes.Unlocked: When sum of all story likes ≥ 50.
Book Author
Compiled your first digital book.Unlocked: After minting 1 NFT book.
Daily Grinder
Wrote 3 stories in one day.Unlocked: When any single day has ≥ 3 stories.
Vocalist
Recorded a story with audio.Unlocked: After saving story with
has_audio: true.Visual States:
- 🎖️ Earned: Green background, checkmark, date shown
- ⚪ Locked: Gray background, reduced opacity
Activity Tab
Contribution Heatmap (app/profile/ProfilePageClient.tsx:769-841)
GitHub-Style Activity Grid
GitHub-Style Activity Grid
How it works:Grid Layout:
- Last 52 weeks displayed
- 7 rows (Sun-Sat) × ~52 columns
- Each cell = one day
- Aligned to Sunday as week start
- ⬜ Gray: No stories (0 count)
- 🟢 Light Green: 1 story
- 🟢 Medium Green: 2-3 stories
- 🟢 Dark Green: 4+ stories
- Stories mapped to dates via
created_at - Counts aggregated by date
- Hover shows date + story count tooltip
Recent Activity List (app/profile/ProfilePageClient.tsx:843-881)
Last 14 Days Breakdown
Last 14 Days Breakdown
Per-Day Statistics:For each active day in past 2 weeks:
- Date (e.g., “Monday, Mar 4”)
- 📖 Entry count (stories written)
- ❤️ Likes received (sum across day’s stories)
Only shows days with activity. Inactive days are hidden.
Settings Tab
Profile Settings Form (app/profile/ProfilePageClient.tsx:887-924)
Edit Profile Fields
Editable Information:
- Display Name: Your public name
- Username: Unique handle (alphanumeric + underscores)
- Bio: Short description (280 chars max recommended)
- Location: City/country (optional)
- Website: Personal site or social link
Changes save to
users table via Supabase update query.Save Changes
Click Save Changes button:What happens:
- Form validation (no empty required fields)
- Supabase update:
users.update(formData).eq('id', authInfo.id) - Success toast: “Profile updated!”
- Auto-switch to Overview tab
Linked Accounts (app/profile/ProfilePageClient.tsx:926-1027)
Managing Authentication Methods
Managing Authentication Methods
Linking Status:
Link Wallet (Google → Wallet):
| Account Type | Check | Indicator |
|---|---|---|
authInfo?.google_id exists | ✅ Linked (email) / ⬜ Not linked | |
| Wallet | authInfo?.wallet_address exists | ✅ Linked (0x123…456) / ⬜ Not linked |
- Sign in with Google
- Click Link Wallet in Settings
- If no wallet connected, RainbowKit modal opens
- After connection, sign message to prove ownership
- Server verifies signature via
/api/auth/link-account - Profile updated:
auth_provider: 'both'
- Sign in with wallet
- Click Link Google in Settings
- Sign message to get secure linking token
- OAuth flow initiates (stored in
sessionStorage) - After OAuth, AuthProvider matches token + completes link
- Profile updated with
google_id+auth_provider: 'both'
Security: Linking requires cryptographic signature to prevent account hijacking.
Vault Encryption
Local Vault Settings (components/vault/VaultSettings.tsx)
What is the Vault?
Client-side encryption layer:
- Stories encrypted with AES-256-GCM before saving to IndexedDB
- PIN never leaves your device
- DEK (Data Encryption Key) derived from PIN via PBKDF2 (100,000 iterations)
- KEK (Key Encryption Key) wraps DEK using AES-KW
- Decryption only possible when vault unlocked
Architecture: PIN → PBKDF2 → KEK → wraps DEK → encrypts content.
Set Up Vault
Initial Setup:
- Navigate to Profile → Settings → Local Vault
- Click Set Up Vault
- Create 6-digit PIN
- Confirm PIN
- Vault initialized (DEK generated, wrapped with PIN-derived KEK)
- Stored in IndexedDB:
vault.vault_keystable
Unlock Vault
Before accessing encrypted stories:
- Click Unlock Vault
- Enter 6-digit PIN
- KEK derived from PIN + stored salt
- DEK unwrapped with KEK
- DEK held in memory for session
- Encrypted stories decrypted on-demand
Vault auto-locks on sign-out. DEKs cleared from memory via
clearAllKeys().Vault Integration (lib/vault/)
How Vault Works with Stories
How Vault Works with Stories
Save Flow (with vault enabled):
- User saves story via
/record - Story saves to cloud (Supabase + IPFS)
- If vault unlocked:
getDEK(userId)retrieves DEK from memory - Title + content encrypted with
encryptString(text, dek) - Ciphertext + IV stored in IndexedDB
- Checksum (SHA-256) stored for integrity verification
- Linked to cloud via
cloud_idfield
useLocalStories()hook queries IndexedDB- Returns encrypted records
- If vault unlocked:
decryptString(ciphertext, iv, dek) - Decrypted stories rendered in UI
- If vault locked: Shows “Unlock vault to view” placeholder
Non-blocking: Vault save failure never prevents cloud save success.
Security Best Practices
Use Strong PIN
- Avoid simple patterns (123456, 111111)
- Don’t reuse PINs from other services
- 6 random digits recommended
Store PIN Securely
- Write it down physically (not digitally)
- Store in password manager
- Never share with anyone
Lock Vault When Away
- Click Lock Vault before stepping away
- Auto-locks on sign-out
- Prevents unauthorized access if device left open
Test Recovery
- Save test story with vault
- Lock vault, unlock with PIN
- Verify story decrypts correctly
Preferences
Notification Settings (app/profile/ProfilePageClient.tsx:1034-1069)
Email Notifications
Email Notifications
Toggle on/off:
- New follower alerts (future feature)
- Tip received notifications
- Weekly reflection summaries
- System announcements
Currently local UI state. Will persist to database in future update.
Public Profile
Public Profile
Controls social feed visibility:
- Public: Your public stories appear on social feed
- Private: All stories hidden from social feed (even public ones)
AI Enhancements
AI Enhancements
Auto-suggest improvements:
- Grammar/punctuation fixes
- Sentence flow improvements
- Clarity suggestions
Disabled by default. Enable to see AI suggestions after transcription.
Troubleshooting
Streak shows 0 but I posted yesterday
Streak shows 0 but I posted yesterday
Timezone Mismatch:
- Streak uses server timestamp (UTC)
- Your local time might be in different day
- Check
created_attimestamps in database - Post earlier in your timezone
- Streak recalculates on page load
Vault unlock fails - 'Invalid PIN'
Vault unlock fails - 'Invalid PIN'
Possible causes:
- Typo in PIN entry
- Caps Lock on (if using keyboard shortcuts)
- Browser IndexedDB corrupted
- Try entering PIN carefully
- Check browser console for crypto errors
- If truly forgotten, no recovery possible
- Clear IndexedDB and set up new vault (loses encrypted stories)
Profile picture not updating
Profile picture not updating
Avatar Source Priority:
- Manually uploaded avatar (future feature)
- Google OAuth avatar
- Default avatar based on first letter of name
- If using Google, update Google profile picture
- Sign out and sign in again
- Clear browser cache
Next Steps
Record More Stories
Build your streak and unlock achievements.
Explore Social Feed
Share your public stories with the community.