Inspiration

Every journal app I have tried asks me to be prolific. Blank page, streak counter, "you haven't written in 3 days." But the thing I actually do every night is smaller and less comfortable: I lie in bed and think, I should have said something in that meeting, or I wasted the whole evening. Those thoughts never make it into a journal because they are not nice, and by morning they are gone.

They are also not random. I noticed the same three or four of them coming back for years — mostly communication, mostly courage. That is the gap Undo exists to close: a journal that asks one uncomfortable question and then, quietly, shows you where your regrets keep clustering. Not to make you feel bad — to make the pattern impossible to ignore, because a pattern you can see is a pattern you can change.

What it does

Undo asks a single question before bed: "Is there anything today you wish you'd done differently?" You write freely — no character limit, as many notes in an evening as you need, and no cap on how old the journal can be — tag it with one of eight categories (Work, Money, Health, Relationships, Time, Courage, Communication, Other) and rate how heavily it sits on you.

Three tabs:

  • Check-in — the nightly question, the composer, tonight's stack of notes (each one editable, deletable, undoable), and a From the archive card that hands one evening from years ago back to you ("Ten years ago tonight") with a tap-through to that exact night.
  • Journal — the archive: pinned search, category filters, pages of 50 as you scroll, and a jump-to-month control that reaches any month that holds notes in one step.
  • Insights — your streak, a category donut, one plain-language sentence naming the pattern ("most of what you'd change this week sits in Communication"), and a monthly trend. Free users see the last 7 days; Premium unlocks the full history and the trend across months.

An unsaved draft is written to disk on every keystroke, so a killed app comes back mid-sentence. Everything is on-device: no account, no sync, no analytics, and the notes database is excluded from Android cloud backup. CSV export and import are deliberately free, because that file is the backup.

How I built it

Kotlin and Jetpack Compose, Material 3, single activity, three screens. Room holds one table; there is no backend at all.

The hard constraint I set early: the journal must survive decades. That ruled out the normal "load the list, filter in memory" approach. The Journal reads pages of 50 over a (date, createdAt) index with a date ceiling instead of OFFSET-ing past years; Insights takes every number straight from SQL (GROUP BY category, GROUP BY substr(date,1,7)); the streak comes from a list of distinct dates; the archive card picks its evening from those dates and then loads exactly one day. Nothing in the app ever holds the journal in memory. Against 20 years and 7,185 seeded notes, the first page plus the exact count comes back in about 40 ms warm.

WorkManager schedules the nightly reminder through a notification channel and tolerates Doze. Vico draws the donut and the trend. The insight sentence is pure aggregation over category templates — no AI, no network call.

Design was a real part of the build, not a coat of paint: one palette, one spacing scale and one panel component shared by all three tabs, a near-black plum "midnight observatory" background with a slow star field, a serif pull-quote for the nightly question, and a second premium theme (Midnight Reflection) that pushes the same hues deeper and colder.

How RevenueCat powers the purchase

The paywall loads the current RevenueCat offering and renders its packages with the real localised prices the SDK returns, then purchases or restores through purchases. Every premium decision in the app is derived from one place — entitlements["premium"].isActive — in purchase/RevenueCatManager.kt, so there is no second source of truth and no way for the UI to disagree with the store.

premium unlocks four things: the full history, the monthly trend, a custom reminder time, and the Midnight Reflection theme. Free users keep unlimited check-ins, the timeline, the 7-day insights, and CSV export/import.

Undo has not been released to a store yet, so the demo build runs against RevenueCat's Test Store — the same SDK, the same default offering, the same CustomerInfo flow, with sandbox transactions visible in the RevenueCat dashboard. No Apple or Google developer account is involved, and no real card is ever charged.

Challenges I faced

The empty-demo problem. A journal app installs empty by design — notes are written by hand and there is no per-day limit, so a fresh install has nothing to demo, screenshot or record. I added a debug-only Sample journal card that back-fills about 90 deterministic days, and a Twenty years card that seeds roughly 8,700 notes across 20 years, so the performance claim is checkable instead of asserted. Both are hidden in release builds.

Bugs that only exist on a device. Reviewing on the emulator rather than in the IDE found two things no amount of reading the code would have: the platform theme painted dark status-bar icons on the near-black canvas, so the clock was effectively invisible in every capture; and a Scaffold with a transparent container left LocalContentColor unset, so every Text relying on the default colour rendered almost black — headings were unreadable on the device while looking perfectly fine in code.

A journal that outlives its layout. Building for decades meant every read had to be re-thought, and it also produced the feature I am happiest with: the From the archive card, which only becomes possible once the journal is old enough to have a past.

The store gate I could not pass in time. I originally aimed at Google Play and found that a new personal Play Console account requires 12 testers opted in continuously for 14 days before you can even apply for production access — a schedule the submission window does not allow. That is part of why this is a Next Gen entry: the video and the open-source repo carry it, and no store listing is required.

What I learned

The API I re-learned the hard way is not holding data. Designing for twenty years forced SQL-level aggregation, paging and date ceilings into the app from the start, and every screen got simpler as a result. On the purchase side, the lesson was to keep entitlement state in exactly one place and let the UI read it — the moment premium logic is duplicated in a screen, the store and the app start disagreeing.

What's next

The obvious next step is a real store listing, which turns the Test Store build into a live one without touching the integration: only the API key changes. After that: optional encrypted export, a monthly "what changed" review, and a widget for the nightly question so the check-in is one tap from the home screen.

Undo is MIT-licensed and fully open source.

Built With

Share this project:

Updates

Submission history