Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# RadarFiel

RadarFiel é um aplicativo mobile multiplataforma (Android/iOS) construído com React Native, Expo e TypeScript para verificar se perfis estão ativos em aplicativos de namoro populares.

## Tecnologias principais

- React Native + Expo (TypeScript)
- Zustand para gerenciamento de estado com persistência em AsyncStorage
- NativeWind (Tailwind) para estilização
- Lottie para animações (onboarding, loading, feedback)
- Arquitetura modular seguindo princípios SOLID com factories e services dedicados
- Internacionalização (i18n) com suporte PT-BR e EN-US

## Estrutura do projeto

```
app/
App.tsx
app.json
package.json
src/
assets/lottie
components
factories
hooks
screens
services
stores
utils
backend/
docs/
```

## Pré-requisitos

- Node.js 18 ou superior (recomendado usar `nvm use 18`)
- npm 9+ ou pnpm/yarn compatíveis
- Conta Expo (opcional para builds EAS)

## Como executar localmente

1. Instale as dependências do projeto Expo:

```bash
cd app
npm install
```

2. Inicie o projeto Expo:

```bash
npm run start
```

3. Escaneie o QR Code com o aplicativo Expo Go (Android/iOS) ou use um emulador (`npm run android` / `npm run ios`).

Se encontrar problemas ao instalar dependências, verifique a versão do Node e limpe o cache do npm (`npm cache clean --force`) antes de tentar novamente.

## Builds com Expo EAS

1. Configure o EAS CLI conforme a [documentação oficial](https://docs.expo.dev/eas/).
2. Rode um build para Android ou iOS:

```bash
eas build --platform android
eas build --platform ios
```

## Mock de backend

O app utiliza `src/utils/mockBackend.ts` para simular uma API que retorna status de contas. Este mock pode ser substituído futuramente por integrações com APIs oficiais ou funções serverless. A especificação OpenAPI do mock está disponível em `docs/openapi.yaml`.

## Internacionalização

O idioma é detectado automaticamente via `expo-localization`, com fallback para PT-BR. Strings principais estão localizadas em `src/utils/i18n.ts`.

## Acessibilidade e responsividade

- Layout escuro (#0B0F1A) com contraste adequado
- Componentes possuem `accessibilityRole` quando necessário
- Botões e cards com área mínima de toque ≥ 48px

## Futuras melhorias

- Integração com serviços reais de verificação
- Autenticação e perfis de usuário
- Notificações push para alertas em tempo real
7 changes: 7 additions & 0 deletions app/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
root: true,
extends: ['universe/native', 'universe/shared/typescript-analysis'],
parserOptions: {
project: './tsconfig.json'
}
};
66 changes: 66 additions & 0 deletions app/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { StatusBar } from 'expo-status-bar';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { useFonts, Inter_400Regular, Inter_600SemiBold } from '@expo-google-fonts/inter';
import { ActivityIndicator, View } from 'react-native';
import { NativeWindStyleSheet } from 'nativewind';

import './src/utils/i18n';
import OnboardingScreen from './src/screens/OnboardingScreen';
import CheckAccountScreen from './src/screens/CheckAccountScreen';
import ResultScreen from './src/screens/ResultScreen';
import HistoryScreen from './src/screens/HistoryScreen';

NativeWindStyleSheet.setOutput({ default: 'native' });

export type RootStackParamList = {
Onboarding: undefined;
CheckAccount: undefined;
Result: {
status: 'Ativa' | 'Não encontrada';
username: string;
app: string;
};
History: undefined;
};

const Stack = createNativeStackNavigator<RootStackParamList>();

const App: React.FC = () => {
const [fontsLoaded] = useFonts({
Inter_400Regular,
Inter_600SemiBold
});

if (!fontsLoaded) {
return (
<View className="flex-1 items-center justify-center bg-background">
<ActivityIndicator size="large" color="#FF416C" />
</View>
);
}

return (
<SafeAreaProvider>
<NavigationContainer>
<StatusBar style="light" />
<Stack.Navigator
initialRouteName="Onboarding"
screenOptions={{
headerShown: false,
contentStyle: { backgroundColor: '#0B0F1A' }
}}
>
<Stack.Screen name="Onboarding" component={OnboardingScreen} />
<Stack.Screen name="CheckAccount" component={CheckAccountScreen} />
<Stack.Screen name="Result" component={ResultScreen} />
<Stack.Screen name="History" component={HistoryScreen} />
</Stack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
);
};

export default App;
31 changes: 31 additions & 0 deletions app/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"expo": {
"name": "RadarFiel",
"slug": "radarfiel",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "dark",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#0B0F1A"
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#0B0F1A"
}
},
"web": {
"bundler": "metro",
"output": "static"
}
}
}
3 changes: 3 additions & 0 deletions app/assets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Assets

Adicione aqui ícones (icon.png, splash.png, adaptive-icon.png) para builds do Expo.
7 changes: 7 additions & 0 deletions app/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['nativewind/babel', 'react-native-reanimated/plugin']
};
};
5 changes: 5 additions & 0 deletions app/metro.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname);

module.exports = config;
56 changes: 56 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "radarfiel",
"version": "1.0.0",
"main": "expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@expo-google-fonts/inter": "^0.2.3",
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "~1.20.1",
"@react-navigation/native": "^6.1.18",
"@react-navigation/native-stack": "^6.9.27",
"@shopify/flash-list": "^1.6.4",
"expo": "~50.0.6",
"expo-font": "~11.6.0",
"expo-linear-gradient": "~12.7.0",
"expo-localization": "~15.0.1",
"expo-splash-screen": "~0.20.5",
"expo-status-bar": "~1.11.1",
"i18next": "^23.10.1",
"lottie-react-native": "^6.7.0",
"nativewind": "^4.0.36",
"react": "18.2.0",
"react-i18next": "^13.5.0",
"react-native": "0.73.6",
"react-native-gesture-handler": "~2.13.4",
"react-native-reanimated": "~3.6.1",
"react-native-safe-area-context": "4.7.4",
"react-native-screens": "~3.29.0",
"zustand": "^4.5.2"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/react": "18.2.46",
"@types/react-native": "^0.73.0",
"@types/react-test-renderer": "18.0.0",
"@typescript-eslint/eslint-plugin": "^7.7.1",
"@typescript-eslint/parser": "^7.7.1",
"eslint": "^8.57.0",
"eslint-config-universe": "^12.0.0",
"tailwindcss": "^3.3.3",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.38",
"typescript": "^5.4.5"
},
"engines": {
"node": ">=18.0.0"
},
"private": true
}
105 changes: 105 additions & 0 deletions app/src/assets/lottie/failure.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"v": "5.7.4",
"fr": 60,
"ip": 0,
"op": 90,
"w": 200,
"h": 200,
"nm": "failure-cross",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "Cross",
"sr": 1,
"ks": {
"o": { "a": 0, "k": 100 },
"r": { "a": 0, "k": 0 },
"p": { "a": 0, "k": [100, 100, 0] },
"a": { "a": 0, "k": [0, 0, 0] },
"s": { "a": 0, "k": [100, 100, 100] }
},
"shapes": [
{
"ty": "gr",
"it": [
{
"ty": "sh",
"ks": {
"a": 1,
"k": [
{ "t": 0, "s": { "i": [], "o": [], "v": [[-40, -40], [0, 0], [40, -40]] } },
{ "t": 45, "s": { "i": [], "o": [], "v": [[-40, -40], [0, 0], [40, -40]] } }
]
}
},
{
"ty": "sh",
"ks": {
"a": 1,
"k": [
{ "t": 45, "s": { "i": [], "o": [], "v": [[-40, 40], [0, 0], [40, 40]] } },
{ "t": 90, "s": { "i": [], "o": [], "v": [[-40, 40], [0, 0], [40, 40]] } }
]
}
},
{
"ty": "st",
"c": { "a": 0, "k": [0.972, 0.282, 0.333, 1] },
"o": { "a": 0, "k": 100 },
"w": { "a": 0, "k": 18 },
"lc": 2,
"lj": 2
},
{ "ty": "tr", "p": { "a": 0, "k": [0, 0] }, "a": { "a": 0, "k": [0, 0] }, "s": { "a": 0, "k": [100, 100] }, "r": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } }
]
}
],
"ip": 0,
"op": 90,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 2,
"ty": 4,
"nm": "Glow",
"sr": 1,
"ks": {
"o": {
"a": 1,
"k": [
{ "t": 0, "s": 0 },
{ "t": 30, "s": 100 },
{ "t": 90, "s": 0 }
]
},
"r": { "a": 0, "k": 0 },
"p": { "a": 0, "k": [100, 100, 0] },
"a": { "a": 0, "k": [0, 0, 0] },
"s": { "a": 0, "k": [100, 100, 100] }
},
"shapes": [
{
"ty": "el",
"p": { "a": 0, "k": [0, 0] },
"s": { "a": 0, "k": [160, 160] }
},
{
"ty": "fl",
"c": { "a": 0, "k": [0.972, 0.282, 0.333, 0.35] },
"o": { "a": 0, "k": 100 }
},
{ "ty": "tr", "p": { "a": 0, "k": [0, 0] }, "a": { "a": 0, "k": [0, 0] }, "s": { "a": 0, "k": [100, 100] }, "r": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } }
],
"ip": 0,
"op": 90,
"st": 0,
"bm": 0
}
]
}
Loading