Inspiration
A few things - i found it hard to collect stories for my partner's birthday and then make a physical book. Later, when my son came along, I wanted one place to capture all the love we received when he was born, especially the stories of my mum who had passed away 4 years before he arrived.
What it does
Use Chorus to send a link out to family and friends to gather stories with images, voice and words to make into digital and physical books.
The organiser names the person and the occasion and gets one link. Everyone who opens it says who they are, gets a question to start them off, records up to twenty minutes or types it instead, adds a photo, and sends. No account, no install, nothing to pay, and nobody hears it until they send it. The organiser watches the names go up as they land, listens to every voice, edits the book, and orders it. The phone app adds the thing a link cannot do: hand somebody your phone in the room and record them there and then. It also has a second door, a book for a child as they grow, where a parent picks a rhythm and gets a prompt with the child's age on it.
How we built it
Used Claude, AssemblyAI, Supabase & Netlify.
The app is React Native and Expo with expo-router. It calls the same edge function the web contributor flow calls, with the same publishable key, so a recording made on the phone lands as a row like any other and the transcript starts on arrival. RevenueCat handles the two digital tiers in the store, with a webhook writing the tier onto the account. The printed keepsake opens the web till in a browser, because a physical book posted to the buyer is not something Play Billing may sell.
Challenges we ran into
The live app had a dead button. I installed it from Play and could not get past "Who's this from?" in Record someone, nor past the birthday screen in As they grow. The button was drawn and took no touch. Every screen sat in a flex: 1 View inside the ScrollView's content, which pins the page to the window height, so the page could never grow and never scroll. When the keyboard opened, Android resized the window, the page kept its full-height layout, and the buttons at the foot of it were drawn outside their parent's bounds. Android draws a child outside its parent and delivers no touch to it, so the button was visible and dead with no scroll to reach it. Fixed by putting the children straight into the ScrollView's content container with flexGrow on the container.
Then a policy one that could have cost us the listing. The paywall fell back to opening choruskeepsake.com whenever the store had no offer for a tier, which is every day until the two Play products exist. Google's rule is that digital content sold inside an Android app goes through the store's own till and the app may not send the buyer out to a web checkout for it. That build went to the internal track and was never promoted. Now, when the store is present and has nothing to sell, the two digital cards are not drawn at all, and only the printed keepsake stays, because a physical thing is allowed to be bought on the web.
The one still open is not code. No in-app product can be created on the developer account until the bank account on the payments profile is verified, and that verification is not something I can complete yet. So the app is live selling nothing, which is exactly what Play's rule wants of an app with no products.
Accomplishments that we're proud of
1.0.0 went to Google Play production and Google approved it overnight.
The RevenueCat webhook is proved end to end rather than assumed. Send Test Event came back 200 with the function's own reply, so the URL was right, the shared secret matched, and the code took the branch it should.
Orders carry a store and a unique store transaction id, so a retried webhook delivery cannot become a second order.
No price is written in the app. It reads the live price file when the screen opens and gives up after six seconds, so a phone on a bad line cannot leave the paywall spinning.
What we learned
A phone app is proved on a phone. This one went to Google Play having only ever run in a browser pane, where the window never shrinks for a keyboard, so the fault that broke the first screen could not appear. A preview APK is minutes of work and would have caught it before a single person installed it. There are four assertions on that layout in the build check now, and each one was proved by restoring the broken file and watching it fail.
The account admin is the long pole, not the code. Entitlements, webhook, migration and store permissions took an evening. A bank verification does not care how fast you write.
What's next for Chorus
The bank verification, then the two Play products with their prices read from the live file, then one licence-tester purchase watched in the webhook log.
iOS, once the Apple Developer Program enrolment clears.
As they grow for both parents. The birthday, the rhythm and the role live on one phone today, so a second parent signing in sees no book at all. That needs the settings on the book in the database, a way to put a second person on a book, and the reminder firing on each person's own phone.
Built With
- android
- assemblyai
- claude
- expo.io
- netlify
- react-native
- revenuecat
- stripe
- supabase
- typescript
Log in or sign up for Devpost to join the conversation.