feat: initial MetaBloom cross-platform MVP

This commit is contained in:
AI Company
2026-07-21 18:36:41 +00:00
commit 710d672307
18 changed files with 6481 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
{
"enabledPlugins": {
"expo@claude-plugins-official": true
}
}
+5
View File
@@ -0,0 +1,5 @@
node_modules/
dist/
.expo/
*.log
.DS_Store
+3
View File
@@ -0,0 +1,3 @@
# Expo HAS CHANGED
Read the exact versioned docs at https://docs.expo.dev/versions/v57.0.0/ before writing any code.
+56
View File
@@ -0,0 +1,56 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { LinearGradient } from 'expo-linear-gradient';
import { StatusBar } from 'expo-status-bar';
import React, { useEffect, useMemo, useState } from 'react';
import { Alert, Pressable, SafeAreaView, ScrollView, StyleSheet, Text, TextInput, View, useWindowDimensions } from 'react-native';
type Tab = 'today' | 'journal' | 'progress' | 'profile';
type Entry = { date: string; weight: number; water: number; steps: number; glucose?: number };
const C = { ink:'#15211c', muted:'#637169', cream:'#f6f7f2', white:'#fff', green:'#1d6b50', mint:'#dcefe5', coral:'#ef835f', yellow:'#f4d58d', line:'#e5e9e3' };
const seed: Entry[] = [
{date:'Lun',weight:91.4,water:1500,steps:6100,glucose:112},{date:'Mar',weight:91.2,water:1800,steps:7400,glucose:108},
{date:'Mer',weight:91.1,water:1600,steps:6900,glucose:110},{date:'Jeu',weight:90.9,water:2000,steps:8200,glucose:105},
{date:'Ven',weight:90.8,water:2100,steps:8600,glucose:104},{date:'Sam',weight:90.7,water:1750,steps:7200,glucose:107},{date:'Auj.',weight:90.6,water:1250,steps:6240,glucose:103},
];
function Card({children, style}: any){return <View style={[s.card,style]}>{children}</View>}
function Pill({children, tone='green'}:any){return <View style={[s.pill,{backgroundColor:tone==='coral'?'#fff0eb':C.mint}]}><Text style={[s.pillText,{color:tone==='coral'?'#9b432a':C.green}]}>{children}</Text></View>}
function Metric({emoji,label,value,sub,onPress}:any){return <Pressable onPress={onPress} style={({pressed})=>[s.metric,pressed&&{opacity:.7}]}><Text style={s.metricEmoji}>{emoji}</Text><Text style={s.metricLabel}>{label}</Text><Text style={s.metricValue}>{value}</Text><Text style={s.metricSub}>{sub}</Text></Pressable>}
function ProgressBar({value,color=C.green}:any){return <View style={s.track}><View style={[s.fill,{width:`${Math.min(value,100)}%`,backgroundColor:color}]}/></View>}
function Nav({tab,setTab}:any){const items:[Tab,string,string][]=[['today','⌂','Aujourdhui'],['journal','','Journal'],['progress','↗','Progrès'],['profile','○','Profil']];return <View style={s.nav}>{items.map(([id,icon,label])=><Pressable accessibilityRole="button" key={id} onPress={()=>setTab(id)} style={s.navItem}><Text style={[s.navIcon,tab===id&&s.active]}>{icon}</Text><Text style={[s.navLabel,tab===id&&s.active]}>{label}</Text></Pressable>)}</View>}
export default function App(){
const [tab,setTab]=useState<Tab>('today'); const [entries,setEntries]=useState(seed); const [water,setWater]=useState(1250); const [steps,setSteps]=useState(6240);
const [weight,setWeight]=useState('90.6'); const [glucose,setGlucose]=useState('103'); const [saved,setSaved]=useState(false); const {width}=useWindowDimensions();
useEffect(()=>{AsyncStorage.getItem('metabloom').then(v=>{if(v){const d=JSON.parse(v);setEntries(d.entries||seed);setWater(d.water||1250);setSteps(d.steps||6240)}}).catch(()=>{})},[]);
useEffect(()=>{AsyncStorage.setItem('metabloom',JSON.stringify({entries,water,steps})).catch(()=>{})},[entries,water,steps]);
const score=Math.round((Math.min(water/2000,1)+Math.min(steps/8000,1)+.82+.75)*25);
const save=()=>{const w=parseFloat(weight.replace(',','.')); const g=parseFloat(glucose); if(!w||w<30||w>300){Alert.alert('Valeur à vérifier','Saisis un poids compris entre 30 et 300 kg.');return} setEntries(x=>[...x.slice(0,6),{date:'Auj.',weight:w,water,steps,glucose:g||undefined}]);setSaved(true);setTimeout(()=>setSaved(false),2200)};
const content=useMemo(()=>tab==='today'?<Today score={score} water={water} setWater={setWater} steps={steps} setSteps={setSteps} go={setTab}/>:tab==='journal'?<Journal weight={weight} setWeight={setWeight} glucose={glucose} setGlucose={setGlucose} water={water} setWater={setWater} steps={steps} setSteps={setSteps} save={save} saved={saved}/>:tab==='progress'?<Progress entries={entries}/>:<Profile reset={()=>{setEntries(seed);setWater(1250);setSteps(6240);AsyncStorage.removeItem('metabloom')}}/>,[tab,score,water,steps,weight,glucose,saved,entries]);
return <SafeAreaView style={s.safe}><StatusBar style="dark"/><View style={[s.shell,{maxWidth:width>900?1120:640}]}>{content}<Nav tab={tab} setTab={setTab}/></View></SafeAreaView>
}
function Header({eyebrow,title,right}:any){return <View style={s.header}><View><Text style={s.eyebrow}>{eyebrow}</Text><Text style={s.title}>{title}</Text></View>{right}</View>}
function Today({score,water,setWater,steps,setSteps,go}:any){return <ScrollView contentContainerStyle={s.page} showsVerticalScrollIndicator={false}>
<Header eyebrow="MARDI 21 JUILLET" title="Bonjour Vincent 👋" right={<View style={s.avatar}><Text style={s.avatarText}>V</Text></View>}/>
<LinearGradient colors={['#1d6b50','#2d8063']} style={s.hero}><View style={{flex:1}}><Pill>Aujourdhui</Pill><Text style={s.heroTitle}>Chaque petit pas compte.</Text><Text style={s.heroText}>Tu avances à ton rythme, sans pression. Encore une action douce pour compléter ta journée.</Text><Pressable onPress={()=>go('journal')} style={s.heroButton}><Text style={s.heroButtonText}>Ajouter une donnée </Text></Pressable></View><View style={s.ring}><Text style={s.ringBig}>{score}</Text><Text style={s.ringSmall}>/ 100</Text></View></LinearGradient>
<View style={s.sectionHead}><Text style={s.h2}>Mes essentiels</Text><Text style={s.caption}>Objectifs ajustables</Text></View>
<View style={s.grid}><Metric emoji="💧" label="Hydratation" value={`${water/1000} L`} sub="sur 2 L" onPress={()=>setWater((x:number)=>Math.min(x+250,2500))}/><Metric emoji="👟" label="Mouvement" value={steps.toLocaleString('fr-FR')} sub="sur 8 000 pas" onPress={()=>setSteps((x:number)=>x+500)}/><Metric emoji="🥗" label="Repas équilibrés" value="2 / 3" sub="très bien"/><Metric emoji="🌙" label="Sommeil" value="7 h 12" sub="objectif 8 h"/></View>
<Card style={s.tip}><View style={s.tipIcon}><Text style={{fontSize:22}}></Text></View><View style={{flex:1}}><Text style={s.tipTag}>PETIT CONSEIL DU JOUR</Text><Text style={s.tipTitle}>Une marche de 10 minutes après le repas ?</Text><Text style={s.body}>Un peu de mouvement aide à se sentir mieux. Adapte toujours leffort à ta situation.</Text></View></Card>
<Text style={s.disclaimer}>MetaBloom accompagne vos habitudes. Il ne remplace ni un diagnostic, ni un professionnel de santé.</Text>
</ScrollView>}
function Journal({weight,setWeight,glucose,setGlucose,water,setWater,steps,setSteps,save,saved}:any){return <ScrollView contentContainerStyle={s.page} keyboardShouldPersistTaps="handled"><Header eyebrow="SUIVI SIMPLE" title="Mon journal" right={<Pill>🔒 Local</Pill>}/><Text style={s.lead}>Note ce qui compte pour toi. Rien nest obligatoire.</Text>
<Card><Text style={s.h2}>Mesures du jour</Text><Text style={s.inputLabel}>Poids <Text style={s.optional}>facultatif</Text></Text><View style={s.inputRow}><TextInput accessibilityLabel="Poids en kilogrammes" value={weight} onChangeText={setWeight} keyboardType="decimal-pad" style={s.input}/><Text style={s.unit}>kg</Text></View><Text style={s.inputLabel}>Glycémie <Text style={s.optional}>facultatif · saisie manuelle</Text></Text><View style={s.inputRow}><TextInput accessibilityLabel="Glycémie" value={glucose} onChangeText={setGlucose} keyboardType="decimal-pad" style={s.input}/><Text style={s.unit}>mg/dL</Text></View><Text style={s.helper}>Ces valeurs servent uniquement à ton suivi personnel. Aucune interprétation médicale nest fournie.</Text></Card>
<Card><Text style={s.h2}>Habitudes</Text><Stepper icon="💧" label="Eau" value={`${water} ml`} minus={()=>setWater(Math.max(0,water-250))} plus={()=>setWater(water+250)}/><Stepper icon="👟" label="Pas" value={steps.toLocaleString('fr-FR')} minus={()=>setSteps(Math.max(0,steps-500))} plus={()=>setSteps(steps+500)}/></Card>
<Pressable onPress={save} style={({pressed})=>[s.primary,pressed&&{opacity:.8}]}><Text style={s.primaryText}>{saved?'✓ Journée enregistrée':'Enregistrer ma journée'}</Text></Pressable>
</ScrollView>}
function Stepper({icon,label,value,minus,plus}:any){return <View style={s.stepper}><Text style={{fontSize:24}}>{icon}</Text><View style={{flex:1}}><Text style={s.metricLabel}>{label}</Text><Text style={s.stepValue}>{value}</Text></View><Pressable onPress={minus} style={s.stepBtn}><Text style={s.stepBtnText}></Text></Pressable><Pressable onPress={plus} style={s.stepBtn}><Text style={s.stepBtnText}>+</Text></Pressable></View>}
function Progress({entries}:any){const min=Math.min(...entries.map((e:Entry)=>e.weight)); const max=Math.max(...entries.map((e:Entry)=>e.weight));return <ScrollView contentContainerStyle={s.page}><Header eyebrow="7 DERNIERS JOURS" title="Mes progrès" right={<Pill tone="coral"> 0,8 kg</Pill>}/><Text style={s.lead}>Observe les tendances, pas la perfection.</Text><Card><View style={s.sectionHead}><View><Text style={s.metricLabel}>Tendance de poids</Text><Text style={s.bigNumber}>{entries.at(-1).weight} <Text style={s.bigUnit}>kg</Text></Text></View><Pill>Progression douce</Pill></View><View style={s.chart}>{entries.map((e:Entry)=><View key={e.date} style={s.barCol}><Text style={s.barValue}>{e.weight}</Text><View style={[s.bar,{height:58+((max-e.weight)/(max-min||1))*55}]}/><Text style={s.barLabel}>{e.date}</Text></View>)}</View></Card>
<View style={s.grid}><Card style={s.stat}><Text style={s.statIcon}>💧</Text><Text style={s.bigNumber}>1,7 L</Text><Text style={s.body}>moyenne deau</Text></Card><Card style={s.stat}><Text style={s.statIcon}>👟</Text><Text style={s.bigNumber}>7 436</Text><Text style={s.body}>pas en moyenne</Text></Card></View>
<Card style={s.insight}><Text style={s.tipTag}>À RETENIR</Text><Text style={s.tipTitle}>Une semaine régulière 🌱</Text><Text style={s.body}>Tu as bougé au moins 6 000 pas chaque jour. Cette régularité compte davantage quune journée parfaite.</Text></Card></ScrollView>}
function Profile({reset}:any){return <ScrollView contentContainerStyle={s.page}><Header eyebrow="MON ESPACE" title="Profil & sécurité"/><Card style={s.profile}><View style={s.avatarLarge}><Text style={s.avatarLargeText}>V</Text></View><View><Text style={s.tipTitle}>Vincent</Text><Text style={s.body}>Objectif : habitudes durables</Text></View></Card><Text style={s.h2}>Préférences</Text>{[['🎯','Objectif principal','Équilibre métabolique'],['⚖️','Unité de poids','Kilogrammes (kg)'],['🩸','Unité glycémie','mg/dL'],['🔔','Rappels','À configurer']].map(x=><Card key={x[1]} style={s.setting}><Text style={{fontSize:22}}>{x[0]}</Text><View style={{flex:1}}><Text style={s.metricLabel}>{x[1]}</Text><Text style={s.body}>{x[2]}</Text></View><Text style={s.chevron}></Text></Card>)}<Card style={s.safety}><Text style={s.tipTag}>SÉCURITÉ & CONFIDENTIALITÉ</Text><Text style={s.tipTitle}>Vos données restent sur cet appareil.</Text><Text style={s.body}>Ce prototype nenvoie aucune donnée vers un serveur. Avant un usage réel, une validation clinique, RGPD/HDS et réglementaire sera nécessaire.</Text></Card><Pressable onPress={()=>Alert.alert('Effacer les données locales ?','Cette action réinitialise la démonstration.',[{text:'Annuler'},{text:'Effacer',style:'destructive',onPress:reset}])} style={s.danger}><Text style={s.dangerText}>Effacer mes données locales</Text></Pressable><Text style={s.disclaimer}>En cas de malaise ou durgence, contactez immédiatement les services durgence. MetaBloom ne fournit pas de conseil thérapeutique.</Text></ScrollView>}
const s=StyleSheet.create({safe:{flex:1,backgroundColor:C.cream},shell:{flex:1,width:'100%',alignSelf:'center'},page:{padding:20,paddingTop:26,paddingBottom:120,gap:16},header:{flexDirection:'row',justifyContent:'space-between',alignItems:'center',marginBottom:4},eyebrow:{fontSize:11,fontWeight:'800',letterSpacing:1.4,color:C.green},title:{fontSize:28,fontWeight:'800',color:C.ink,letterSpacing:-.7,marginTop:4},avatar:{width:44,height:44,borderRadius:22,backgroundColor:C.yellow,alignItems:'center',justifyContent:'center'},avatarText:{fontWeight:'800',color:C.ink,fontSize:17},hero:{borderRadius:28,padding:22,minHeight:230,flexDirection:'row',alignItems:'center',gap:14,overflow:'hidden'},heroTitle:{fontSize:27,lineHeight:32,fontWeight:'800',color:C.white,marginTop:18,maxWidth:360},heroText:{fontSize:14,lineHeight:21,color:'#dcebe4',marginTop:8,maxWidth:400},heroButton:{backgroundColor:C.white,borderRadius:14,paddingVertical:13,paddingHorizontal:16,alignSelf:'flex-start',marginTop:18},heroButtonText:{color:C.green,fontWeight:'800'},ring:{width:96,height:96,borderWidth:8,borderColor:C.yellow,borderRadius:50,alignItems:'center',justifyContent:'center',backgroundColor:'#ffffff13'},ringBig:{color:C.white,fontWeight:'900',fontSize:28},ringSmall:{color:'#dcebe4',fontSize:11},pill:{borderRadius:99,paddingHorizontal:11,paddingVertical:6,alignSelf:'flex-start'},pillText:{fontSize:11,fontWeight:'800'},sectionHead:{flexDirection:'row',justifyContent:'space-between',alignItems:'center'},h2:{fontSize:19,fontWeight:'800',color:C.ink},caption:{fontSize:12,color:C.muted},grid:{flexDirection:'row',flexWrap:'wrap',gap:12},metric:{backgroundColor:C.white,borderRadius:20,padding:17,width:'48%',minWidth:150,borderWidth:1,borderColor:C.line},metricEmoji:{fontSize:24,marginBottom:12},metricLabel:{fontSize:12,fontWeight:'700',color:C.muted},metricValue:{fontSize:22,fontWeight:'900',color:C.ink,marginTop:3},metricSub:{fontSize:11,color:C.muted,marginTop:3},card:{backgroundColor:C.white,borderRadius:22,padding:19,borderWidth:1,borderColor:C.line},tip:{flexDirection:'row',gap:14,backgroundColor:'#fffaf0'},tipIcon:{width:46,height:46,borderRadius:14,backgroundColor:C.yellow,alignItems:'center',justifyContent:'center'},tipTag:{fontSize:10,fontWeight:'900',letterSpacing:1.1,color:C.green,marginBottom:6},tipTitle:{fontSize:17,fontWeight:'800',color:C.ink,marginBottom:5},body:{fontSize:13,lineHeight:19,color:C.muted},disclaimer:{fontSize:11,lineHeight:17,textAlign:'center',color:C.muted,paddingHorizontal:16},nav:{position:'absolute',bottom:0,left:0,right:0,height:82,backgroundColor:'#fffffffa',borderTopWidth:1,borderColor:C.line,flexDirection:'row',paddingTop:9},navItem:{flex:1,alignItems:'center',justifyContent:'center',gap:3},navIcon:{fontSize:24,color:'#829087'},navLabel:{fontSize:10,fontWeight:'700',color:'#829087'},active:{color:C.green},lead:{fontSize:15,lineHeight:22,color:C.muted,marginTop:-8},inputLabel:{fontSize:13,fontWeight:'800',color:C.ink,marginTop:17,marginBottom:7},optional:{fontWeight:'500',color:C.muted},inputRow:{height:56,borderWidth:1,borderColor:C.line,borderRadius:14,flexDirection:'row',alignItems:'center',paddingHorizontal:15,backgroundColor:C.cream},input:{flex:1,fontSize:20,fontWeight:'800',color:C.ink,outlineStyle:'none'} as any,unit:{fontWeight:'700',color:C.muted},helper:{fontSize:11,lineHeight:16,color:C.muted,marginTop:12},stepper:{flexDirection:'row',alignItems:'center',gap:12,paddingVertical:13,borderBottomWidth:1,borderBottomColor:C.line},stepValue:{fontSize:17,fontWeight:'800',color:C.ink,marginTop:2},stepBtn:{width:42,height:42,borderRadius:13,backgroundColor:C.mint,alignItems:'center',justifyContent:'center'},stepBtnText:{fontSize:24,color:C.green,fontWeight:'700'},primary:{backgroundColor:C.green,borderRadius:16,padding:17,alignItems:'center'},primaryText:{color:C.white,fontSize:15,fontWeight:'900'},track:{height:8,borderRadius:4,backgroundColor:C.line,overflow:'hidden'},fill:{height:8,borderRadius:4},bigNumber:{fontSize:28,fontWeight:'900',color:C.ink,marginTop:4},bigUnit:{fontSize:14,color:C.muted},chart:{height:160,flexDirection:'row',alignItems:'flex-end',justifyContent:'space-between',marginTop:20},barCol:{flex:1,alignItems:'center'},bar:{width:22,backgroundColor:C.green,borderRadius:8,marginVertical:6},barValue:{fontSize:9,color:C.muted},barLabel:{fontSize:10,color:C.muted},stat:{width:'48%',minWidth:150},statIcon:{fontSize:22},insight:{backgroundColor:C.mint},profile:{flexDirection:'row',alignItems:'center',gap:15},avatarLarge:{width:62,height:62,borderRadius:22,backgroundColor:C.yellow,alignItems:'center',justifyContent:'center'},avatarLargeText:{fontSize:23,fontWeight:'900',color:C.ink},setting:{flexDirection:'row',alignItems:'center',gap:13,padding:15},chevron:{fontSize:28,color:C.muted},safety:{backgroundColor:C.mint},danger:{padding:16,alignItems:'center'},dangerText:{color:'#a23d33',fontWeight:'800'}});
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+76
View File
@@ -0,0 +1,76 @@
# MetaBloom
Compagnon métabolique moderne, francophone et sans culpabilisation pour aider les personnes concernées par le surpoids, le prédiabète ou le diabète de type 2 à suivre leurs habitudes quotidiennes.
> **Statut : prototype éducatif et bien-être.** MetaBloom ne diagnostique aucune maladie, ne calcule aucun traitement et ne remplace pas un professionnel de santé. En cas durgence, contactez les services durgence.
## Le produit
Une seule application mobile-first pour iOS, Android et le web :
- tableau de bord quotidien avec score souple et petites actions ;
- journal rapide : poids, eau, pas et glycémie manuelle facultative ;
- tendances sur 7 jours et encouragements non culpabilisants ;
- préférences dunités et informations de sécurité ;
- persistance locale sur lappareil, sans compte ni serveur pour ce MVP ;
- interface responsive, accessible et utilisable au clavier.
## Pourquoi MetaBloom ?
Le marché combine trois familles : suivi nutritionnel (MyFitnessPal), coaching comportemental (Noom/WW) et journal du diabète (mySugr). MetaBloom se différencie comme **compagnon métabolique inclusif et francophone**, simple, calme, centré sur la prévention et la confidentialité. Il évite les objectifs extrêmes et la gamification punitive.
## Stack
- Expo SDK 57
- React Native 0.86 + React 19
- TypeScript
- AsyncStorage pour les données locales
- Expo Linear Gradient
## Démarrer
Prérequis : Node.js 20+ et npm.
```bash
npm install
npm run web
```
Autres cibles :
```bash
npm run android # appareil/émulateur Android ou Expo Go
npm run ios # macOS + simulateur iOS, ou Expo Go
npm run typecheck
npm run build:web
```
## Architecture
```text
App.tsx Interface, navigation et état du MVP
index.ts Point dentrée Expo
app.json Configuration multiplateforme
assets/ Icônes et splash screen
```
Le MVP reste volontairement monolithique pour accélérer la validation produit. Une V2 séparera écrans, composants, domaine et accès aux données.
## Données et sécurité
Les données de démonstration et saisies sont stockées exclusivement via AsyncStorage sur lappareil. Aucun backend, tracker publicitaire ou transfert de données nest inclus.
Avant une mise en production réelle : analyse dimpact RGPD, consentement explicite, chiffrement, gestion de leffacement/export, évaluation HDS, audit de sécurité, validation clinique et qualification au regard du règlement européen MDR. Les fonctions de diagnostic, prédiction dhypoglycémie, dosage dinsuline ou adaptation thérapeutique sont explicitement hors périmètre.
## Roadmap
1. Tests utilisateurs et accessibilité WCAG.
2. Authentification et synchronisation chiffrée auto-hébergeable.
3. Export PDF/CSV partageable avec consentement.
4. Rappels locaux et intégrations HealthKit / Health Connect.
5. Base alimentaire francophone et scan, après validation qualité.
6. Parcours professionnels de santé et conformité réglementaire.
## Licence
MIT — voir `LICENSE`.
+26
View File
@@ -0,0 +1,26 @@
{
"expo": {
"name": "MetaBloom",
"slug": "metabloom",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"description": "Des habitudes bienveillantes pour un meilleur équilibre métabolique.",
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"backgroundColor": "#E6F4FE",
"foregroundImage": "./assets/android-icon-foreground.png",
"backgroundImage": "./assets/android-icon-background.png",
"monochromeImage": "./assets/android-icon-monochrome.png"
},
"predictiveBackGestureEnabled": false
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+8
View File
@@ -0,0 +1,8 @@
import { registerRootComponent } from 'expo';
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);
+6246
View File
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
{
"name": "metabloom",
"version": "1.0.0",
"main": "index.ts",
"dependencies": {
"@react-native-async-storage/async-storage": "2.2.0",
"expo": "~57.0.7",
"expo-linear-gradient": "~57.0.1",
"expo-status-bar": "~57.0.1",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-native": "0.86.0",
"react-native-web": "^0.21.2"
},
"devDependencies": {
"@types/react": "~19.2.2",
"typescript": "~6.0.3"
},
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"typecheck": "tsc --noEmit",
"build:web": "expo export --platform web"
},
"private": true
}
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
}
}