<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: moyunzero</title>
    <description>The latest articles on DEV Community by moyunzero (@moyunzero).</description>
    <link>https://dev.to/moyunzero</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1367918%2F61ba50b9-58d1-4e21-b538-ea40cb396aaa.png</url>
      <title>DEV Community: moyunzero</title>
      <link>https://dev.to/moyunzero</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/moyunzero"/>
    <language>en</language>
    <item>
      <title>Built a local-first mood journal in React Native — and the boring sync parts nearly broke it</title>
      <dc:creator>moyunzero</dc:creator>
      <pubDate>Sun, 18 Oct 2026 01:36:32 +0000</pubDate>
      <link>https://dev.to/moyunzero/built-a-local-first-mood-journal-in-react-native-and-the-boring-sync-parts-nearly-broke-it-5f6n</link>
      <guid>https://dev.to/moyunzero/built-a-local-first-mood-journal-in-react-native-and-the-boring-sync-parts-nearly-broke-it-5f6n</guid>
      <description>&lt;p&gt;After tense conversations, I used to open Notes, type half a paragraph, and delete it.&lt;/p&gt;

&lt;p&gt;Not because the feeling wasn't real—because Notes felt like the wrong container. Too easy to sync into places I didn't want. Too unstructured to help me see patterns later. And every "mood tracker" I tried pulled me toward something else: emoji grids that felt like homework, couple apps that need two people online, or chatty AI coaches that wouldn't leave me alone.&lt;/p&gt;

&lt;p&gt;What I actually needed was quieter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A private place to write emotions that are usually &lt;strong&gt;about someone&lt;/strong&gt;—without turning it into therapy homework or a social product.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I built &lt;strong&gt;MoodMO ** for myself first, in React Native / Expo. It is currently published on the **iOS App Store&lt;/strong&gt; —not Android. This post is about the problem, the lines I refused to cross, and a few sync/privacy decisions that burned real engineering time. If it sounds familiar, I'd love for you to try it on iOS and tell me what's still awkward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; This is a personal journaling tool, not therapy, diagnosis, or crisis support. If you need immediate help, contact local emergency services or a trusted hotline in your area.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem I was solving
&lt;/h2&gt;

&lt;p&gt;Most mood apps answer: &lt;em&gt;"How do you feel on a scale?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My nights looked more like: &lt;em&gt;"I'm upset **with this person&lt;/em&gt;&lt;em&gt;, about **this kind of trigger&lt;/em&gt;&lt;em&gt;, and I don't know whether to talk today, this week, or just digest it alone."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That relationship context kept falling out of generic trackers. Couple apps solve shared journaling. AI coaches solve conversation. I wanted a &lt;strong&gt;single-player&lt;/strong&gt; loop: record → later see whether things softened → optionally release words I no longer needed to keep staring at.&lt;/p&gt;

&lt;p&gt;Privacy wasn't a marketing line. If diary body, tags, or voice notes ever felt like telemetry fodder or social content, I simply wouldn't use the app myself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I deliberately did &lt;em&gt;not&lt;/em&gt; build
&lt;/h2&gt;

&lt;p&gt;Saying no early saved me from shipping the wrong product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No medical framing&lt;/strong&gt; — not a crisis tool, not CBT coursework.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No conversational AI coach&lt;/strong&gt; — no chat that follows you around (details in the next section).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No couple co-writing / no in-app social feed&lt;/strong&gt; — one person's relationship memory, not a timeline for others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-first by default&lt;/strong&gt; — write without an account; cloud sync is opt-in later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share cards that don't dump the diary by default&lt;/strong&gt; — weather/garden chrome + a closing line; optional short user snippet only if they opt in—not entry bodies or people/trigger lists by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those constraints shaped the UX metaphors more than any feature brainstorm did.&lt;/p&gt;




&lt;h2&gt;
  
  
  The AI I deliberately kept boring
&lt;/h2&gt;

&lt;p&gt;Optional extras are batch-style only: mood forecast text, a short reflective "podcast-style" blurb, suggested next steps, and one review closing line. There is &lt;strong&gt;no in-app "paste your API key" screen&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When AI runs, it is gated by a Groq key present at &lt;strong&gt;build time&lt;/strong&gt;. That key is an &lt;code&gt;EXPO_PUBLIC_&lt;/code&gt; env var, which means &lt;strong&gt;it ships inside the client bundle&lt;/strong&gt;—anyone who unpacks the IPA can extract it. That is a deliberate trade-off for a small indie app with &lt;strong&gt;no backend proxy&lt;/strong&gt;, not a claim that client-held provider keys are safe. If this grows, the right fix is a server-side proxy (and rotating whatever leaked). If the key is missing, those features fall back to on-device default copy.&lt;/p&gt;

&lt;p&gt;When the key &lt;em&gt;is&lt;/em&gt; present, calls send &lt;strong&gt;aggregates&lt;/strong&gt; (counts, averages, trigger labels)—not full diary paragraphs—but that is still third-party processing. Same honesty class as the at-rest gap below.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the product took shape
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Weather&lt;/strong&gt; for intensity. &lt;strong&gt;A garden&lt;/strong&gt; for growth. People/trigger tags, a gentle "when might I talk about this?", a person timeline, "on this day," a ritual to release words, and a small home-screen widget (&lt;strong&gt;Soft Stack&lt;/strong&gt;—garden/weather chrome only, no diary text).&lt;/p&gt;

&lt;p&gt;I am not claiming the best architecture in the industry—only consistency with the problem I set out to solve.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the boring parts actually worked
&lt;/h2&gt;

&lt;p&gt;This is the section I almost skipped—and the one that made sync feel trustworthy enough to ask strangers to try the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local-first storage (and an honest at-rest gap).&lt;/strong&gt; Entries live on-device first as JSON in AsyncStorage (guest key vs per-user key). Guest mode never requires a network. Optional sync uses Supabase when you sign in. Auth session tokens use SecureStore; &lt;strong&gt;diary entries do not&lt;/strong&gt;. Entries sit in the iOS app sandbox and rely on platform protections. I have &lt;strong&gt;not&lt;/strong&gt; added application-layer encryption at rest. That is a known gap, not a claim.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Soft delete vs "gone forever."&lt;/strong&gt; Soft delete stamps &lt;code&gt;deletedAt&lt;/code&gt; (recycle bin can restore). Permanent delete inserts a tombstone id for signed-in users; pull/push filter those ids so another device cannot resurrect a purged entry. Tombstones live in a cloud table with &lt;strong&gt;no automatic expiry job today&lt;/strong&gt;—the guarantee holds as long as those rows remain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upload and pull are separate actions—not one automatic pipeline.&lt;/strong&gt; In Profile you choose &lt;strong&gt;upload&lt;/strong&gt; (push) or &lt;strong&gt;pull&lt;/strong&gt; (recover from cloud). Pull shows a confirm dialog that cloud wins on id collision.&lt;/p&gt;

&lt;p&gt;Semantics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pull&lt;/strong&gt; → for each shared id, the server version replaces local; local-only ids stay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push&lt;/strong&gt; → only rows whose revision field (&lt;code&gt;updatedAt&lt;/code&gt;—&lt;strong&gt;not&lt;/strong&gt; a logical clock; a known simplification) advanced since the last successful sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result&lt;/strong&gt; → after you deliberately pull, devices look like the server for shared ids.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So yes: if you edit offline and &lt;strong&gt;pull without uploading first&lt;/strong&gt;, cloud can overwrite you. That is a deliberate trade-off with a confirm prompt—not a silent magic sync. Clearer "you have unsynced local edits" UX before pull is still on my list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Widget as hostile multi-process storage (iOS).&lt;/strong&gt; Soft Stack never receives diary body, people names, or audio paths—only a whitelist snapshot (weather, growth stage, active-count hint, schema version). Publish runs when weather recalculates; clear runs on logout, serialized so a late publish cannot overwrite a clear. On iOS the snapshot is JSON in an &lt;strong&gt;App Group &lt;code&gt;UserDefaults&lt;/code&gt; suite&lt;/strong&gt; shared with the widget extension. (Android widget code exists in the repo; &lt;strong&gt;Android is not published&lt;/strong&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Telemetry we refuse.&lt;/strong&gt; No Sentry. No sending diary body / tags / audio content to analytics. Cloud sync (when enabled) uses HTTPS and user-scoped access.&lt;/p&gt;

&lt;p&gt;None of that is novel. It is the glue that made "private journaling" feel true enough that I kept using the app myself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where I still need your eyes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Does the weather/garden language help—or get in the way after day three?&lt;/li&gt;
&lt;li&gt;Is recording fast enough for a bad evening?&lt;/li&gt;
&lt;li&gt;Do person tags + timelines help, or create pressure to "organize feelings"?&lt;/li&gt;
&lt;li&gt;Is the privacy story clear—especially once you know at-rest encryption and the client-held AI key are known gaps?&lt;/li&gt;
&lt;li&gt;What's missing in week two?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Critical notes are more useful than compliments.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it on iOS (and tell me what breaks)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;iPhone only&lt;/strong&gt; (App Store). Guest mode works offline; sync is optional after sign-in:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.apple.com/app/id6759703686" rel="noopener noreferrer"&gt;https://apps.apple.com/app/id6759703686&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;English and Chinese are in the app. Feedback in the comments—especially if you bounce—is exactly why I posted.&lt;/p&gt;

&lt;p&gt;Thanks for reading a builder note instead of a launch blast.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>showdev</category>
      <category>privacy</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>I shipped my first App Store puzzle app — and I’d love honest feedback</title>
      <dc:creator>moyunzero</dc:creator>
      <pubDate>Thu, 15 Oct 2026 01:03:18 +0000</pubDate>
      <link>https://dev.to/moyunzero/i-shipped-my-first-app-store-puzzle-app-and-id-love-honest-feedback-3nm3</link>
      <guid>https://dev.to/moyunzero/i-shipped-my-first-app-store-puzzle-app-and-id-love-honest-feedback-3nm3</guid>
      <description>&lt;p&gt;Hey everyone 👋&lt;/p&gt;

&lt;p&gt;I shipped my first App Store app a few months ago: &lt;strong&gt;Brainfool&lt;/strong&gt; .&lt;/p&gt;

&lt;p&gt;It’s a tiny daily puzzle loop:&lt;br&gt;
one game a day, chosen for you from Sudoku, Binary Puzzle, Nonogram, or Slitherlink.&lt;br&gt;
Offline. No ads. No rankings. Finish or bail → a slightly sharp closing line. Come back tomorrow.&lt;/p&gt;

&lt;p&gt;I built it as a quieter alternative to “homework factory” puzzle apps.&lt;/p&gt;

&lt;p&gt;I’m not fishing for generic praise. I’m trying to decide three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rules:&lt;/strong&gt; If you get Binary / Nonogram / Slitherlink, is the first-time intro enough to start filling — or do you bounce?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Roughly how long until finish or give-up? Too easy, about right, or stuck-frustrating?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The constraint:&lt;/strong&gt; After one game, do you want tomorrow — or do you wish you could play another right away?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;iOS, free: &lt;a href="https://apps.apple.com/app/id6770218110" rel="noopener noreferrer"&gt;https://apps.apple.com/app/id6770218110&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, a short answer to any of the three is perfect.&lt;br&gt;
If a rating/review feels fair after that, great — if not, the feedback still helps.&lt;/p&gt;

&lt;p&gt;Also curious: when you ask strangers to test something you shipped, what question format gets you the most useful replies?&lt;/p&gt;

</description>
      <category>ios</category>
      <category>mobile</category>
      <category>showdev</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
