diff --git a/lib/data/dtos/films_dto.dart b/lib/data/dtos/films_dto.dart new file mode 100644 index 0000000..6a84462 --- /dev/null +++ b/lib/data/dtos/films_dto.dart @@ -0,0 +1,42 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'films_dto.g.dart'; + +@JsonSerializable(createToJson: false) +class FilmsDto { + final int total; + final List items; + + const FilmsDto({ + required this.total, + required this.items, + }); + + factory FilmsDto.fromJson(Map json) => _$FilmsDtoFromJson(json); +} + +@JsonSerializable(createToJson: false) +class FilmDataDto { + final String nameRu; + final int year; + final List genres; + final String posterUrl; + + const FilmDataDto({ + required this.nameRu, + required this.year, + required this.genres, + required this.posterUrl, + }); + + factory FilmDataDto.fromJson(Map json) => _$FilmDataDtoFromJson(json); +} + +@JsonSerializable(createToJson: false) +class GenreDto { + final String genre; + + const GenreDto({required this.genre}); + + factory GenreDto.fromJson(Map json) => _$GenreDtoFromJson(json); +} \ No newline at end of file diff --git a/lib/data/mappers/films_mapper.dart b/lib/data/mappers/films_mapper.dart new file mode 100644 index 0000000..c3013cc --- /dev/null +++ b/lib/data/mappers/films_mapper.dart @@ -0,0 +1,31 @@ +import 'package:project1/data/dtos/films_dto.dart'; +import 'package:project1/domain/models/card.dart'; +import 'package:project1/domain/models/home.dart'; + +const _imagePlaceholder = + 'https://upload.wikimedia.org/wikipedia/en/archive/b/b1/20210811082420%21Portrait_placeholder.png'; + +extension FilmsDtoToModel on FilmsDto { + HomeData toDomain() => HomeData( + data: items.map((e) => e.toDomain()).toList(), + nextPage: null, // Если нужно добавить поддержку пагинации, нужно будет добавить соответствующие поля в HomeData + ); +} + +extension FilmDataDtoToModel on FilmDataDto { + CardData toDomain() => CardData( + nameRu, + imageUrl: posterUrl ?? _imagePlaceholder, + descriptionText: _makeDescriptionText(year, genres), + ); + + String _makeDescriptionText(int? year, List genres) { + final yearText = year != null ? 'Год выхода: $year\n' : ''; + final genresText = genres.isNotEmpty ? 'Жанр: ${genres.map((g) => g.genre).join(', ')}' : ''; + return [yearText, genresText].where((s) => s.isNotEmpty).join(); + } +} + +extension GenreDtoToModel on GenreDto { + String toDomain() => genre; +} \ No newline at end of file diff --git a/lib/data/repositories/films_repository.dart b/lib/data/repositories/films_repository.dart new file mode 100644 index 0000000..67f0436 --- /dev/null +++ b/lib/data/repositories/films_repository.dart @@ -0,0 +1,94 @@ +import 'package:dio/dio.dart'; +import 'package:pretty_dio_logger/pretty_dio_logger.dart'; +import 'package:project1/data/dtos/films_dto.dart'; +import 'package:project1/data/mappers/films_mapper.dart'; +import 'package:project1/domain/models/home.dart'; + +typedef OnErrorCallback = void Function(String? error); + +class FilmsRepository { + static final Dio _dio = Dio() + ..interceptors.add(PrettyDioLogger( + requestHeader: true, + requestBody: true, + )); + + static const String _baseUrl = 'https://kinopoiskapiunofficial.tech'; + static const String _apiKey = '67c830e4-b979-48ba-903d-a00c8f96fd4b'; + + Future loadData({ + OnErrorCallback? onError, + String? q, + int page = 1, + int pageSize = 7, + }) async { + try { + String url = '$_baseUrl/api/v2.2/films/premieres'; + + if (q != null && q.isNotEmpty) { + url = '$_baseUrl/api/v2.1/films/search-by-keyword'; + } + + final List months = [ + 'JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE', + 'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER', 'NOVEMBER', 'DECEMBER' + ]; + + final List allFilms = []; + + for (final month in months) { + final Response response = await _dio.get>( + url, + queryParameters: { + 'year': DateTime.now().year, + 'month': month, + 'keyword': q, + 'page': page, + 'pageSize': pageSize, + }, + options: Options( + headers: { + 'X-API-KEY': _apiKey, + 'Content-Type': 'application/json', + }, + ), + ); + + final FilmsDto dto = FilmsDto.fromJson(response.data as Map); + allFilms.addAll(dto.items); + } + + final HomeData data = HomeData( + data: allFilms.map((e) => e.toDomain()).toList(), + nextPage: page + 1, // Увеличиваем номер страницы для следующего запроса + ); + + return data; + } on DioException catch (e) { + onError?.call(e.error?.toString()); + return null; + } + } + + Future getFilmById(int filmId) async { + try { + final String url = '$_baseUrl/api/v2.2/films/$filmId'; + + final Response response = await _dio.get>( + url, + options: Options( + headers: { + 'X-API-KEY': _apiKey, + 'Content-Type': 'application/json', + }, + ), + ); + + final FilmDataDto dto = FilmDataDto.fromJson(response.data as Map); + return dto; + } on DioException catch (e) { + print('Request failed with error: ${e.error}'); + return null; + } + } +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index a59a8a9..358a7ce 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:project1/presentation/like_bloc/like_bloc.dart'; import 'package:project1/presentation/locale_bloc/locale_bloc.dart'; import 'package:project1/presentation/locale_bloc/locale_state.dart'; import 'components/locale/l10n/app_locale.dart'; +import 'data/repositories/films_repository.dart'; import 'data/repositories/potter_repository.dart'; void main() { @@ -34,15 +35,15 @@ class MyApp extends StatelessWidget { colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), - home: RepositoryProvider( + home: RepositoryProvider( lazy: true, - create: (_) => PotterRepository(), + create: (_) => FilmsRepository(), child: BlocProvider( lazy: false, create: (context) => LikeBloc(), child: BlocProvider( lazy: false, - create: (context) => HomeBloc(context.read()), + create: (context) => HomeBloc(context.read()), child: const MyHomePage(title: 'Булатова Карина Раилевна'), ), ), diff --git a/lib/presentation/home_page/bloc/bloc.dart b/lib/presentation/home_page/bloc/bloc.dart index 15c63bc..4b9594c 100644 --- a/lib/presentation/home_page/bloc/bloc.dart +++ b/lib/presentation/home_page/bloc/bloc.dart @@ -3,8 +3,10 @@ import 'package:project1/data/repositories/potter_repository.dart'; import 'package:project1/presentation/home_page/bloc/events.dart'; import 'package:project1/presentation/home_page/bloc/state.dart'; +import '../../../data/repositories/films_repository.dart'; + class HomeBloc extends Bloc { - final PotterRepository repo; + final FilmsRepository repo; HomeBloc(this.repo) : super(const HomeState()) { on(_onLoadData);