[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fB9pPACv5Hfa3EpJxmg3D2WfHNcZs8Y12JhIw0-JyV1E":3,"$fPVLa3QldGkh2gxLB9oHCIps6lALH42uQdOc5h2-uf2U":22},{"id":4,"slug":5,"titleFr":6,"titleEn":7,"titleKm":8,"contentFr":9,"contentEn":10,"contentKm":10,"excerptFr":11,"excerptEn":12,"excerptKm":13,"tags":14,"createdAt":21,"updatedAt":21},110,"daily-pushup-tracker-52-jours","52 jours de pompes : comment j'ai gamifié ma discipline avec du code","52 days of pushups: how I gamified my discipline with code","រាំងដៃ ៥២ ថ្ងៃ៖ របៀបដែលខ្ញុំបានប្រើ gamification លើវិន័យរបស់ខ្ញុំជាមួយកូដ","## Introduction\n\nIl y a 52 jours, le 1er janvier 2026, j'ai pris une résolution. Pas une de ces grandes déclarations qu'on oublie mi-février — un truc simple, concret, mesurable : **faire des pompes tous les jours**.\n\nCe n'est pas la première fois que j'essaye. J'ai toujours fait du sport de manière intermittente. Mais avec le monde de la startup — les deadlines, les sprints, les soirées à debugger en prod — j'ai plusieurs fois perdu la motivation. Trois semaines d'affilée, puis plus rien pendant deux mois. Le schéma classique.\n\nCette fois, j'ai décidé de traiter le problème comme un développeur : avec du code, des données, et un système qui ne me laisse pas tricher.\n\n## Le problème : la motivation ne suffit pas\n\nLa motivation, c'est un pic. Ça monte fort le 1er janvier, et ça redescend doucement vers le 20. Les études en psychologie comportementale le montrent : **la motivation intrinsèque seule ne suffit pas pour maintenir une habitude**. Il faut un système.\n\nDuolingo l'a compris depuis longtemps. Leur mécanique de streak — cette série de jours consécutifs qu'on ne veut surtout pas casser — est un levier psychologique redoutable. Quand tu as 30 jours de streak, rater un jour te coûte émotionnellement bien plus que le simple effort de faire tes pompes.\n\nJ'ai décidé de construire le même mécanisme, mais pour mes pompes.\n\n## Version 1 : le composant web\n\nLe tracker est d'abord né dans mon portfolio. Un composant Vue intégré à mon site Nuxt, avec :\n- Un **stepper** (+5/-5) pour ajuster le nombre de pompes\n- Un **calendrier visuel** : les jours validés sont cochés en or, les jours manqués en rouge\n- Un **compteur de streak** bien visible\n- Des **statistiques** : total de pompes, nombre de jours, meilleur streak\n\nLe backend est minimal : trois endpoints Nitro (stats, entries, validate) qui tapent dans une table PostgreSQL sur Neon. Pas d'authentification, pas de complexité inutile. Juste moi et mes pompes.\n\nÇa a marché. Les 10 premiers jours, le simple fait de voir le calendrier se remplir m'a motivé. Au jour 20, je ne voulais plus casser la chaîne. Au jour 30, c'était devenu un réflexe.\n\n## Le problème du web sur mobile\n\nMais au quotidien, ouvrir un navigateur, taper l'URL, attendre le chargement... c'est trop de friction. La plupart du temps, je valide mes pompes le matin au réveil ou le soir avant de dormir. Sur mon téléphone. Et l'expérience web mobile n'est pas assez fluide pour un geste qu'on répète chaque jour.\n\nJ'ai donc construit une **application Android native**.\n\n## L'application Android\n\nL'app est écrite en **Kotlin** avec une architecture classique MVVM :\n- **Room** pour le cache local — même hors connexion, je vois mes données\n- **Retrofit 2** pour la synchronisation avec le backend Nuxt\n- **ViewModel + LiveData** pour un UI réactif\n- **SwipeRefreshLayout** pour le pull-to-refresh\n\nMais le vrai game-changer, c'est le **widget**.\n\n### Le widget qui change tout\n\nJ'ai ajouté un widget Android qui se place sur l'écran d'accueil. Il affiche le streak actuel et le statut du jour. **Chaque fois que j'allume mon téléphone, je le vois.** Impossible de l'ignorer. Impossible de se dire \"je ferai ça plus tard\".\n\nLe widget se synchronise en arrière-plan via WorkManager toutes les 30 minutes. Pas besoin d'ouvrir l'app pour voir où j'en suis.\n\nC'est cette combinaison — le streak psychologique + la visibilité permanente du widget — qui a fait tenir l'habitude pendant 52 jours sans interruption.\n\n## L'authentification Google OAuth\n\nAu départ, l'app était un outil personnel. Pas d'auth, pas de notion d'utilisateur. Mais j'ai voulu la rendre partageable — que d'autres personnes puissent l'utiliser avec leurs propres données.\n\nLe choix de l'authentification a été réfléchi. Le client principal est une app Android native. Les sessions/cookies sont pensées pour le web. J'ai opté pour une approche **stateless** :\n\n1. Android obtient un **Google ID Token** via Credential Manager\n2. Le token est envoyé dans le header `Authorization: Bearer` à chaque requête\n3. Le backend vérifie le token avec `google-auth-library` et upsert l'utilisateur\n\nPas de sessions à gérer côté serveur. Pas de cookies. Compatible avec le serverless de Vercel. Et l'expérience utilisateur est transparente : un tap sur \"Se connecter avec Google\" et c'est réglé.\n\n### La migration des données existantes\n\nLe moment délicat : j'avais déjà 52 jours de données sans notion d'utilisateur. La migration s'est faite en trois étapes :\n1. Ajout de la table `users` et de la colonne `userId` (nullable) dans `health_entries`\n2. Script de migration qui rattache toutes les entries existantes à mon compte\n3. Ajout de la contrainte unique composite `(userId, date)`\n\nZéro donnée perdue. Mon streak de 52 jours est intact.\n\n## Le light mode\n\nDétail qui compte : j'ai passé l'application en **light mode**. Fond beige `#F5F2EC`, le même que mon portfolio. Si vous avez lu mon article sur l'abandon du dark theme, vous savez pourquoi. Cohérence visuelle, lisibilité, et alignement avec ma conviction que le light mode est sous-estimé.\n\n## Les chiffres après 52 jours\n\n- **52 jours** de streak ininterrompu\n- **1 080 pompes** au total (20/jour en janvier, 25/jour depuis mi-février)\n- **0 jour manqué** depuis le 1er janvier\n- **Temps moyen** : moins de 2 minutes par session\n\nCe n'est pas un exploit sportif. 20-25 pompes par jour, c'est accessible à presque tout le monde. Mais la régularité sur 52 jours, ça c'est le résultat du système.\n\n## Ce que j'ai appris\n\n**La gamification fonctionne, même sur soi-même.** Je savais intellectuellement que les streaks sont un levier puissant. Le vivre au quotidien, c'est différent. Au jour 40, je me suis surpris à faire mes pompes à 23h55, malade, juste pour ne pas perdre le streak.\n\n**La friction est l'ennemi de l'habitude.** L'app web marchait, mais c'est le widget Android qui a tout changé. Réduire le nombre de taps entre \"j'y pense\" et \"c'est fait\" est crucial.\n\n**Over-engineering est tentant, mais inutile.** Trois endpoints, une table, un token. Pas de framework d'auth complexe, pas de Redis pour les sessions, pas de microservices. Le système le plus simple qui fonctionne.\n\n## Conclusion\n\nCe projet est petit. Pas de millions d'utilisateurs, pas d'architecture distribuée complexe. Mais c'est peut-être le projet dont je suis le plus satisfait. Parce qu'il résout un vrai problème — le mien — et qu'il le résout bien.\n\n52 jours et counting. L'objectif passera à 30 pompes en mars. Le streak continue.","## Introduction\n\n52 days ago, on January 1st, 2026, I made a resolution. Not one of those grand declarations you forget by mid-February — something simple, concrete, measurable: **do pushups every day**.\n\nIt's not the first time I've tried. I've always done sports intermittently. But with the startup world — deadlines, sprints, late-night production debugging — I've lost motivation several times. Three weeks straight, then nothing for two months. The classic pattern.\n\nThis time, I decided to tackle the problem like a developer: with code, data, and a system that won't let me cheat.\n\n## The Problem: Motivation Isn't Enough\n\nMotivation is a spike. It peaks on January 1st, and slowly fades by the 20th. Behavioral psychology studies show it: **intrinsic motivation alone isn't enough to maintain a habit**. You need a system.\n\nDuolingo figured this out long ago. Their streak mechanics — that consecutive day series you don't want to break — is a formidable psychological lever. When you have a 30-day streak, missing one day costs you emotionally far more than the simple effort of doing your pushups.\n\nI decided to build the same mechanism, but for my pushups.\n\n## Version 1: The Web Component\n\nThe tracker was first born in my portfolio. A Vue component integrated into my Nuxt site, with:\n- A **stepper** (+5/-5) to adjust the pushup count\n- A **visual calendar**: validated days checked in gold, missed days in red\n- A prominent **streak counter**\n- **Statistics**: total pushups, number of days, best streak\n\nThe backend is minimal: three Nitro endpoints (stats, entries, validate) hitting a PostgreSQL table on Neon. No authentication, no unnecessary complexity. Just me and my pushups.\n\nIt worked. The first 10 days, simply seeing the calendar fill up motivated me. By day 20, I didn't want to break the chain. By day 30, it was reflex.\n\n## The Problem with Web on Mobile\n\nBut in daily use, opening a browser, typing the URL, waiting for it to load... too much friction. Most of the time, I validate my pushups in the morning when waking up or at night before sleep. On my phone. And the mobile web experience isn't smooth enough for a gesture you repeat every day.\n\nSo I built a **native Android app**.\n\n## The Android App\n\nThe app is written in **Kotlin** with a classic MVVM architecture:\n- **Room** for local caching — even offline, I see my data\n- **Retrofit 2** for backend synchronization with Nuxt\n- **ViewModel + LiveData** for reactive UI\n- **SwipeRefreshLayout** for pull-to-refresh\n\nBut the real game-changer is the **widget**.\n\n### The Widget That Changes Everything\n\nI added an Android widget that sits on the home screen. It shows the current streak and today's status. **Every time I unlock my phone, I see it.** Impossible to ignore. Impossible to tell yourself \"I'll do it later.\"\n\nThe widget syncs in the background via WorkManager every 30 minutes. No need to open the app to see where I stand.\n\nIt's this combination — the psychological streak + the permanent visibility of the widget — that sustained the habit for 52 uninterrupted days.\n\n## Google OAuth Authentication\n\nInitially, the app was a personal tool. No auth, no user concept. But I wanted to make it shareable — so other people could use it with their own data.\n\nThe auth choice was deliberate. The main client is a native Android app. Sessions/cookies are designed for the web. I went with a **stateless** approach:\n\n1. Android gets a **Google ID Token** via Credential Manager\n2. The token is sent in the `Authorization: Bearer` header with every request\n3. The backend verifies the token with `google-auth-library` and upserts the user\n\nNo server-side sessions. No cookies. Vercel serverless compatible. And the user experience is seamless: one tap on \"Sign in with Google\" and you're in.\n\n### Migrating Existing Data\n\nThe tricky moment: I already had 52 days of data with no user concept. The migration happened in three steps:\n1. Add the `users` table and `userId` column (nullable) to `health_entries`\n2. Migration script to assign all existing entries to my account\n3. Add the composite unique constraint `(userId, date)`\n\nZero data lost. My 52-day streak is intact.\n\n## Light Mode\n\nA detail that matters: I switched the app to **light mode**. Beige background `#F5F2EC`, matching my portfolio. If you've read my article about abandoning dark theme, you know why. Visual consistency, readability, and alignment with my conviction that light mode is underrated.\n\n## The Numbers After 52 Days\n\n- **52 days** of unbroken streak\n- **1,080 pushups** total (20/day in January, 25/day since mid-February)\n- **0 days missed** since January 1st\n- **Average time**: less than 2 minutes per session\n\nThis isn't an athletic feat. 20-25 pushups a day is accessible to almost everyone. But the consistency over 52 days — that's the system at work.\n\n## What I Learned\n\n**Gamification works, even on yourself.** I knew intellectually that streaks are a powerful lever. Living it daily is different. On day 40, I caught myself doing pushups at 11:55 PM, sick, just to not lose the streak.\n\n**Friction is the enemy of habit.** The web app worked, but the Android widget changed everything. Reducing the number of taps between \"I think about it\" and \"it's done\" is crucial.\n\n**Over-engineering is tempting but useless.** Three endpoints, one table, one token. No complex auth framework, no Redis for sessions, no microservices. The simplest system that works.\n\n## Conclusion\n\nThis project is small. No millions of users, no complex distributed architecture. But it might be the project I'm most satisfied with. Because it solves a real problem — mine — and it solves it well.\n\n52 days and counting. The target will go up to 30 pushups in March. The streak continues.","Comment un simple défi de pompes quotidiennes est devenu une application Android avec widget, Google OAuth et 52 jours de streak ininterrompu.","How a simple daily pushup challenge became an Android app with widget, Google OAuth and 52 days of unbroken streak.","របៀបដែលបញ្ហាប្រឈមរាំងដៃប្រចាំថ្ងៃបានក្លាយជាកម្មវិធី Android ជាមួយ widget, Google OAuth និង streak ៥២ ថ្ងៃ។",[15,16,17,18,19,20],"Android","Kotlin","OAuth","Gamification","Health","Side Project","2026-02-27T19:00:00",[]]