chore: remove legacy models/utils/api/database layers
Also moves stray lib/pages/quel-est-ce-pokemon.code-workspace to repo root, and fixes two trivial test lint infos (curly_braces_in_flow_ control_structures, constant_identifier_names) to reach clean analyze. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
dc7e681508
commit
ca77317a20
@ -1,109 +0,0 @@
|
||||
import '../models/pokemon.dart';
|
||||
import '../utils/pokemon_type.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/foundation.dart'; // Import for debugPrint
|
||||
|
||||
// Classe qui permet de récupérer les données des pokémons depuis l'API Tyradex
|
||||
// On utilise la librairie http pour effectuer les requêtes
|
||||
// On utilise la librairie dart:convert pour convertir les données JSON en objet Dart
|
||||
class PokemonApi {
|
||||
static const String baseUrl = 'tyradex.app';
|
||||
static const String pokemonUrl = 'api/v1/pokemon';
|
||||
|
||||
static Future<Pokemon> getPokemon(int id) async {
|
||||
print('API Call: Fetching Pokémon $id from Tyradex...');
|
||||
// On utilise la méthode get de la classe http pour effectuer une requête GET
|
||||
// On utilise Uri.https pour construire l'URL de la requête
|
||||
var response = await http.get(Uri.https(baseUrl, "$pokemonUrl/$id"));
|
||||
if (response.statusCode != 200) {
|
||||
// Si le code de retour de la requête n'est pas 200, on lève une exception
|
||||
throw Exception('Erreur lors de la récupération du pokémon $id, code de retour ${response.statusCode}');
|
||||
}
|
||||
// On utilise la méthode jsonDecode de la librairie dart:convert pour convertir le corps de la réponse en fichier JSON
|
||||
var json = jsonDecode(response.body);
|
||||
// Récupération du nom en français
|
||||
String name = json['name']['fr'] ?? json['name']['en'] ?? 'unknown';
|
||||
|
||||
// Récupération des types (en français dans l'API Tyradex)
|
||||
List types = json['types'] ?? [];
|
||||
PokemonType type1 = types.isNotEmpty
|
||||
? frenchTypeToEnum(types[0]['name'])
|
||||
: PokemonType.unknown;
|
||||
PokemonType? type2 = types.length > 1
|
||||
? frenchTypeToEnum(types[1]['name'])
|
||||
: null;
|
||||
|
||||
// Récupération des statistiques
|
||||
Map<String, dynamic>? stats = json['stats'];
|
||||
int hp = stats?['hp'] ?? 0;
|
||||
int atk = stats?['atk'] ?? 0;
|
||||
int def = stats?['def'] ?? 0;
|
||||
int spd = stats?['vit'] ?? 0; // 'vit' est la clé pour la vitesse dans tyradex.app
|
||||
|
||||
// Récupération de la description
|
||||
String? description = json['category'];
|
||||
|
||||
// On crée un objet Pokemon à partir du fichier JSON
|
||||
return Pokemon(
|
||||
name: name,
|
||||
id: id,
|
||||
type1: type1,
|
||||
type2: type2,
|
||||
hp: hp,
|
||||
atk: atk,
|
||||
def: def,
|
||||
spd: spd,
|
||||
description: description,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<List<Pokemon>> getAllPokemon() async {
|
||||
print('API Call: Fetching ALL Pokémon from Tyradex...');
|
||||
final response = await http.get(Uri.https(baseUrl, pokemonUrl));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
List<dynamic> jsonList = jsonDecode(response.body);
|
||||
List<Pokemon> allPokemon = [];
|
||||
|
||||
for (var json in jsonList) {
|
||||
// Skip default tyradex id 0 response which is generic typing
|
||||
if(json['pokedex_id'] == 0) continue;
|
||||
|
||||
try {
|
||||
String name = json['name']['fr'];
|
||||
int id = json['pokedex_id'];
|
||||
List<dynamic> types = json['types'] ?? [];
|
||||
PokemonType type1 = frenchTypeToEnum(types[0]['name']);
|
||||
PokemonType? type2 = types.length > 1 ? frenchTypeToEnum(types[1]['name']) : null;
|
||||
|
||||
Map<String, dynamic>? stats = json['stats'];
|
||||
int hp = stats?['hp'] ?? 0;
|
||||
int atk = stats?['atk'] ?? 0;
|
||||
int def = stats?['def'] ?? 0;
|
||||
int spd = stats?['vit'] ?? 0;
|
||||
|
||||
String? description = json['category'];
|
||||
|
||||
allPokemon.add(Pokemon(
|
||||
name: name,
|
||||
id: id,
|
||||
type1: type1,
|
||||
type2: type2,
|
||||
hp: hp,
|
||||
atk: atk,
|
||||
def: def,
|
||||
spd: spd,
|
||||
description: description,
|
||||
));
|
||||
} catch (e) {
|
||||
debugPrint("Failed parsing pokemon: ${json['name']} - $e");
|
||||
}
|
||||
}
|
||||
return allPokemon;
|
||||
} else {
|
||||
throw Exception('Failed to load pokemon');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,112 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sqflite_common/sqflite.dart';
|
||||
import '../models/pokemon.dart';
|
||||
|
||||
// Permet de gérer la base de données
|
||||
class PokedexDatabase {
|
||||
static Database? database;
|
||||
static final ValueNotifier<int> onDatabaseUpdate = ValueNotifier(0);
|
||||
|
||||
static Future<void> initDatabase() async {
|
||||
database = await openDatabase(
|
||||
"pokedex.db", // Nom de la base de données
|
||||
version: 2, // Version de la base de données, permet de gérer les migrations
|
||||
onUpgrade: (db, oldVersion, newVersion) async {
|
||||
if (oldVersion < 2) {
|
||||
await db.execute("DROP TABLE IF EXISTS pokemon");
|
||||
await db.execute("CREATE TABLE pokemon (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, type1 TEXT NOT NULL, type2 TEXT, hp INTEGER NOT NULL, atk INTEGER NOT NULL, def INTEGER NOT NULL, spd INTEGER NOT NULL, description TEXT, isCaught INTEGER NOT NULL DEFAULT 0, isSeen INTEGER NOT NULL DEFAULT 0)");
|
||||
}
|
||||
},
|
||||
onCreate: (db, version) async { // Fonction qui sera appelée lors de la création de la base de données
|
||||
// Création de la table pokemon avec les colonnes...
|
||||
await db.execute("CREATE TABLE IF NOT EXISTS pokemon (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, type1 TEXT NOT NULL, type2 TEXT, hp INTEGER NOT NULL, atk INTEGER NOT NULL, def INTEGER NOT NULL, spd INTEGER NOT NULL, description TEXT, isCaught INTEGER NOT NULL DEFAULT 0, isSeen INTEGER NOT NULL DEFAULT 0)");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode qui permet de récupérer la base de données
|
||||
static Future<Database> getDatabase() async {
|
||||
if (database == null) {
|
||||
await initDatabase(); // On initialise la base de données si elle n'est pas encore initialisée
|
||||
}
|
||||
return database!;
|
||||
}
|
||||
|
||||
// Méthode qui permet d'insérer un Pokémon dans la base de données
|
||||
static Future<void> insertPokemon(Pokemon pokemon) async {
|
||||
Database database = await getDatabase();
|
||||
await database.insert(
|
||||
'pokemon',
|
||||
pokemon.toJson(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
onDatabaseUpdate.value++;
|
||||
}
|
||||
|
||||
// Méthode qui permet d'insérer plusieurs Pokémon d'un coup (plus performant)
|
||||
static Future<void> batchInsertPokemon(List<Pokemon> pokemonList) async {
|
||||
Database db = await getDatabase();
|
||||
Batch batch = db.batch();
|
||||
for (var pokemon in pokemonList) {
|
||||
batch.insert(
|
||||
'pokemon',
|
||||
pokemon.toJson(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
await batch.commit(noResult: true);
|
||||
onDatabaseUpdate.value++;
|
||||
}
|
||||
|
||||
// Méthode qui permet de récupérer la liste des pokémons dans la base de données
|
||||
static Future<List<Pokemon>> getPokemonList() async {
|
||||
Database database = await getDatabase();
|
||||
var response = await database.query("pokemon");
|
||||
return response.map((pokemon) => Pokemon.fromJson(pokemon)).toList();
|
||||
}
|
||||
|
||||
// Méthode qui permet de supprimer un Pokémon de la base de données
|
||||
static Future<void> deletePokemon(int id) async {
|
||||
Database database = await getDatabase();
|
||||
await database.delete("pokemon", where: "id = ?", whereArgs: [id]);
|
||||
}
|
||||
|
||||
// Méthode qui permet de supprimer tous les pokémons de la base de données
|
||||
static Future<void> deleteAllPokemon() async {
|
||||
Database database = await getDatabase();
|
||||
await database.delete("pokemon");
|
||||
}
|
||||
|
||||
// Méthode qui permet de mettre à jour un Pokémon dans la base de données
|
||||
static Future<void> updatePokemon(Pokemon pokemon) async {
|
||||
Database database = await getDatabase();
|
||||
await database.update("pokemon", pokemon.toJson(), where: "id = ?", whereArgs: [pokemon.id]);
|
||||
onDatabaseUpdate.value++;
|
||||
}
|
||||
|
||||
// Méthode qui permet de récupérer un Pokémon dans la base de données à partir de son ID
|
||||
static Future<Pokemon?> getPokemon(int id) async {
|
||||
Database database = await getDatabase();
|
||||
List<Map<String, dynamic>> pokemonList = await database.query("pokemon", where: "id = ?", whereArgs: [id]);
|
||||
if (pokemonList.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return Pokemon.fromJson(pokemonList.first);
|
||||
}
|
||||
|
||||
// Obtenir le nombre de pokémon attrapés
|
||||
static Future<int> getCaughtCount() async {
|
||||
Database database = await getDatabase();
|
||||
var result = await database.rawQuery("SELECT COUNT(*) FROM pokemon WHERE isCaught = 1");
|
||||
int count = result.isNotEmpty ? (result.first.values.first as int? ?? 0) : 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Obtenir le nombre de pokémon vus
|
||||
static Future<int> getSeenCount() async {
|
||||
Database database = await getDatabase();
|
||||
var result = await database.rawQuery("SELECT COUNT(*) FROM pokemon WHERE isSeen = 1");
|
||||
int count = result.isNotEmpty ? (result.first.values.first as int? ?? 0) : 0;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@ -1,113 +0,0 @@
|
||||
import 'package:flutter/foundation.dart' show kIsWeb; // Platform is not supported on web
|
||||
import '../database/pokedex_database.dart';
|
||||
import '../api/pokemon_api.dart';
|
||||
import '../utils/pokemon_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
// Classe représentant un Pokémon. Elle contient le nom, le numéro et les types du Pokémon.
|
||||
// Elle contient aussi des propriétés calculées pour récupérer l'url de l'image du Pokémon, l'url de l'image shiny du Pokémon et l'url du cri du Pokémon.
|
||||
class Pokemon {
|
||||
String name;
|
||||
int id;
|
||||
PokemonType type1;
|
||||
PokemonType? type2;
|
||||
int hp;
|
||||
int atk;
|
||||
int def;
|
||||
int spd;
|
||||
String? description;
|
||||
bool isCaught;
|
||||
bool isSeen;
|
||||
|
||||
String get imageUrl => 'https://raw.githubusercontent.com/Yarkis01/TyraDex/images/sprites/$id/regular.png';
|
||||
String get shinyImageUrl => 'https://raw.githubusercontent.com/Yarkis01/TyraDex/images/sprites/$id/shiny.png';
|
||||
String get cryUrl => 'https://pokemoncries.com/cries/$id.mp3';
|
||||
|
||||
String get formatedName {
|
||||
return name[0].toUpperCase() + name.substring(1);
|
||||
}
|
||||
Color get type1Color => typeToColor(type1);
|
||||
Color get type2Color => typeToColor(type2 ?? PokemonType.unknown);
|
||||
String get type1Formated => formatedTypeName(type1);
|
||||
String get type2Formated => formatedTypeName(type2 ?? PokemonType.unknown);
|
||||
|
||||
Pokemon({
|
||||
required this.name,
|
||||
required this.id,
|
||||
required this.type1,
|
||||
this.type2, // Le type 2 n'est pas toujours présent
|
||||
required this.hp,
|
||||
required this.atk,
|
||||
required this.def,
|
||||
required this.spd,
|
||||
this.description,
|
||||
this.isCaught = false,
|
||||
this.isSeen = false,
|
||||
});
|
||||
|
||||
// Constructeur qui permet de créer un Pokémon à partir d'un fichier JSON récupéré depuis l'API.
|
||||
// Sera aussi utilisé pour la récupération depuis la base de données
|
||||
factory Pokemon.fromJson(Map<String, dynamic> json) {
|
||||
return Pokemon(
|
||||
name: json['name'],
|
||||
id: json['id'],
|
||||
// Parcours des valeurs de l'enum PokemonType et récupération de la première valeur qui correspond à la string 'PokemonType.${json['type1']}'
|
||||
type1: PokemonType.values.firstWhere((element) => element.toString() == 'PokemonType.${json['type1']}'),
|
||||
type2: json['type2'] != null ? PokemonType.values.firstWhere((element) => element.toString() == 'PokemonType.${json['type2']}') : null,
|
||||
hp: json['hp'] ?? 0,
|
||||
atk: json['atk'] ?? 0,
|
||||
def: json['def'] ?? 0,
|
||||
spd: json['spd'] ?? 0,
|
||||
description: json['description'],
|
||||
isCaught: json['isCaught'] == 1 || json['isCaught'] == true,
|
||||
isSeen: json['isSeen'] == 1 || json['isSeen'] == true,
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode qui permet de convertir un Pokémon en fichier JSON. Sera aussi utilisé pour l'insertion dans la base de données
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'name': name,
|
||||
'id': id,
|
||||
'type1': type1.toString().split('.').last, // On récupère la valeur de l'enum PokemonType sans le préfixe 'PokemonType.'
|
||||
'type2': type2?.toString().split('.').last,
|
||||
'hp': hp,
|
||||
'atk': atk,
|
||||
'def': def,
|
||||
'spd': spd,
|
||||
'description': description,
|
||||
'isCaught': isCaught ? 1 : 0,
|
||||
'isSeen': isSeen ? 1 : 0,
|
||||
};
|
||||
}
|
||||
|
||||
// Méthode qui permet de récupérer un Pokémon à partir de son ID
|
||||
// Si le Pokémon n'est pas présent dans la base de données, on le récupère depuis l'API
|
||||
static Future<Pokemon?> fromID(int id) async {
|
||||
Pokemon? pokemon;
|
||||
if (!kIsWeb) {
|
||||
// La base de données n'est pas disponible sur le web
|
||||
pokemon = await PokedexDatabase.getPokemon(id);
|
||||
}
|
||||
if (pokemon == null) {
|
||||
try {
|
||||
pokemon = await PokemonApi.getPokemon(id);
|
||||
if (!kIsWeb) {
|
||||
// On insère le Pokémon dans la base de données
|
||||
await PokedexDatabase.insertPokemon(pokemon);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return pokemon;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Enum qui représente les différents types de Pokémon
|
||||
enum PokemonType {
|
||||
normal, fighting, flying, poison, ground, rock, bug, ghost, steel, fire, water, grass, electric, psychic, ice, dragon, dark, fairy, unknown, shadow
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../models/pokemon.dart';
|
||||
|
||||
// Convertit un nom de type français (de l'API Tyradex) en PokemonType
|
||||
PokemonType frenchTypeToEnum(String frenchType) {
|
||||
const Map<String, PokemonType> frenchToEnglish = {
|
||||
'Normal': PokemonType.normal,
|
||||
'Combat': PokemonType.fighting,
|
||||
'Vol': PokemonType.flying,
|
||||
'Poison': PokemonType.poison,
|
||||
'Sol': PokemonType.ground,
|
||||
'Roche': PokemonType.rock,
|
||||
'Insecte': PokemonType.bug,
|
||||
'Spectre': PokemonType.ghost,
|
||||
'Acier': PokemonType.steel,
|
||||
'Feu': PokemonType.fire,
|
||||
'Eau': PokemonType.water,
|
||||
'Plante': PokemonType.grass,
|
||||
'Électrik': PokemonType.electric,
|
||||
'Psy': PokemonType.psychic,
|
||||
'Glace': PokemonType.ice,
|
||||
'Dragon': PokemonType.dragon,
|
||||
'Ténèbres': PokemonType.dark,
|
||||
'Fée': PokemonType.fairy,
|
||||
};
|
||||
return frenchToEnglish[frenchType] ?? PokemonType.unknown;
|
||||
}
|
||||
|
||||
// Permet de mapper un type de Pokémon avec une couleur
|
||||
Color typeToColor(PokemonType type) {
|
||||
Map<PokemonType, Color> typeToColor = {
|
||||
PokemonType.normal: Colors.white,
|
||||
PokemonType.fire: Colors.red,
|
||||
PokemonType.water: Colors.blue,
|
||||
PokemonType.electric: Colors.yellow,
|
||||
PokemonType.grass: Colors.green,
|
||||
PokemonType.ice: Colors.cyan,
|
||||
PokemonType.fighting: Colors.orange,
|
||||
PokemonType.poison: Colors.purple,
|
||||
PokemonType.ground: Colors.brown,
|
||||
PokemonType.flying: Colors.indigo,
|
||||
PokemonType.psychic: Colors.pink,
|
||||
PokemonType.bug: Colors.lightGreen,
|
||||
PokemonType.rock: Colors.grey,
|
||||
PokemonType.ghost: Colors.indigo,
|
||||
PokemonType.dragon: Colors.indigo,
|
||||
PokemonType.dark: Colors.black45,
|
||||
PokemonType.steel: Colors.grey.shade600,
|
||||
PokemonType.fairy: Colors.pinkAccent,
|
||||
PokemonType.unknown: Colors.transparent,
|
||||
PokemonType.shadow: Colors.transparent,
|
||||
};
|
||||
return typeToColor[type] ?? Colors.transparent;
|
||||
}
|
||||
|
||||
// Met le nom du type de Pokémon avec une majuscule
|
||||
String formatedTypeName(PokemonType type) {
|
||||
String typeName = type.toString().split('.').last.replaceAll('PokemonType.', '');
|
||||
return typeName[0].toUpperCase() + typeName.substring(1);
|
||||
}
|
||||
@ -21,7 +21,7 @@ class FakeLocal implements PokemonLocalDataSource {
|
||||
Future<Pokemon?> getById(int id) async => store[id];
|
||||
@override
|
||||
Future<void> saveAll(List<Pokemon> pokemons) async {
|
||||
for (final p in pokemons) store[p.id] = p;
|
||||
for (final p in pokemons) { store[p.id] = p; }
|
||||
}
|
||||
@override
|
||||
Future<void> update(Pokemon pokemon) async => store[pokemon.id] = pokemon;
|
||||
|
||||
@ -81,7 +81,7 @@ void main() {
|
||||
s = _engine.startRound(s, _poke('pikachu'), isShiny: false);
|
||||
final o = _engine.submitGuess(s, 'pikachu'); // 5e bonne réponse
|
||||
expect(o.state.sessionCorrectCount, 5);
|
||||
expect(o.state.hints, AppConstantsHints + 1);
|
||||
expect(o.state.hints, appConstantsHints + 1);
|
||||
});
|
||||
|
||||
test('useHint consomme un indice', () {
|
||||
@ -121,4 +121,4 @@ void main() {
|
||||
}
|
||||
|
||||
/// Valeur attendue de hints au démarrage (miroir d'AppConstants.startingHints = 3).
|
||||
const AppConstantsHints = 3;
|
||||
const appConstantsHints = 3;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user