Art Journal - Month 2 Update - main wins and struggles

Live: artjournal.ing

What’s New This Month

After launching last month, I focused on polish, user experience improvements, and squashing bugs that only surface when real users start using the app.

New Features

Journal Stats Dashboard - The My Journal tab now shows meaningful stats: total reflections, unique artists explored, venues visited, current streak (consecutive days journaling), and your most-used tag. Instead of “words written,” I converted it to “minutes spent reflecting on art” using 20 words/minute - because art reflection is slower than regular writing.

Prompt of the Day - Added a database of 109 art journaling prompts across categories (emotional reflection, memory, observation, meaning, personal connection, creative, museum visits, street art, music/performance). Users can refresh to get a new random prompt for inspiration.

Simplified Blog Content - Stripped out CTAs and carousels that interrupted reading flow. The “Why Modern Art Feels Confusing” article is now clean, readable prose.

Technical Improvements

Area Fix
Cache System Added user ID to cache keys to prevent cross-user data leakage
Like System Moved optimistic updates to parent component to fix count flickering
Auth State Added Supabase auth state listener to header for real-time session updates
Pull-to-Refresh Added overscroll-behavior-y: contain and tab persistence
Username Generation Database trigger now only adds numeric suffix if username is taken

Main Wins

  • Real user feedback loop - Having actual users exposed edge cases I never would have found in testing. Same browser, different accounts? Cache contamination. Pull down to refresh on Discovery? Redirects to My Journal.
  • Optimistic UI is tricky - The like button was updating in two places (card component AND parent), causing the count to jump from 3→4→2. Centralizing state management in the parent fixed it.
  • Session storage needs user scoping - Cache keys like feed-cache-my seemed fine until two users logged in on the same browser. Now it’s feed-cache-{userId}-my.

Main Challenges

  • Event timing on navigation - Dispatching window.dispatchEvent() before router.replace() doesn’t work because the receiving page isn’t mounted yet. Had to use sessionStorage flags (feed-needs-refresh) that the feed page checks on mount.
  • Native browser pull-to-refresh - Mobile browsers have their own pull-to-refresh that fights with custom implementations. CSS overscroll-behavior-y: contain was the fix.
  • Legacy data validation - Users with auto-generated usernames (ending in _1234) couldn’t save profile changes because validation ran on every save. Fixed by tracking originalUsername and only validating if changed.
  • Database trigger complexity - Updating the username generation trigger to “only add suffix if taken” required a loop that checks availability and increments - more complex than the original one-liner.

What I’m Still Struggling With

  • Cache invalidation is hard - Still finding edge cases where data doesn’t refresh when expected. The sessionStorage approach works but feels hacky.
  • Mobile-specific behaviors - Each mobile browser handles gestures differently. Testing on real devices catches things simulators miss.

Next Up

Planning to add some public profile stats (minutes reflecting, favorite tag) visible to others, and continuing to refine the mobile experience based on user feedback.

2 Likes