diff --git a/analysis_options.yaml b/analysis_options.yaml index 9722fb45..22f04010 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,6 @@ analyzer: + errors: + invalid_annotation_target: ignore plugins: - dart_code_metrics diff --git a/android/app/build.gradle b/android/app/build.gradle index 4c483ee5..7652369e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,67 +1,59 @@ -plugins{ - id 'com.android.application' - id 'kotlin-android' +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" } + def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') +def localPropertiesFile = rootProject.file("local.properties") if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> + localPropertiesFile.withReader("UTF-8") { reader -> localProperties.load(reader) } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") if (flutterVersionCode == null) { - flutterVersionCode = '1' + flutterVersionCode = "1" } -def flutterVersionName = localProperties.getProperty('flutter.versionName') +def flutterVersionName = localProperties.getProperty("flutter.versionName") if (flutterVersionName == null) { - flutterVersionName = '1.0' + flutterVersionName = "1.0" } -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { - compileSdkVersion flutter.compileSdkVersion + namespace = "com.eatvioapp.app" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - lintOptions { - disable 'InvalidPackage' + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.eatvioapp.app" - minSdkVersion 23 - targetSdkVersion flutter.targetSdkVersion - multiDexEnabled true - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + applicationId = "com.eatvioapp.app" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdk = 23 + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig = signingConfigs.debug } } } flutter { - source '../..' + source = "../.." } -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "androidx.multidex:multidex:2.0.1" -} diff --git a/android/build.gradle b/android/build.gradle index d7f8aafa..5869be3d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.7.20' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 6b665338..aeaff6f8 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 44e62bcf..536165d3 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/assets/fonts/MulishBold.ttf b/assets/fonts/MulishBold.ttf index 0c1eed73..e69de29b 100644 Binary files a/assets/fonts/MulishBold.ttf and b/assets/fonts/MulishBold.ttf differ diff --git a/assets/fonts/MulishRegular.ttf b/assets/fonts/MulishRegular.ttf index 0971518b..e69de29b 100644 Binary files a/assets/fonts/MulishRegular.ttf and b/assets/fonts/MulishRegular.ttf differ diff --git a/assets/fonts/MulishSemiBold.ttf b/assets/fonts/MulishSemiBold.ttf index 9ac1fc8f..e69de29b 100644 Binary files a/assets/fonts/MulishSemiBold.ttf and b/assets/fonts/MulishSemiBold.ttf differ diff --git a/assets/fonts/SegoeUISemibold.ttf b/assets/fonts/SegoeUISemibold.ttf index 46b3b993..e69de29b 100644 Binary files a/assets/fonts/SegoeUISemibold.ttf and b/assets/fonts/SegoeUISemibold.ttf differ diff --git a/devtools_options.yaml b/devtools_options.yaml index 7e7e7f67..5c27c3e3 100644 --- a/devtools_options.yaml +++ b/devtools_options.yaml @@ -1 +1,2 @@ extensions: + - provider: true \ No newline at end of file diff --git a/lib/app_router.dart b/lib/app_router.dart index c6de2d5a..3c60e6e6 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -1,6 +1,7 @@ +import 'package:eatvio_app/core/token_storage.dart'; +import 'package:eatvio_app/features/chat/presentation/pages/chat_screen.dart'; +import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/chat_select_screen.dart'; import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart'; -import 'package:eatvio_app/presentation/chat/chat_screen/chat_screen.dart'; -import 'package:eatvio_app/presentation/chat/chat_select_screen/chat_select_screen.dart'; import 'package:eatvio_app/presentation/diary/diary_edit_screen/diary_edit_screen.dart'; import 'package:eatvio_app/presentation/diets/diet_create_screen/diet_create_screen.dart'; import 'package:eatvio_app/presentation/diets/diet_select_screen/diet_select_screen.dart'; @@ -29,9 +30,9 @@ import 'package:eatvio_app/presentation/registration/medicine_intake_screen/medi import 'package:eatvio_app/presentation/registration/name_screen/name_screen.dart'; import 'package:eatvio_app/presentation/registration/weight_screen/weight_screen.dart'; import 'package:eatvio_app/widgets/bottom_navigation_bar_widget.dart'; +import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:hive_flutter/hive_flutter.dart'; class AppRouter { static GlobalKey navigatorKey = GlobalKey(); @@ -44,11 +45,7 @@ class AppRouter { path: '/auth-screen', name: 'authScreen', redirect: (context, state) async { - final box = await Hive.openBox('token'); - final token = box.get('token'); - if (token != null) { - return '/my-profile-screen'; - } + if (await TokenStorage.haveTokenInStorage()) return '/my-profile-screen'; return null; }, builder: (context, state) => AuthScreen.builder(context), @@ -209,7 +206,7 @@ class AppRouter { GoRoute( path: 'chat-screen', name: 'chatScreen', - builder: (context, state) => ChatScreen.builder(context, state.extra), + builder: (context, state) => ChatScreen(chatId: state.extra as String), ), ], ), @@ -245,6 +242,14 @@ class AppRouter { name: 'glossaryScreen', builder: (context, state) => GlossaryScreen.builder(context), ), + GoRoute( + path: '/photo-view-screen/:index', + name: 'photoViewScreen', + builder: (context, state) => PhotoViewScreen( + imageUrls: state.extra as List, + index: int.parse(state.pathParameters['index']!), + ), + ), ], ); } diff --git a/lib/core/token_storage.dart b/lib/core/token_storage.dart new file mode 100644 index 00000000..82d2935e --- /dev/null +++ b/lib/core/token_storage.dart @@ -0,0 +1,32 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/locator_service.dart'; +import 'package:hive_flutter/hive_flutter.dart'; + +abstract class TokenStorage { + static addTokenToDioHeader(String token) { + sl().options.headers['Authorization'] = 'Bearer $token'; + } + + static haveTokenInStorage() async { + final box = await Hive.openBox('token'); + final token = box.get('token'); + if (token == null) { + return false; + } else { + addTokenToDioHeader(token); + return true; + } + } + + static getTokenFromStorage() async => await Hive.openBox('token') + ..get('token'); + + static saveTokenInStorage(String token) async { + await Hive.openBox('token') + ..put('token', token); + addTokenToDioHeader(token); + } + + static removeTokenInStorage(String token) async => await Hive.openBox('token') + ..delete('token'); +} diff --git a/lib/data/models/tokenModel/token_model.dart b/lib/data/models/tokenModel/token_model.dart index 17ab28a1..3aa1d3f6 100644 --- a/lib/data/models/tokenModel/token_model.dart +++ b/lib/data/models/tokenModel/token_model.dart @@ -1,5 +1,5 @@ import 'package:hive/hive.dart'; - +/// https://pub.dev/packages/flutter_secure_storage TODO replace hive to secure_storage for token storage @HiveType(typeId: 0) class TokenModel { @HiveField(0) diff --git a/lib/features/chat/models/chat_message_model.dart b/lib/features/chat/models/chat_message_model.dart new file mode 100644 index 00000000..220df3bf --- /dev/null +++ b/lib/features/chat/models/chat_message_model.dart @@ -0,0 +1,18 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'chat_message_model.freezed.dart'; + +part 'chat_message_model.g.dart'; + +@freezed +class ChatMessageModel with _$ChatMessageModel { + const factory ChatMessageModel({ + required int id, + required String data, + @JsonKey(name: 'media_data') required List mediaData, + @JsonKey(name: 'author_id') required int authorId, + @JsonKey(name: 'updated_at') required String updatedAt, + }) = _ChatMessageModel; + + factory ChatMessageModel.fromJson(Map json) => _$ChatMessageModelFromJson(json); +} diff --git a/lib/features/chat/models/chat_message_model.freezed.dart b/lib/features/chat/models/chat_message_model.freezed.dart new file mode 100644 index 00000000..61856085 --- /dev/null +++ b/lib/features/chat/models/chat_message_model.freezed.dart @@ -0,0 +1,257 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'chat_message_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ChatMessageModel _$ChatMessageModelFromJson(Map json) { + return _ChatMessageModel.fromJson(json); +} + +/// @nodoc +mixin _$ChatMessageModel { + int get id => throw _privateConstructorUsedError; + String get data => throw _privateConstructorUsedError; + @JsonKey(name: 'media_data') + List get mediaData => throw _privateConstructorUsedError; + @JsonKey(name: 'author_id') + int get authorId => throw _privateConstructorUsedError; + @JsonKey(name: 'updated_at') + String get updatedAt => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $ChatMessageModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ChatMessageModelCopyWith<$Res> { + factory $ChatMessageModelCopyWith( + ChatMessageModel value, $Res Function(ChatMessageModel) then) = + _$ChatMessageModelCopyWithImpl<$Res, ChatMessageModel>; + @useResult + $Res call( + {int id, + String data, + @JsonKey(name: 'media_data') List mediaData, + @JsonKey(name: 'author_id') int authorId, + @JsonKey(name: 'updated_at') String updatedAt}); +} + +/// @nodoc +class _$ChatMessageModelCopyWithImpl<$Res, $Val extends ChatMessageModel> + implements $ChatMessageModelCopyWith<$Res> { + _$ChatMessageModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? data = null, + Object? mediaData = null, + Object? authorId = null, + Object? updatedAt = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + data: null == data + ? _value.data + : data // ignore: cast_nullable_to_non_nullable + as String, + mediaData: null == mediaData + ? _value.mediaData + : mediaData // ignore: cast_nullable_to_non_nullable + as List, + authorId: null == authorId + ? _value.authorId + : authorId // ignore: cast_nullable_to_non_nullable + as int, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ChatMessageModelImplCopyWith<$Res> + implements $ChatMessageModelCopyWith<$Res> { + factory _$$ChatMessageModelImplCopyWith(_$ChatMessageModelImpl value, + $Res Function(_$ChatMessageModelImpl) then) = + __$$ChatMessageModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + String data, + @JsonKey(name: 'media_data') List mediaData, + @JsonKey(name: 'author_id') int authorId, + @JsonKey(name: 'updated_at') String updatedAt}); +} + +/// @nodoc +class __$$ChatMessageModelImplCopyWithImpl<$Res> + extends _$ChatMessageModelCopyWithImpl<$Res, _$ChatMessageModelImpl> + implements _$$ChatMessageModelImplCopyWith<$Res> { + __$$ChatMessageModelImplCopyWithImpl(_$ChatMessageModelImpl _value, + $Res Function(_$ChatMessageModelImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? data = null, + Object? mediaData = null, + Object? authorId = null, + Object? updatedAt = null, + }) { + return _then(_$ChatMessageModelImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + data: null == data + ? _value.data + : data // ignore: cast_nullable_to_non_nullable + as String, + mediaData: null == mediaData + ? _value._mediaData + : mediaData // ignore: cast_nullable_to_non_nullable + as List, + authorId: null == authorId + ? _value.authorId + : authorId // ignore: cast_nullable_to_non_nullable + as int, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ChatMessageModelImpl implements _ChatMessageModel { + const _$ChatMessageModelImpl( + {required this.id, + required this.data, + @JsonKey(name: 'media_data') required final List mediaData, + @JsonKey(name: 'author_id') required this.authorId, + @JsonKey(name: 'updated_at') required this.updatedAt}) + : _mediaData = mediaData; + + factory _$ChatMessageModelImpl.fromJson(Map json) => + _$$ChatMessageModelImplFromJson(json); + + @override + final int id; + @override + final String data; + final List _mediaData; + @override + @JsonKey(name: 'media_data') + List get mediaData { + if (_mediaData is EqualUnmodifiableListView) return _mediaData; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_mediaData); + } + + @override + @JsonKey(name: 'author_id') + final int authorId; + @override + @JsonKey(name: 'updated_at') + final String updatedAt; + + @override + String toString() { + return 'ChatMessageModel(id: $id, data: $data, mediaData: $mediaData, authorId: $authorId, updatedAt: $updatedAt)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ChatMessageModelImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.data, data) || other.data == data) && + const DeepCollectionEquality() + .equals(other._mediaData, _mediaData) && + (identical(other.authorId, authorId) || + other.authorId == authorId) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, id, data, + const DeepCollectionEquality().hash(_mediaData), authorId, updatedAt); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ChatMessageModelImplCopyWith<_$ChatMessageModelImpl> get copyWith => + __$$ChatMessageModelImplCopyWithImpl<_$ChatMessageModelImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ChatMessageModelImplToJson( + this, + ); + } +} + +abstract class _ChatMessageModel implements ChatMessageModel { + const factory _ChatMessageModel( + {required final int id, + required final String data, + @JsonKey(name: 'media_data') required final List mediaData, + @JsonKey(name: 'author_id') required final int authorId, + @JsonKey(name: 'updated_at') required final String updatedAt}) = + _$ChatMessageModelImpl; + + factory _ChatMessageModel.fromJson(Map json) = + _$ChatMessageModelImpl.fromJson; + + @override + int get id; + @override + String get data; + @override + @JsonKey(name: 'media_data') + List get mediaData; + @override + @JsonKey(name: 'author_id') + int get authorId; + @override + @JsonKey(name: 'updated_at') + String get updatedAt; + @override + @JsonKey(ignore: true) + _$$ChatMessageModelImplCopyWith<_$ChatMessageModelImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/chat/models/chat_message_model.g.dart b/lib/features/chat/models/chat_message_model.g.dart new file mode 100644 index 00000000..8b28b749 --- /dev/null +++ b/lib/features/chat/models/chat_message_model.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'chat_message_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ChatMessageModelImpl _$$ChatMessageModelImplFromJson( + Map json) => + _$ChatMessageModelImpl( + id: (json['id'] as num).toInt(), + data: json['data'] as String, + mediaData: json['media_data'] as List, + authorId: (json['author_id'] as num).toInt(), + updatedAt: json['updated_at'] as String, + ); + +Map _$$ChatMessageModelImplToJson( + _$ChatMessageModelImpl instance) => + { + 'id': instance.id, + 'data': instance.data, + 'media_data': instance.mediaData, + 'author_id': instance.authorId, + 'updated_at': instance.updatedAt, + }; diff --git a/lib/features/chat/models/chat_room_model.dart b/lib/features/chat/models/chat_room_model.dart new file mode 100644 index 00000000..1fa5afe7 --- /dev/null +++ b/lib/features/chat/models/chat_room_model.dart @@ -0,0 +1,20 @@ +import 'package:eatvio_app/features/chat/models/chat_message_model.dart'; +import 'package:eatvio_app/features/chat/models/chat_room_settings_model.dart'; +import 'package:eatvio_app/features/chat/models/chat_user_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'chat_room_model.freezed.dart'; + +part 'chat_room_model.g.dart'; + +@freezed +class ChatRoomModel with _$ChatRoomModel { + const factory ChatRoomModel({ + required String id, + @JsonKey(name: 'chat_room_setting') required ChatRoomSettingsModel chatRoomSettings, + required List messages, + required Map users, + }) = _ChatRoomModel; + + factory ChatRoomModel.fromJson(Map json) => _$ChatRoomModelFromJson(json); +} diff --git a/lib/features/chat/models/chat_room_model.freezed.dart b/lib/features/chat/models/chat_room_model.freezed.dart new file mode 100644 index 00000000..b8b4ce46 --- /dev/null +++ b/lib/features/chat/models/chat_room_model.freezed.dart @@ -0,0 +1,255 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'chat_room_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ChatRoomModel _$ChatRoomModelFromJson(Map json) { + return _ChatRoomModel.fromJson(json); +} + +/// @nodoc +mixin _$ChatRoomModel { + String get id => throw _privateConstructorUsedError; + @JsonKey(name: 'chat_room_setting') + ChatRoomSettingsModel get chatRoomSettings => + throw _privateConstructorUsedError; + List get messages => throw _privateConstructorUsedError; + Map get users => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $ChatRoomModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ChatRoomModelCopyWith<$Res> { + factory $ChatRoomModelCopyWith( + ChatRoomModel value, $Res Function(ChatRoomModel) then) = + _$ChatRoomModelCopyWithImpl<$Res, ChatRoomModel>; + @useResult + $Res call( + {String id, + @JsonKey(name: 'chat_room_setting') + ChatRoomSettingsModel chatRoomSettings, + List messages, + Map users}); + + $ChatRoomSettingsModelCopyWith<$Res> get chatRoomSettings; +} + +/// @nodoc +class _$ChatRoomModelCopyWithImpl<$Res, $Val extends ChatRoomModel> + implements $ChatRoomModelCopyWith<$Res> { + _$ChatRoomModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? chatRoomSettings = null, + Object? messages = null, + Object? users = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + chatRoomSettings: null == chatRoomSettings + ? _value.chatRoomSettings + : chatRoomSettings // ignore: cast_nullable_to_non_nullable + as ChatRoomSettingsModel, + messages: null == messages + ? _value.messages + : messages // ignore: cast_nullable_to_non_nullable + as List, + users: null == users + ? _value.users + : users // ignore: cast_nullable_to_non_nullable + as Map, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $ChatRoomSettingsModelCopyWith<$Res> get chatRoomSettings { + return $ChatRoomSettingsModelCopyWith<$Res>(_value.chatRoomSettings, + (value) { + return _then(_value.copyWith(chatRoomSettings: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$ChatRoomModelImplCopyWith<$Res> + implements $ChatRoomModelCopyWith<$Res> { + factory _$$ChatRoomModelImplCopyWith( + _$ChatRoomModelImpl value, $Res Function(_$ChatRoomModelImpl) then) = + __$$ChatRoomModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String id, + @JsonKey(name: 'chat_room_setting') + ChatRoomSettingsModel chatRoomSettings, + List messages, + Map users}); + + @override + $ChatRoomSettingsModelCopyWith<$Res> get chatRoomSettings; +} + +/// @nodoc +class __$$ChatRoomModelImplCopyWithImpl<$Res> + extends _$ChatRoomModelCopyWithImpl<$Res, _$ChatRoomModelImpl> + implements _$$ChatRoomModelImplCopyWith<$Res> { + __$$ChatRoomModelImplCopyWithImpl( + _$ChatRoomModelImpl _value, $Res Function(_$ChatRoomModelImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? chatRoomSettings = null, + Object? messages = null, + Object? users = null, + }) { + return _then(_$ChatRoomModelImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + chatRoomSettings: null == chatRoomSettings + ? _value.chatRoomSettings + : chatRoomSettings // ignore: cast_nullable_to_non_nullable + as ChatRoomSettingsModel, + messages: null == messages + ? _value._messages + : messages // ignore: cast_nullable_to_non_nullable + as List, + users: null == users + ? _value._users + : users // ignore: cast_nullable_to_non_nullable + as Map, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ChatRoomModelImpl implements _ChatRoomModel { + const _$ChatRoomModelImpl( + {required this.id, + @JsonKey(name: 'chat_room_setting') required this.chatRoomSettings, + required final List messages, + required final Map users}) + : _messages = messages, + _users = users; + + factory _$ChatRoomModelImpl.fromJson(Map json) => + _$$ChatRoomModelImplFromJson(json); + + @override + final String id; + @override + @JsonKey(name: 'chat_room_setting') + final ChatRoomSettingsModel chatRoomSettings; + final List _messages; + @override + List get messages { + if (_messages is EqualUnmodifiableListView) return _messages; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_messages); + } + + final Map _users; + @override + Map get users { + if (_users is EqualUnmodifiableMapView) return _users; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_users); + } + + @override + String toString() { + return 'ChatRoomModel(id: $id, chatRoomSettings: $chatRoomSettings, messages: $messages, users: $users)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ChatRoomModelImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.chatRoomSettings, chatRoomSettings) || + other.chatRoomSettings == chatRoomSettings) && + const DeepCollectionEquality().equals(other._messages, _messages) && + const DeepCollectionEquality().equals(other._users, _users)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + id, + chatRoomSettings, + const DeepCollectionEquality().hash(_messages), + const DeepCollectionEquality().hash(_users)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ChatRoomModelImplCopyWith<_$ChatRoomModelImpl> get copyWith => + __$$ChatRoomModelImplCopyWithImpl<_$ChatRoomModelImpl>(this, _$identity); + + @override + Map toJson() { + return _$$ChatRoomModelImplToJson( + this, + ); + } +} + +abstract class _ChatRoomModel implements ChatRoomModel { + const factory _ChatRoomModel( + {required final String id, + @JsonKey(name: 'chat_room_setting') + required final ChatRoomSettingsModel chatRoomSettings, + required final List messages, + required final Map users}) = _$ChatRoomModelImpl; + + factory _ChatRoomModel.fromJson(Map json) = + _$ChatRoomModelImpl.fromJson; + + @override + String get id; + @override + @JsonKey(name: 'chat_room_setting') + ChatRoomSettingsModel get chatRoomSettings; + @override + List get messages; + @override + Map get users; + @override + @JsonKey(ignore: true) + _$$ChatRoomModelImplCopyWith<_$ChatRoomModelImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/chat/models/chat_room_model.g.dart b/lib/features/chat/models/chat_room_model.g.dart new file mode 100644 index 00000000..4ab38671 --- /dev/null +++ b/lib/features/chat/models/chat_room_model.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'chat_room_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ChatRoomModelImpl _$$ChatRoomModelImplFromJson(Map json) => + _$ChatRoomModelImpl( + id: json['id'] as String, + chatRoomSettings: ChatRoomSettingsModel.fromJson( + json['chat_room_setting'] as Map), + messages: (json['messages'] as List) + .map((e) => ChatMessageModel.fromJson(e as Map)) + .toList(), + users: (json['users'] as Map).map( + (k, e) => + MapEntry(k, ChatUserModel.fromJson(e as Map)), + ), + ); + +Map _$$ChatRoomModelImplToJson(_$ChatRoomModelImpl instance) => + { + 'id': instance.id, + 'chat_room_setting': instance.chatRoomSettings, + 'messages': instance.messages, + 'users': instance.users, + }; diff --git a/lib/features/chat/models/chat_room_settings_model.dart b/lib/features/chat/models/chat_room_settings_model.dart new file mode 100644 index 00000000..7262c9a2 --- /dev/null +++ b/lib/features/chat/models/chat_room_settings_model.dart @@ -0,0 +1,18 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'chat_room_settings_model.freezed.dart'; + +part 'chat_room_settings_model.g.dart'; + +@freezed +class ChatRoomSettingsModel with _$ChatRoomSettingsModel { + const factory ChatRoomSettingsModel({ + required int id, + @JsonKey(name: 'room_name') required String roomName, + required String state, + @JsonKey(name: 'access_level') required String accessLevel, + @JsonKey(name: 'updated_at') required String updatedAt, + }) = _ChatRoomSettingsModel; + + factory ChatRoomSettingsModel.fromJson(Map json) => _$ChatRoomSettingsModelFromJson(json); +} diff --git a/lib/features/chat/models/chat_room_settings_model.freezed.dart b/lib/features/chat/models/chat_room_settings_model.freezed.dart new file mode 100644 index 00000000..4f217556 --- /dev/null +++ b/lib/features/chat/models/chat_room_settings_model.freezed.dart @@ -0,0 +1,254 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'chat_room_settings_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ChatRoomSettingsModel _$ChatRoomSettingsModelFromJson( + Map json) { + return _ChatRoomSettingsModel.fromJson(json); +} + +/// @nodoc +mixin _$ChatRoomSettingsModel { + int get id => throw _privateConstructorUsedError; + @JsonKey(name: 'room_name') + String get roomName => throw _privateConstructorUsedError; + String get state => throw _privateConstructorUsedError; + @JsonKey(name: 'access_level') + String get accessLevel => throw _privateConstructorUsedError; + @JsonKey(name: 'updated_at') + String get updatedAt => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $ChatRoomSettingsModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ChatRoomSettingsModelCopyWith<$Res> { + factory $ChatRoomSettingsModelCopyWith(ChatRoomSettingsModel value, + $Res Function(ChatRoomSettingsModel) then) = + _$ChatRoomSettingsModelCopyWithImpl<$Res, ChatRoomSettingsModel>; + @useResult + $Res call( + {int id, + @JsonKey(name: 'room_name') String roomName, + String state, + @JsonKey(name: 'access_level') String accessLevel, + @JsonKey(name: 'updated_at') String updatedAt}); +} + +/// @nodoc +class _$ChatRoomSettingsModelCopyWithImpl<$Res, + $Val extends ChatRoomSettingsModel> + implements $ChatRoomSettingsModelCopyWith<$Res> { + _$ChatRoomSettingsModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? roomName = null, + Object? state = null, + Object? accessLevel = null, + Object? updatedAt = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + roomName: null == roomName + ? _value.roomName + : roomName // ignore: cast_nullable_to_non_nullable + as String, + state: null == state + ? _value.state + : state // ignore: cast_nullable_to_non_nullable + as String, + accessLevel: null == accessLevel + ? _value.accessLevel + : accessLevel // ignore: cast_nullable_to_non_nullable + as String, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ChatRoomSettingsModelImplCopyWith<$Res> + implements $ChatRoomSettingsModelCopyWith<$Res> { + factory _$$ChatRoomSettingsModelImplCopyWith( + _$ChatRoomSettingsModelImpl value, + $Res Function(_$ChatRoomSettingsModelImpl) then) = + __$$ChatRoomSettingsModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + @JsonKey(name: 'room_name') String roomName, + String state, + @JsonKey(name: 'access_level') String accessLevel, + @JsonKey(name: 'updated_at') String updatedAt}); +} + +/// @nodoc +class __$$ChatRoomSettingsModelImplCopyWithImpl<$Res> + extends _$ChatRoomSettingsModelCopyWithImpl<$Res, + _$ChatRoomSettingsModelImpl> + implements _$$ChatRoomSettingsModelImplCopyWith<$Res> { + __$$ChatRoomSettingsModelImplCopyWithImpl(_$ChatRoomSettingsModelImpl _value, + $Res Function(_$ChatRoomSettingsModelImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? roomName = null, + Object? state = null, + Object? accessLevel = null, + Object? updatedAt = null, + }) { + return _then(_$ChatRoomSettingsModelImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + roomName: null == roomName + ? _value.roomName + : roomName // ignore: cast_nullable_to_non_nullable + as String, + state: null == state + ? _value.state + : state // ignore: cast_nullable_to_non_nullable + as String, + accessLevel: null == accessLevel + ? _value.accessLevel + : accessLevel // ignore: cast_nullable_to_non_nullable + as String, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ChatRoomSettingsModelImpl implements _ChatRoomSettingsModel { + const _$ChatRoomSettingsModelImpl( + {required this.id, + @JsonKey(name: 'room_name') required this.roomName, + required this.state, + @JsonKey(name: 'access_level') required this.accessLevel, + @JsonKey(name: 'updated_at') required this.updatedAt}); + + factory _$ChatRoomSettingsModelImpl.fromJson(Map json) => + _$$ChatRoomSettingsModelImplFromJson(json); + + @override + final int id; + @override + @JsonKey(name: 'room_name') + final String roomName; + @override + final String state; + @override + @JsonKey(name: 'access_level') + final String accessLevel; + @override + @JsonKey(name: 'updated_at') + final String updatedAt; + + @override + String toString() { + return 'ChatRoomSettingsModel(id: $id, roomName: $roomName, state: $state, accessLevel: $accessLevel, updatedAt: $updatedAt)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ChatRoomSettingsModelImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.roomName, roomName) || + other.roomName == roomName) && + (identical(other.state, state) || other.state == state) && + (identical(other.accessLevel, accessLevel) || + other.accessLevel == accessLevel) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, id, roomName, state, accessLevel, updatedAt); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ChatRoomSettingsModelImplCopyWith<_$ChatRoomSettingsModelImpl> + get copyWith => __$$ChatRoomSettingsModelImplCopyWithImpl< + _$ChatRoomSettingsModelImpl>(this, _$identity); + + @override + Map toJson() { + return _$$ChatRoomSettingsModelImplToJson( + this, + ); + } +} + +abstract class _ChatRoomSettingsModel implements ChatRoomSettingsModel { + const factory _ChatRoomSettingsModel( + {required final int id, + @JsonKey(name: 'room_name') required final String roomName, + required final String state, + @JsonKey(name: 'access_level') required final String accessLevel, + @JsonKey(name: 'updated_at') required final String updatedAt}) = + _$ChatRoomSettingsModelImpl; + + factory _ChatRoomSettingsModel.fromJson(Map json) = + _$ChatRoomSettingsModelImpl.fromJson; + + @override + int get id; + @override + @JsonKey(name: 'room_name') + String get roomName; + @override + String get state; + @override + @JsonKey(name: 'access_level') + String get accessLevel; + @override + @JsonKey(name: 'updated_at') + String get updatedAt; + @override + @JsonKey(ignore: true) + _$$ChatRoomSettingsModelImplCopyWith<_$ChatRoomSettingsModelImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/features/chat/models/chat_room_settings_model.g.dart b/lib/features/chat/models/chat_room_settings_model.g.dart new file mode 100644 index 00000000..7b824334 --- /dev/null +++ b/lib/features/chat/models/chat_room_settings_model.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'chat_room_settings_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ChatRoomSettingsModelImpl _$$ChatRoomSettingsModelImplFromJson( + Map json) => + _$ChatRoomSettingsModelImpl( + id: (json['id'] as num).toInt(), + roomName: json['room_name'] as String, + state: json['state'] as String, + accessLevel: json['access_level'] as String, + updatedAt: json['updated_at'] as String, + ); + +Map _$$ChatRoomSettingsModelImplToJson( + _$ChatRoomSettingsModelImpl instance) => + { + 'id': instance.id, + 'room_name': instance.roomName, + 'state': instance.state, + 'access_level': instance.accessLevel, + 'updated_at': instance.updatedAt, + }; diff --git a/lib/features/chat/models/chat_user_model.dart b/lib/features/chat/models/chat_user_model.dart new file mode 100644 index 00000000..dd8bf02a --- /dev/null +++ b/lib/features/chat/models/chat_user_model.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'chat_user_model.freezed.dart'; + +part 'chat_user_model.g.dart'; + +@freezed +class ChatUserModel with _$ChatUserModel { + const factory ChatUserModel({ + required int id, + required String name, + @JsonKey(name: 'profile_photo_path') + String? profilePhotoPath, + @JsonKey(name: 'profile_photo_url') + String? profilePhotoUrl, + }) = _ChatUserModel; + + factory ChatUserModel.fromJson(Map json) => _$ChatUserModelFromJson(json); +} diff --git a/lib/features/chat/models/chat_user_model.freezed.dart b/lib/features/chat/models/chat_user_model.freezed.dart new file mode 100644 index 00000000..095c09e2 --- /dev/null +++ b/lib/features/chat/models/chat_user_model.freezed.dart @@ -0,0 +1,225 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'chat_user_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ChatUserModel _$ChatUserModelFromJson(Map json) { + return _ChatUserModel.fromJson(json); +} + +/// @nodoc +mixin _$ChatUserModel { + int get id => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + @JsonKey(name: 'profile_photo_path') + String? get profilePhotoPath => throw _privateConstructorUsedError; + @JsonKey(name: 'profile_photo_url') + String? get profilePhotoUrl => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $ChatUserModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ChatUserModelCopyWith<$Res> { + factory $ChatUserModelCopyWith( + ChatUserModel value, $Res Function(ChatUserModel) then) = + _$ChatUserModelCopyWithImpl<$Res, ChatUserModel>; + @useResult + $Res call( + {int id, + String name, + @JsonKey(name: 'profile_photo_path') String? profilePhotoPath, + @JsonKey(name: 'profile_photo_url') String? profilePhotoUrl}); +} + +/// @nodoc +class _$ChatUserModelCopyWithImpl<$Res, $Val extends ChatUserModel> + implements $ChatUserModelCopyWith<$Res> { + _$ChatUserModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? name = null, + Object? profilePhotoPath = freezed, + Object? profilePhotoUrl = freezed, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + profilePhotoPath: freezed == profilePhotoPath + ? _value.profilePhotoPath + : profilePhotoPath // ignore: cast_nullable_to_non_nullable + as String?, + profilePhotoUrl: freezed == profilePhotoUrl + ? _value.profilePhotoUrl + : profilePhotoUrl // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ChatUserModelImplCopyWith<$Res> + implements $ChatUserModelCopyWith<$Res> { + factory _$$ChatUserModelImplCopyWith( + _$ChatUserModelImpl value, $Res Function(_$ChatUserModelImpl) then) = + __$$ChatUserModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + String name, + @JsonKey(name: 'profile_photo_path') String? profilePhotoPath, + @JsonKey(name: 'profile_photo_url') String? profilePhotoUrl}); +} + +/// @nodoc +class __$$ChatUserModelImplCopyWithImpl<$Res> + extends _$ChatUserModelCopyWithImpl<$Res, _$ChatUserModelImpl> + implements _$$ChatUserModelImplCopyWith<$Res> { + __$$ChatUserModelImplCopyWithImpl( + _$ChatUserModelImpl _value, $Res Function(_$ChatUserModelImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? name = null, + Object? profilePhotoPath = freezed, + Object? profilePhotoUrl = freezed, + }) { + return _then(_$ChatUserModelImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + profilePhotoPath: freezed == profilePhotoPath + ? _value.profilePhotoPath + : profilePhotoPath // ignore: cast_nullable_to_non_nullable + as String?, + profilePhotoUrl: freezed == profilePhotoUrl + ? _value.profilePhotoUrl + : profilePhotoUrl // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ChatUserModelImpl implements _ChatUserModel { + const _$ChatUserModelImpl( + {required this.id, + required this.name, + @JsonKey(name: 'profile_photo_path') this.profilePhotoPath, + @JsonKey(name: 'profile_photo_url') this.profilePhotoUrl}); + + factory _$ChatUserModelImpl.fromJson(Map json) => + _$$ChatUserModelImplFromJson(json); + + @override + final int id; + @override + final String name; + @override + @JsonKey(name: 'profile_photo_path') + final String? profilePhotoPath; + @override + @JsonKey(name: 'profile_photo_url') + final String? profilePhotoUrl; + + @override + String toString() { + return 'ChatUserModel(id: $id, name: $name, profilePhotoPath: $profilePhotoPath, profilePhotoUrl: $profilePhotoUrl)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ChatUserModelImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.name, name) || other.name == name) && + (identical(other.profilePhotoPath, profilePhotoPath) || + other.profilePhotoPath == profilePhotoPath) && + (identical(other.profilePhotoUrl, profilePhotoUrl) || + other.profilePhotoUrl == profilePhotoUrl)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, id, name, profilePhotoPath, profilePhotoUrl); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ChatUserModelImplCopyWith<_$ChatUserModelImpl> get copyWith => + __$$ChatUserModelImplCopyWithImpl<_$ChatUserModelImpl>(this, _$identity); + + @override + Map toJson() { + return _$$ChatUserModelImplToJson( + this, + ); + } +} + +abstract class _ChatUserModel implements ChatUserModel { + const factory _ChatUserModel( + {required final int id, + required final String name, + @JsonKey(name: 'profile_photo_path') final String? profilePhotoPath, + @JsonKey(name: 'profile_photo_url') final String? profilePhotoUrl}) = + _$ChatUserModelImpl; + + factory _ChatUserModel.fromJson(Map json) = + _$ChatUserModelImpl.fromJson; + + @override + int get id; + @override + String get name; + @override + @JsonKey(name: 'profile_photo_path') + String? get profilePhotoPath; + @override + @JsonKey(name: 'profile_photo_url') + String? get profilePhotoUrl; + @override + @JsonKey(ignore: true) + _$$ChatUserModelImplCopyWith<_$ChatUserModelImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/chat/models/chat_user_model.g.dart b/lib/features/chat/models/chat_user_model.g.dart new file mode 100644 index 00000000..78e0f981 --- /dev/null +++ b/lib/features/chat/models/chat_user_model.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'chat_user_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ChatUserModelImpl _$$ChatUserModelImplFromJson(Map json) => + _$ChatUserModelImpl( + id: (json['id'] as num).toInt(), + name: json['name'] as String, + profilePhotoPath: json['profile_photo_path'] as String?, + profilePhotoUrl: json['profile_photo_url'] as String?, + ); + +Map _$$ChatUserModelImplToJson(_$ChatUserModelImpl instance) => + { + 'id': instance.id, + 'name': instance.name, + 'profile_photo_path': instance.profilePhotoPath, + 'profile_photo_url': instance.profilePhotoUrl, + }; diff --git a/lib/features/chat/models/websocket_info_model.dart b/lib/features/chat/models/websocket_info_model.dart new file mode 100644 index 00000000..9fe8abc4 --- /dev/null +++ b/lib/features/chat/models/websocket_info_model.dart @@ -0,0 +1,12 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'websocket_info_model.freezed.dart'; + +part 'websocket_info_model.g.dart'; + +@freezed +class WebsocketInfoModel with _$WebsocketInfoModel { + const factory WebsocketInfoModel() = _WebsocketInfoModel; + + factory WebsocketInfoModel.fromJson(Map json) => _$WebsocketInfoModelFromJson(json); +} diff --git a/lib/features/chat/models/websocket_info_model.freezed.dart b/lib/features/chat/models/websocket_info_model.freezed.dart new file mode 100644 index 00000000..fd9ef4b9 --- /dev/null +++ b/lib/features/chat/models/websocket_info_model.freezed.dart @@ -0,0 +1,96 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'websocket_info_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +WebsocketInfoModel _$WebsocketInfoModelFromJson(Map json) { + return _WebsocketInfoModel.fromJson(json); +} + +/// @nodoc +mixin _$WebsocketInfoModel { + Map toJson() => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $WebsocketInfoModelCopyWith<$Res> { + factory $WebsocketInfoModelCopyWith( + WebsocketInfoModel value, $Res Function(WebsocketInfoModel) then) = + _$WebsocketInfoModelCopyWithImpl<$Res, WebsocketInfoModel>; +} + +/// @nodoc +class _$WebsocketInfoModelCopyWithImpl<$Res, $Val extends WebsocketInfoModel> + implements $WebsocketInfoModelCopyWith<$Res> { + _$WebsocketInfoModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$WebsocketInfoModelImplCopyWith<$Res> { + factory _$$WebsocketInfoModelImplCopyWith(_$WebsocketInfoModelImpl value, + $Res Function(_$WebsocketInfoModelImpl) then) = + __$$WebsocketInfoModelImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$WebsocketInfoModelImplCopyWithImpl<$Res> + extends _$WebsocketInfoModelCopyWithImpl<$Res, _$WebsocketInfoModelImpl> + implements _$$WebsocketInfoModelImplCopyWith<$Res> { + __$$WebsocketInfoModelImplCopyWithImpl(_$WebsocketInfoModelImpl _value, + $Res Function(_$WebsocketInfoModelImpl) _then) + : super(_value, _then); +} + +/// @nodoc +@JsonSerializable() +class _$WebsocketInfoModelImpl implements _WebsocketInfoModel { + const _$WebsocketInfoModelImpl(); + + factory _$WebsocketInfoModelImpl.fromJson(Map json) => + _$$WebsocketInfoModelImplFromJson(json); + + @override + String toString() { + return 'WebsocketInfoModel()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$WebsocketInfoModelImpl); + } + + @JsonKey(ignore: true) + @override + int get hashCode => runtimeType.hashCode; + + @override + Map toJson() { + return _$$WebsocketInfoModelImplToJson( + this, + ); + } +} + +abstract class _WebsocketInfoModel implements WebsocketInfoModel { + const factory _WebsocketInfoModel() = _$WebsocketInfoModelImpl; + + factory _WebsocketInfoModel.fromJson(Map json) = + _$WebsocketInfoModelImpl.fromJson; +} diff --git a/lib/features/chat/models/websocket_info_model.g.dart b/lib/features/chat/models/websocket_info_model.g.dart new file mode 100644 index 00000000..6573c3c7 --- /dev/null +++ b/lib/features/chat/models/websocket_info_model.g.dart @@ -0,0 +1,15 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'websocket_info_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$WebsocketInfoModelImpl _$$WebsocketInfoModelImplFromJson( + Map json) => + _$WebsocketInfoModelImpl(); + +Map _$$WebsocketInfoModelImplToJson( + _$WebsocketInfoModelImpl instance) => + {}; diff --git a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart new file mode 100644 index 00000000..f66069e2 --- /dev/null +++ b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart @@ -0,0 +1,20 @@ +import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart'; +import 'package:eatvio_app/features/chat/repositories/chat_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ChatRoomCubit extends Cubit { + final ChatRepository chatRepository; + + ChatRoomCubit({ + required this.chatRepository, + }) : super(ChatRoomState.loading()); + + initChat(String chatId) async { + try { + final chatRoomModel = await chatRepository.getChatRoom(chatId); + emit(ChatRoomState.loaded(chatRoomModel: chatRoomModel)); + } catch (e) { + emit(ChatRoomState.error(errorMessage: 'Ошибка получеия данных')); + } + } +} diff --git a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart new file mode 100644 index 00000000..89184a63 --- /dev/null +++ b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart @@ -0,0 +1,19 @@ +import 'package:eatvio_app/features/chat/models/chat_room_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'chat_room_state.freezed.dart'; + +@freezed +abstract class ChatRoomState with _$ChatRoomState { + const factory ChatRoomState() = _ChatRoomState; + + const factory ChatRoomState.loading() = ChatRoomStateLoading; + + const factory ChatRoomState.loaded({ + required ChatRoomModel chatRoomModel, + }) = ChatRoomStateLoaded; + + const factory ChatRoomState.error({ + required String errorMessage, + }) = ChatRoomStateError; +} diff --git a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.freezed.dart b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.freezed.dart new file mode 100644 index 00000000..5bd9ae89 --- /dev/null +++ b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.freezed.dart @@ -0,0 +1,622 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'chat_room_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$ChatRoomState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(ChatRoomModel chatRoomModel) loaded, + required TResult Function(String errorMessage) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(ChatRoomModel chatRoomModel)? loaded, + TResult? Function(String errorMessage)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(ChatRoomModel chatRoomModel)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(_ChatRoomState value) $default, { + required TResult Function(ChatRoomStateLoading value) loading, + required TResult Function(ChatRoomStateLoaded value) loaded, + required TResult Function(ChatRoomStateError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_ChatRoomState value)? $default, { + TResult? Function(ChatRoomStateLoading value)? loading, + TResult? Function(ChatRoomStateLoaded value)? loaded, + TResult? Function(ChatRoomStateError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(_ChatRoomState value)? $default, { + TResult Function(ChatRoomStateLoading value)? loading, + TResult Function(ChatRoomStateLoaded value)? loaded, + TResult Function(ChatRoomStateError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ChatRoomStateCopyWith<$Res> { + factory $ChatRoomStateCopyWith( + ChatRoomState value, $Res Function(ChatRoomState) then) = + _$ChatRoomStateCopyWithImpl<$Res, ChatRoomState>; +} + +/// @nodoc +class _$ChatRoomStateCopyWithImpl<$Res, $Val extends ChatRoomState> + implements $ChatRoomStateCopyWith<$Res> { + _$ChatRoomStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$ChatRoomStateImplCopyWith<$Res> { + factory _$$ChatRoomStateImplCopyWith( + _$ChatRoomStateImpl value, $Res Function(_$ChatRoomStateImpl) then) = + __$$ChatRoomStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$ChatRoomStateImplCopyWithImpl<$Res> + extends _$ChatRoomStateCopyWithImpl<$Res, _$ChatRoomStateImpl> + implements _$$ChatRoomStateImplCopyWith<$Res> { + __$$ChatRoomStateImplCopyWithImpl( + _$ChatRoomStateImpl _value, $Res Function(_$ChatRoomStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$ChatRoomStateImpl implements _ChatRoomState { + const _$ChatRoomStateImpl(); + + @override + String toString() { + return 'ChatRoomState()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$ChatRoomStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(ChatRoomModel chatRoomModel) loaded, + required TResult Function(String errorMessage) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(ChatRoomModel chatRoomModel)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(ChatRoomModel chatRoomModel)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_ChatRoomState value) $default, { + required TResult Function(ChatRoomStateLoading value) loading, + required TResult Function(ChatRoomStateLoaded value) loaded, + required TResult Function(ChatRoomStateError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_ChatRoomState value)? $default, { + TResult? Function(ChatRoomStateLoading value)? loading, + TResult? Function(ChatRoomStateLoaded value)? loaded, + TResult? Function(ChatRoomStateError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_ChatRoomState value)? $default, { + TResult Function(ChatRoomStateLoading value)? loading, + TResult Function(ChatRoomStateLoaded value)? loaded, + TResult Function(ChatRoomStateError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class _ChatRoomState implements ChatRoomState { + const factory _ChatRoomState() = _$ChatRoomStateImpl; +} + +/// @nodoc +abstract class _$$ChatRoomStateLoadingImplCopyWith<$Res> { + factory _$$ChatRoomStateLoadingImplCopyWith(_$ChatRoomStateLoadingImpl value, + $Res Function(_$ChatRoomStateLoadingImpl) then) = + __$$ChatRoomStateLoadingImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$ChatRoomStateLoadingImplCopyWithImpl<$Res> + extends _$ChatRoomStateCopyWithImpl<$Res, _$ChatRoomStateLoadingImpl> + implements _$$ChatRoomStateLoadingImplCopyWith<$Res> { + __$$ChatRoomStateLoadingImplCopyWithImpl(_$ChatRoomStateLoadingImpl _value, + $Res Function(_$ChatRoomStateLoadingImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$ChatRoomStateLoadingImpl implements ChatRoomStateLoading { + const _$ChatRoomStateLoadingImpl(); + + @override + String toString() { + return 'ChatRoomState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ChatRoomStateLoadingImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(ChatRoomModel chatRoomModel) loaded, + required TResult Function(String errorMessage) error, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(ChatRoomModel chatRoomModel)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(ChatRoomModel chatRoomModel)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_ChatRoomState value) $default, { + required TResult Function(ChatRoomStateLoading value) loading, + required TResult Function(ChatRoomStateLoaded value) loaded, + required TResult Function(ChatRoomStateError value) error, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_ChatRoomState value)? $default, { + TResult? Function(ChatRoomStateLoading value)? loading, + TResult? Function(ChatRoomStateLoaded value)? loaded, + TResult? Function(ChatRoomStateError value)? error, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_ChatRoomState value)? $default, { + TResult Function(ChatRoomStateLoading value)? loading, + TResult Function(ChatRoomStateLoaded value)? loaded, + TResult Function(ChatRoomStateError value)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class ChatRoomStateLoading implements ChatRoomState { + const factory ChatRoomStateLoading() = _$ChatRoomStateLoadingImpl; +} + +/// @nodoc +abstract class _$$ChatRoomStateLoadedImplCopyWith<$Res> { + factory _$$ChatRoomStateLoadedImplCopyWith(_$ChatRoomStateLoadedImpl value, + $Res Function(_$ChatRoomStateLoadedImpl) then) = + __$$ChatRoomStateLoadedImplCopyWithImpl<$Res>; + @useResult + $Res call({ChatRoomModel chatRoomModel}); + + $ChatRoomModelCopyWith<$Res> get chatRoomModel; +} + +/// @nodoc +class __$$ChatRoomStateLoadedImplCopyWithImpl<$Res> + extends _$ChatRoomStateCopyWithImpl<$Res, _$ChatRoomStateLoadedImpl> + implements _$$ChatRoomStateLoadedImplCopyWith<$Res> { + __$$ChatRoomStateLoadedImplCopyWithImpl(_$ChatRoomStateLoadedImpl _value, + $Res Function(_$ChatRoomStateLoadedImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? chatRoomModel = null, + }) { + return _then(_$ChatRoomStateLoadedImpl( + chatRoomModel: null == chatRoomModel + ? _value.chatRoomModel + : chatRoomModel // ignore: cast_nullable_to_non_nullable + as ChatRoomModel, + )); + } + + @override + @pragma('vm:prefer-inline') + $ChatRoomModelCopyWith<$Res> get chatRoomModel { + return $ChatRoomModelCopyWith<$Res>(_value.chatRoomModel, (value) { + return _then(_value.copyWith(chatRoomModel: value)); + }); + } +} + +/// @nodoc + +class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded { + const _$ChatRoomStateLoadedImpl({required this.chatRoomModel}); + + @override + final ChatRoomModel chatRoomModel; + + @override + String toString() { + return 'ChatRoomState.loaded(chatRoomModel: $chatRoomModel)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ChatRoomStateLoadedImpl && + (identical(other.chatRoomModel, chatRoomModel) || + other.chatRoomModel == chatRoomModel)); + } + + @override + int get hashCode => Object.hash(runtimeType, chatRoomModel); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ChatRoomStateLoadedImplCopyWith<_$ChatRoomStateLoadedImpl> get copyWith => + __$$ChatRoomStateLoadedImplCopyWithImpl<_$ChatRoomStateLoadedImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(ChatRoomModel chatRoomModel) loaded, + required TResult Function(String errorMessage) error, + }) { + return loaded(chatRoomModel); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(ChatRoomModel chatRoomModel)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return loaded?.call(chatRoomModel); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(ChatRoomModel chatRoomModel)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(chatRoomModel); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_ChatRoomState value) $default, { + required TResult Function(ChatRoomStateLoading value) loading, + required TResult Function(ChatRoomStateLoaded value) loaded, + required TResult Function(ChatRoomStateError value) error, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_ChatRoomState value)? $default, { + TResult? Function(ChatRoomStateLoading value)? loading, + TResult? Function(ChatRoomStateLoaded value)? loaded, + TResult? Function(ChatRoomStateError value)? error, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_ChatRoomState value)? $default, { + TResult Function(ChatRoomStateLoading value)? loading, + TResult Function(ChatRoomStateLoaded value)? loaded, + TResult Function(ChatRoomStateError value)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class ChatRoomStateLoaded implements ChatRoomState { + const factory ChatRoomStateLoaded( + {required final ChatRoomModel chatRoomModel}) = _$ChatRoomStateLoadedImpl; + + ChatRoomModel get chatRoomModel; + @JsonKey(ignore: true) + _$$ChatRoomStateLoadedImplCopyWith<_$ChatRoomStateLoadedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$ChatRoomStateErrorImplCopyWith<$Res> { + factory _$$ChatRoomStateErrorImplCopyWith(_$ChatRoomStateErrorImpl value, + $Res Function(_$ChatRoomStateErrorImpl) then) = + __$$ChatRoomStateErrorImplCopyWithImpl<$Res>; + @useResult + $Res call({String errorMessage}); +} + +/// @nodoc +class __$$ChatRoomStateErrorImplCopyWithImpl<$Res> + extends _$ChatRoomStateCopyWithImpl<$Res, _$ChatRoomStateErrorImpl> + implements _$$ChatRoomStateErrorImplCopyWith<$Res> { + __$$ChatRoomStateErrorImplCopyWithImpl(_$ChatRoomStateErrorImpl _value, + $Res Function(_$ChatRoomStateErrorImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? errorMessage = null, + }) { + return _then(_$ChatRoomStateErrorImpl( + errorMessage: null == errorMessage + ? _value.errorMessage + : errorMessage // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$ChatRoomStateErrorImpl implements ChatRoomStateError { + const _$ChatRoomStateErrorImpl({required this.errorMessage}); + + @override + final String errorMessage; + + @override + String toString() { + return 'ChatRoomState.error(errorMessage: $errorMessage)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ChatRoomStateErrorImpl && + (identical(other.errorMessage, errorMessage) || + other.errorMessage == errorMessage)); + } + + @override + int get hashCode => Object.hash(runtimeType, errorMessage); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ChatRoomStateErrorImplCopyWith<_$ChatRoomStateErrorImpl> get copyWith => + __$$ChatRoomStateErrorImplCopyWithImpl<_$ChatRoomStateErrorImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(ChatRoomModel chatRoomModel) loaded, + required TResult Function(String errorMessage) error, + }) { + return error(errorMessage); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(ChatRoomModel chatRoomModel)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return error?.call(errorMessage); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(ChatRoomModel chatRoomModel)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(errorMessage); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_ChatRoomState value) $default, { + required TResult Function(ChatRoomStateLoading value) loading, + required TResult Function(ChatRoomStateLoaded value) loaded, + required TResult Function(ChatRoomStateError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_ChatRoomState value)? $default, { + TResult? Function(ChatRoomStateLoading value)? loading, + TResult? Function(ChatRoomStateLoaded value)? loaded, + TResult? Function(ChatRoomStateError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_ChatRoomState value)? $default, { + TResult Function(ChatRoomStateLoading value)? loading, + TResult Function(ChatRoomStateLoaded value)? loaded, + TResult Function(ChatRoomStateError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class ChatRoomStateError implements ChatRoomState { + const factory ChatRoomStateError({required final String errorMessage}) = + _$ChatRoomStateErrorImpl; + + String get errorMessage; + @JsonKey(ignore: true) + _$$ChatRoomStateErrorImplCopyWith<_$ChatRoomStateErrorImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart new file mode 100644 index 00000000..c6ad9beb --- /dev/null +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -0,0 +1,200 @@ +import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart'; +import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart'; +import 'package:eatvio_app/features/chat/presentation/widgets/chat_bubble_widget.dart'; +import 'package:eatvio_app/locator_service.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:flutter/material.dart'; + +class ChatScreen extends StatelessWidget { + final String chatId; + + const ChatScreen({ + required this.chatId, + super.key, + }); + + Widget build(BuildContext context) { + return BlocProvider( + create: (BuildContext context) => sl()..initChat(chatId), + child: BlocBuilder( + builder: (context, state) { + if (state is ChatRoomStateLoading) { + return Scaffold( + body: Center( + child: CupertinoActivityIndicator(), + ), + ); + } else if (state is ChatRoomStateLoaded) { + return _buildChat(context, state); + } else if (state is ChatRoomStateError) { + return Center(child: Text(state.errorMessage)); + } else { + return Scaffold( + body: Center( + child: CupertinoActivityIndicator(), + ), + ); + } + }, + ), + ); + } + + _buildChat(BuildContext context, ChatRoomStateLoaded state) { + mediaQueryData = MediaQuery.of(context); + return Scaffold( + resizeToAvoidBottomInset: true, + backgroundColor: PrimaryColors().gray50, + appBar: AppBar( + titleSpacing: 0, + centerTitle: false, + backgroundColor: Colors.white, + forceMaterialTransparency: true, + elevation: 0, + title: Row( + children: [ + Container( + width: 40, + height: 40, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Color(0xFFEBF4FF), + shape: BoxShape.circle, + ), + child: Text( + state.chatRoomModel.users['to_chat_user']!.name.split(' ').first[0] + state.chatRoomModel.users['to_chat_user']!.name.split(' ')[1][0], + style: TextStyle( + color: Color(0xFF7F9CF5), + fontWeight: FontWeight.w400, + fontSize: 20, + ), + ), + ), + SizedBox(width: 16), + Text( + state.chatRoomModel.users['to_chat_user']!.name, + style: CustomTextStyles.titleSmallBold.copyWith( + color: PrimaryColors().blueGray800, + ), + ), + ], + ), + ), + body: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Container( + color: Color(0xFFF0F2F5), + width: double.maxFinite, + child: CustomScrollView( + reverse: true, + slivers: [ + SliverPadding( + padding: EdgeInsets.symmetric(vertical: 16), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) => ChatBubbleWidget( + text: state.chatRoomModel.messages[state.chatRoomModel.messages.length - index - 1].data, + isCurrentUser: state.chatRoomModel.users['to_chat_user']!.id != + state.chatRoomModel.messages[state.chatRoomModel.messages.length - index - 1].authorId, + mediaData: state.chatRoomModel.messages[state.chatRoomModel.messages.length - index - 1].mediaData, + ), + childCount: state.chatRoomModel.messages.length, + ), + ), + ), + ], + ), + ), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(width: 8), + InkWell( + onTap: () {}, + borderRadius: BorderRadiusStyle.circleBorder17, + child: Ink( + height: 32, + width: 32, + child: Icon( + Icons.attach_file_rounded, + size: 28, + color: Color(0xFF858E99), + ), + ), + ), + SizedBox(width: 8), + SizedBox( + width: mediaQueryData.size.width - 96, + child: TextFormField( + maxLines: 5, + minLines: 1, + keyboardType: TextInputType.multiline, + style: TextStyle( + fontSize: 16, + fontFamily: 'Mulish', + fontWeight: FontWeight.w400, + color: Colors.black, + ), + decoration: InputDecoration( + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), + hintText: 'Сообщение', + hintStyle: TextStyle( + fontSize: 16, + fontFamily: 'Mulish', + fontWeight: FontWeight.w400, + color: Color(0xFFAEAEB2), + ), + filled: true, + border: OutlineInputBorder( + borderRadius: BorderRadiusStyle.circleBorder17, + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadiusStyle.circleBorder17, + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadiusStyle.circleBorder17, + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + ), + ), + ), + SizedBox(width: 8), + InkWell( + borderRadius: BorderRadiusStyle.circleBorder17, + onTap: () {}, + child: Ink( + height: 32, + width: 32, + child: Icon( + Icons.send_outlined, + size: 28, + color: Color(0xFF858E99), + ), + ), + ), + SizedBox(width: 8), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/chat/chat_select_screen/bloc/chat_select_bloc.dart b/lib/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_bloc.dart similarity index 94% rename from lib/presentation/chat/chat_select_screen/bloc/chat_select_bloc.dart rename to lib/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_bloc.dart index b71f87c2..ba44ade1 100644 --- a/lib/presentation/chat/chat_select_screen/bloc/chat_select_bloc.dart +++ b/lib/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_bloc.dart @@ -1,4 +1,6 @@ import 'package:eatvio_app/app_router.dart'; +import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/models/chat_entry.dart'; +import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/models/chat_select_model.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -8,8 +10,6 @@ import 'package:intl/intl.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'dart:convert'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/chat/chat_select_screen/models/chat_select_model.dart'; -import 'package:eatvio_app/presentation/chat/chat_select_screen/models/chat_entry.dart'; import 'package:go_router/go_router.dart'; part 'chat_select_event.dart'; diff --git a/lib/presentation/chat/chat_select_screen/bloc/chat_select_event.dart b/lib/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_event.dart similarity index 100% rename from lib/presentation/chat/chat_select_screen/bloc/chat_select_event.dart rename to lib/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_event.dart diff --git a/lib/presentation/chat/chat_select_screen/bloc/chat_select_state.dart b/lib/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_state.dart similarity index 100% rename from lib/presentation/chat/chat_select_screen/bloc/chat_select_state.dart rename to lib/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_state.dart diff --git a/lib/presentation/chat/chat_select_screen/chat_search_view.dart b/lib/features/chat/presentation/pages/chat_select_screen/chat_search_view.dart similarity index 76% rename from lib/presentation/chat/chat_select_screen/chat_search_view.dart rename to lib/features/chat/presentation/pages/chat_select_screen/chat_search_view.dart index b9447660..939bb42d 100644 --- a/lib/presentation/chat/chat_select_screen/chat_search_view.dart +++ b/lib/features/chat/presentation/pages/chat_select_screen/chat_search_view.dart @@ -73,18 +73,13 @@ class CustomSearchView extends StatelessWidget { @override Widget build(BuildContext context) { - return alignment != null - ? Align( - alignment: alignment ?? Alignment.center, - child: searchViewWidget(context)) - : searchViewWidget(context); + return alignment != null ? Align(alignment: alignment ?? Alignment.center, child: searchViewWidget(context)) : searchViewWidget(context); } searchViewWidget(BuildContext context) => SizedBox( width: width ?? double.maxFinite, child: TextFormField( - scrollPadding: - EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + scrollPadding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), controller: controller, focusNode: focusNode, onTapOutside: (event) => onTapOutside(), @@ -110,26 +105,19 @@ class CustomSearchView extends StatelessWidget { hintText: hintText ?? "", hintStyle: hintStyle ?? CustomTextStyles.bodySmallMulishBluegray800, prefixIcon: prefix ?? - SizedBox( - child: Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(Icons.search)), - ), - prefixIconConstraints: prefixConstraints ?? - BoxConstraints( - maxHeight: 40.v, + Padding( + padding: EdgeInsets.only(left: 10), + child: Icon( + Icons.search, + color: Color(0xFF7B8794), + ), ), suffixIcon: suffix ?? - Padding( - padding: EdgeInsets.only( - right: 15.h, - ), - child: IconButton( - onPressed: () => controller!.clear(), - icon: Icon( - Icons.clear, - color: Colors.grey.shade600, - ), + IconButton( + onPressed: () => controller!.clear(), + icon: Icon( + Icons.clear, + color: Colors.grey.shade600, ), ), suffixIconConstraints: suffixConstraints ?? @@ -137,12 +125,7 @@ class CustomSearchView extends StatelessWidget { maxHeight: 40.v, ), isDense: true, - contentPadding: contentPadding ?? - EdgeInsets.only( - top: 11.v, - right: 11.h, - bottom: 11.v, - ), + contentPadding: EdgeInsets.all(16), fillColor: fillColor ?? appTheme.gray100, filled: filled, border: borderDecoration ?? diff --git a/lib/features/chat/presentation/pages/chat_select_screen/chat_select_screen.dart b/lib/features/chat/presentation/pages/chat_select_screen/chat_select_screen.dart new file mode 100644 index 00000000..a2068225 --- /dev/null +++ b/lib/features/chat/presentation/pages/chat_select_screen/chat_select_screen.dart @@ -0,0 +1,97 @@ +import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/models/chat_entry.dart'; +import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/widgets/chat_entry_widget.dart'; +import 'package:flutter/cupertino.dart'; + +import 'bloc/chat_select_bloc.dart'; +import 'models/chat_select_model.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'chat_search_view.dart'; +import 'package:flutter/material.dart'; + +class ChatSelectScreen extends StatelessWidget { + const ChatSelectScreen({Key? key}) : super(key: key); + + static Widget builder(BuildContext context) { + return BlocProvider( + create: (context) => ChatSelectBloc( + ChatSelectState( + chatSelectModelObj: ChatSelectModel(), + ), + )..add( + ChatSelectInitialEvent(), + ), + child: ChatSelectScreen(), + ); + } + + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is ChatSelectInitialState) { + return Scaffold( + body: Center( + child: CupertinoActivityIndicator(), + ), + ); + } else if (state is ChatSelectLoadedState) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.white, + forceMaterialTransparency: true, + toolbarHeight: 0, + ), + body: NestedScrollView( + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverAppBar( + backgroundColor: Colors.white, + forceMaterialTransparency: true, + floating: true, + snap: true, + title: CustomSearchView( + controller: state.searchController, + fillColor: Color(0xFFF0F2F5), + hintText: "Поиск", + hintStyle: TextStyle( + color: Color(0xFF7B8794), + ), + ), + ), + ]; + }, + body: _buildChatList(context, state), + ), + ); + } else if (state is ChatSelectErrorState) { + return Center(child: Text(state.errorMessage)); + } else { + return Scaffold( + body: Center( + child: CupertinoActivityIndicator(), + ), + ); + } + }, + ); + } +} + +_buildChatList(BuildContext context, ChatSelectLoadedState state) { + return ListView.separated( + padding: EdgeInsets.all(16), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + separatorBuilder: (context, index) { + return SizedBox( + height: 10.v, + ); + }, + itemCount: state.chatRooms?.length ?? 0, + itemBuilder: (context, index) { + ChatItemModel model = state.chatRooms?[index]; + return ChatEntryWidget( + chatEntryObj: model, + ); + }, + ); +} diff --git a/lib/presentation/chat/chat_select_screen/models/chat_entry.dart b/lib/features/chat/presentation/pages/chat_select_screen/models/chat_entry.dart similarity index 100% rename from lib/presentation/chat/chat_select_screen/models/chat_entry.dart rename to lib/features/chat/presentation/pages/chat_select_screen/models/chat_entry.dart diff --git a/lib/presentation/chat/chat_select_screen/models/chat_select_model.dart b/lib/features/chat/presentation/pages/chat_select_screen/models/chat_select_model.dart similarity index 84% rename from lib/presentation/chat/chat_select_screen/models/chat_select_model.dart rename to lib/features/chat/presentation/pages/chat_select_screen/models/chat_select_model.dart index 2b90449e..c95b52ee 100644 --- a/lib/presentation/chat/chat_select_screen/models/chat_select_model.dart +++ b/lib/features/chat/presentation/pages/chat_select_screen/models/chat_select_model.dart @@ -1,7 +1,7 @@ // ignore_for_file: must_be_immutable +import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/models/chat_entry.dart'; import 'package:equatable/equatable.dart'; -import 'package:eatvio_app/presentation/chat/chat_select_screen/models/chat_entry.dart'; /// This class defines the variables used in the [chat_select_screen], /// and is typically used to hold data that is passed between different parts of the application. diff --git a/lib/features/chat/presentation/pages/chat_select_screen/widgets/chat_entry_widget.dart b/lib/features/chat/presentation/pages/chat_select_screen/widgets/chat_entry_widget.dart new file mode 100644 index 00000000..fbd6b1b0 --- /dev/null +++ b/lib/features/chat/presentation/pages/chat_select_screen/widgets/chat_entry_widget.dart @@ -0,0 +1,93 @@ +// ignore_for_file: must_be_immutable + +import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/models/chat_entry.dart'; +import 'package:flutter/material.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:go_router/go_router.dart'; + +class ChatEntryWidget extends StatelessWidget { + ChatEntryWidget({ + required this.chatEntryObj, + super.key, + }); + + ChatItemModel chatEntryObj; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => GoRouter.of(context).goNamed( + 'chatScreen', + extra: chatEntryObj.id.toString(), + ), + child: Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadiusStyle.circleBorder8, + color: Color(0xFFF6FAFF), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 48, + height: 48, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Color(0xFFEBF4FF), + shape: BoxShape.circle, + ), + child: Text( + chatEntryObj.username!.split(' ').first[0] + chatEntryObj.username!.split(' ')[1][0], + style: TextStyle( + color: Color(0xFF7F9CF5), + fontWeight: FontWeight.w400, + fontSize: 20, + ), + ), + ), + SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + chatEntryObj.username!.length <= 15 ? chatEntryObj.username! : chatEntryObj.username!.substring(0, 15) + '...', + style: theme.textTheme.titleMedium, + ), + Spacer(), + CustomImageView( + imagePath: ImageConstant.imageNotFound, + height: 9.v, + width: 16.h, + margin: EdgeInsets.only( + top: 4.v, + bottom: 6.v, + ), + ), + Padding( + padding: EdgeInsets.only(left: 6.h), + child: Text( + chatEntryObj.dateText!, + style: theme.textTheme.bodyMedium, + ), + ) + ], + ), + SizedBox(height: 6.v), + Text( + chatEntryObj.greetingText!, + style: CustomTextStyles.bodyMediumBluegray800, + ), + ], + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/chat/presentation/pages/photo_view_screen.dart b/lib/features/chat/presentation/pages/photo_view_screen.dart new file mode 100644 index 00000000..229ced53 --- /dev/null +++ b/lib/features/chat/presentation/pages/photo_view_screen.dart @@ -0,0 +1,124 @@ +import 'package:eatvio_app/core/app_export.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:go_router/go_router.dart'; +import 'package:photo_view/photo_view.dart'; +import 'package:photo_view/photo_view_gallery.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class PhotoViewScreen extends StatefulWidget { + final List imageUrls; + final int index; + + const PhotoViewScreen({ + super.key, + required this.imageUrls, + required this.index, + }); + + @override + State createState() => _PhotoViewScreenState(); +} + +class _PhotoViewScreenState extends State { + late final pageController; + int imageIndex = 0; + + @override + void initState() { + imageIndex = widget.index; + pageController = PageController(initialPage: imageIndex); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + centerTitle: true, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarColor: Colors.black, + statusBarBrightness: Brightness.dark, + statusBarIconBrightness: Brightness.light, + ), + backgroundColor: Colors.black, + foregroundColor: Colors.white, + title: Text( + (imageIndex + 1).toString() + ' из ' + widget.imageUrls.length.toString(), + ), + ), + body: Dismissible( + direction: DismissDirection.vertical, + key: const Key('key'), + onDismissed: (_) => GoRouter.of(context).pop(), + resizeDuration: Duration(microseconds: 1), + child: Container( + child: PhotoViewGallery.builder( + scrollPhysics: const BouncingScrollPhysics(), + builder: (BuildContext context, int index) { + return PhotoViewGalleryPageOptions( + imageProvider: NetworkImage(widget.imageUrls[index]), + initialScale: PhotoViewComputedScale.contained, + minScale: PhotoViewComputedScale.contained, + heroAttributes: PhotoViewHeroAttributes(tag: widget.imageUrls[index]), + errorBuilder: (context, object, stackTrace) { + return Column( + children: [ + SizedBox(height: 32), + Text('Файл'), + SizedBox(height: 8), + ListTile( + shape: RoundedRectangleBorder( + borderRadius: BorderRadiusStyle.circleBorder17, + ), + contentPadding: EdgeInsets.all(16), + onTap: () async { + if (!await launchUrl(Uri.parse(widget.imageUrls[index]))) { + throw Exception('Could not launch ${widget.imageUrls[index]}'); + } + }, + leading: Icon( + Icons.file_copy_outlined, + color: Colors.white, + ), + trailing: Icon( + Icons.download_outlined, + color: Colors.white, + ), + title: Text( + widget.imageUrls[index].replaceAll(dotenv.env['STORAGE_URL']! + 'chat_files/', ''), + style: TextStyle( + color: Colors.white, + fontSize: 12, + ), + ), + ), + ], + ); + }, + ); + }, + itemCount: widget.imageUrls.length, + loadingBuilder: (context, event) => Center( + child: Container( + width: 20.0, + height: 20.0, + child: CircularProgressIndicator( + value: event == null ? 0 : event.cumulativeBytesLoaded / event.expectedTotalBytes!, + ), + ), + ), + pageController: pageController, + onPageChanged: (page) { + setState(() { + imageIndex = page; + }); + }, + ), + ), + ), + ); + } +} diff --git a/lib/features/chat/presentation/widgets/chat_bubble_widget.dart b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart new file mode 100644 index 00000000..127795cc --- /dev/null +++ b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart @@ -0,0 +1,110 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:eatvio_app/theme/app_decoration.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:go_router/go_router.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class ChatBubbleWidget extends StatelessWidget { + const ChatBubbleWidget({ + super.key, + required this.text, + required this.isCurrentUser, + this.mediaData, + }); + + final String text; + final bool isCurrentUser; + final List? mediaData; + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.fromLTRB(isCurrentUser ? 64 : 16, 4, isCurrentUser ? 16 : 64, 4), + child: Align( + alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft, + child: DecoratedBox( + decoration: BoxDecoration( + color: isCurrentUser ? Color(0xFFF6FAFF) : Colors.white, + borderRadius: BorderRadiusStyle.circleBorder17, + ), + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + children: [ + if (mediaData != null) + CustomScrollView( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + slivers: [ + SliverList.separated( + itemBuilder: (BuildContext context, int index) { + if (!mediaData![index].toString().contains('.png') && + !mediaData![index].toString().contains('.jpg') && + !mediaData![index].toString().contains('.jpeg')) { + return ListTile( + contentPadding: EdgeInsets.zero, + horizontalTitleGap: 8, + onTap: () async { + if (!await launchUrl(Uri.parse(dotenv.env['STORAGE_URL']! + mediaData![index].toString()))) { + throw Exception('Could not launch ${mediaData![index].toString()}'); + } + }, + leading: Icon( + Icons.file_copy_outlined, + color: Color(0xFF2F80ED), + ), + trailing: Icon( + Icons.download_outlined, + color: Color(0xFF2F80ED), + ), + title: Text( + mediaData![index].toString().replaceAll('chat_files/', ''), + maxLines: 2, + style: TextStyle( + color: Color(0xFF323F4B), + fontSize: 12, + overflow: TextOverflow.ellipsis, + ), + ), + ); + } + return GestureDetector( + onTap: () { + GoRouter.of(context).pushNamed( + 'photoViewScreen', + extra: mediaData!.map((e) => dotenv.env['STORAGE_URL']! + e.toString()).toList(), + pathParameters: { + 'index': index.toString(), + }, + ); + }, + child: ClipRRect( + borderRadius: BorderRadiusStyle.roundedBorder8, + child: CachedNetworkImage( + imageUrl: dotenv.env['STORAGE_URL']! + '${mediaData![index]}', + ), + ), + ); + }, + separatorBuilder: (BuildContext context, int index) => const SizedBox(height: 8), + itemCount: mediaData!.length, + ), + ], + ), + if (mediaData!.length > 0 && text.isNotEmpty) SizedBox(height: 8), + if (text.isNotEmpty) + Text( + text, + style: TextStyle( + color: Color(0xFF323F4B), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/chat/repositories/chat_repository.dart b/lib/features/chat/repositories/chat_repository.dart new file mode 100644 index 00000000..137aa878 --- /dev/null +++ b/lib/features/chat/repositories/chat_repository.dart @@ -0,0 +1,31 @@ +import 'package:eatvio_app/features/chat/models/chat_room_model.dart'; +import 'package:eatvio_app/features/chat/models/websocket_info_model.dart'; +import 'package:eatvio_app/features/chat/services/chat_service.dart'; + +class ChatRepository { + final ChatService chatService; + + ChatRepository({ + required this.chatService, + }); + + /// TODO use "dartz" package because need Either for error handling + Future getChatRoom(String chatId) async { + try { + return (await chatService.getChatRoomModel(chatId))['chatRoom']!; + } catch (e) { + print(e); + throw (e); + } + } + + /// TODO use "dartz" package because need Either for error handling + Future getWebsocket() async { + try { + return (await chatService.getWebsocket()); + } catch (e) { + print(e); + throw (e); + } + } +} diff --git a/lib/features/chat/services/chat_service.dart b/lib/features/chat/services/chat_service.dart new file mode 100644 index 00000000..5b27ccf2 --- /dev/null +++ b/lib/features/chat/services/chat_service.dart @@ -0,0 +1,17 @@ +import 'package:eatvio_app/features/chat/models/chat_room_model.dart'; +import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/chat/models/websocket_info_model.dart'; +import 'package:retrofit/retrofit.dart'; + +part 'chat_service.g.dart'; + +@RestApi() +abstract class ChatService { + factory ChatService(Dio dio, {String baseUrl}) = _ChatService; + + @GET('/chat/show/{id}') + Future> getChatRoomModel(@Path() String id); + + @GET('/profile/ws/params') + Future getWebsocket(); +} diff --git a/lib/features/chat/services/chat_service.g.dart b/lib/features/chat/services/chat_service.g.dart new file mode 100644 index 00000000..db9fdea8 --- /dev/null +++ b/lib/features/chat/services/chat_service.g.dart @@ -0,0 +1,105 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'chat_service.dart'; + +// ************************************************************************** +// RetrofitGenerator +// ************************************************************************** + +// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers + +class _ChatService implements ChatService { + _ChatService( + this._dio, { + this.baseUrl, + }); + + final Dio _dio; + + String? baseUrl; + + @override + Future> getChatRoomModel(String id) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _result = await _dio.fetch>( + _setStreamType>(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/chat/show/${id}', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + var value = _result.data!.map((k, dynamic v) => + MapEntry(k, ChatRoomModel.fromJson(v as Map))); + return value; + } + + @override + Future getWebsocket() async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _result = await _dio + .fetch>(_setStreamType(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/profile/ws/params', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + final value = WebsocketInfoModel.fromJson(_result.data!); + return value; + } + + RequestOptions _setStreamType(RequestOptions requestOptions) { + if (T != dynamic && + !(requestOptions.responseType == ResponseType.bytes || + requestOptions.responseType == ResponseType.stream)) { + if (T == String) { + requestOptions.responseType = ResponseType.plain; + } else { + requestOptions.responseType = ResponseType.json; + } + } + return requestOptions; + } + + String _combineBaseUrls( + String dioBaseUrl, + String? baseUrl, + ) { + if (baseUrl == null || baseUrl.trim().isEmpty) { + return dioBaseUrl; + } + + final url = Uri.parse(baseUrl); + + if (url.isAbsolute) { + return url.toString(); + } + + return Uri.parse(dioBaseUrl).resolveUri(url).toString(); + } +} diff --git a/lib/locator_service.dart b/lib/locator_service.dart new file mode 100644 index 00000000..68233789 --- /dev/null +++ b/lib/locator_service.dart @@ -0,0 +1,28 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart'; +import 'package:eatvio_app/features/chat/repositories/chat_repository.dart'; +import 'package:eatvio_app/features/chat/services/chat_service.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:get_it/get_it.dart'; + +final sl = GetIt.instance; + +Future init() async { + // BLoC + sl.registerFactory(() => ChatRoomCubit(chatRepository: sl())); + + // Repository + sl.registerLazySingleton(() => ChatRepository(chatService: sl())); + + // Services + sl.registerLazySingleton(() => ChatService(sl())); + + // External + sl.registerLazySingleton( + () => Dio( + BaseOptions( + baseUrl: dotenv.env['SERVER_ROOT']!, + ), + ), + ); +} diff --git a/lib/main.dart b/lib/main.dart index 50fb8293..8e0c1b94 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,6 @@ import 'package:eatvio_app/app_router.dart'; -import 'package:eatvio_app/presentation/chat/chat_select_screen/bloc/chat_select_bloc.dart'; -import 'package:eatvio_app/presentation/chat/chat_select_screen/models/chat_select_model.dart'; +import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_bloc.dart'; +import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/models/chat_select_model.dart'; import 'package:eatvio_app/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart'; import 'package:eatvio_app/presentation/diary/diary_edit_screen/models/diary_edit_model.dart'; import 'package:eatvio_app/presentation/feed/feed_screen/bloc/feed_bloc.dart'; @@ -15,11 +15,13 @@ import 'package:hive_flutter/hive_flutter.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:keyboard_dismisser/keyboard_dismisser.dart'; +import 'package:eatvio_app/locator_service.dart' as di; var globalMessengerKey = GlobalKey(); void main() async { WidgetsFlutterBinding.ensureInitialized(); + await di.init(); final appDocumentDir = await getApplicationDocumentsDirectory(); final path = appDocumentDir.path; await dotenv.load(fileName: ".env"); @@ -72,28 +74,29 @@ class MyApp extends StatelessWidget { child: BlocBuilder( builder: (context, state) { return KeyboardDismisser( - gestures: [ - GestureType.onTap, - GestureType.onPanUpdateDownDirection, + gestures: [ + GestureType.onTap, + GestureType.onPanUpdateDownDirection, + ], + child: MaterialApp.router( + theme: theme, + title: 'Eatvio', + routerConfig: AppRouter.appRouter, + debugShowCheckedModeBanner: false, + localizationsDelegates: [ + AppLocalizationDelegate(), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, ], - child: MaterialApp.router( - theme: theme, - title: 'Eatvio', - routerConfig: AppRouter.appRouter, - debugShowCheckedModeBanner: false, - localizationsDelegates: [ - AppLocalizationDelegate(), - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [ - Locale( - 'ru', - '', - ), - ], - )); + supportedLocales: [ + Locale( + 'ru', + '', + ), + ], + ), + ); }, ), ); diff --git a/lib/presentation/auth/auth_screen/auth_screen.dart b/lib/presentation/auth/auth_screen/auth_screen.dart index ea5cfed2..9694a2c9 100644 --- a/lib/presentation/auth/auth_screen/auth_screen.dart +++ b/lib/presentation/auth/auth_screen/auth_screen.dart @@ -1,3 +1,4 @@ +import 'package:eatvio_app/core/token_storage.dart'; import 'package:go_router/go_router.dart'; import 'bloc/auth_bloc.dart'; @@ -106,6 +107,7 @@ class AuthScreen extends StatelessWidget { final box = await Hive.openBox('token'); final token = res['token']; box.put('token', token); + TokenStorage.saveTokenInStorage(token); GoRouter.of(context).go('/my-profile-screen'); } else { showDialog( diff --git a/lib/presentation/chat/chat_screen/bloc/chat_bloc.dart b/lib/presentation/chat/chat_screen/bloc/chat_bloc.dart deleted file mode 100644 index caaa4c1b..00000000 --- a/lib/presentation/chat/chat_screen/bloc/chat_bloc.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:eatvio_app/app_router.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:hive/hive.dart'; -import 'package:http/http.dart' as http; -import 'package:go_router/go_router.dart'; -import 'dart:convert'; -import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/chat/chat_screen/models/chat_model.dart'; -import 'package:eatvio_app/presentation/chat/chat_screen/models/chat_message_model.dart'; - -part 'chat_event.dart'; - -part 'chat_state.dart'; - -/// A bloc that manages the state of a Chat according to the event that is dispatched to it. -class ChatBloc extends Bloc { - ChatBloc(ChatState initialState) : super(initialState) { - on(_onInitialize); - } - - _onInitialize( - ChatInitialEvent event, - Emitter emit, - ) async { - try { - final box = await Hive.openBox('token'); - final token = box.get('token'); - if (token == null) { - AppRouter.navigatorKey.currentContext?.goNamed('authScreen'); - } - final String? chatID = state.chatModelObj?.chatID; - final response = await http.get( - Uri.parse(dotenv.env['SERVER_ROOT']! + '/chat/show/$chatID'), - headers: { - 'Authorization': 'Bearer $token', - }, - ); - if (response.statusCode == 200) { - final data = jsonDecode(response.body); - if (data is Map && data.containsKey('chatRoom') && data['chatRoom'].containsKey('messages')) { - if (state is ChatLoadedState) { - var currentState = state as ChatLoadedState; - emit(currentState.copyWith(chatModelObj: currentState.chatModelObj, messages: currentState.messages)); - } else { - emit(ChatLoadedState(messages: fillMessages(data['chatRoom']['messages']))); - } - } else { - // Один или несколько ключевых полей отсутствуют, обрабатываем ошибку - emit(ChatErrorState('Некоторые необходимые данные отсутствуют.')); - } - } else { - // Статус ответа не равен 200, обрабатываем ошибку - emit(ChatErrorState('Ошибка сервера: ${response.statusCode}')); - } - } catch (e) { - // Ошибка при декодировании JSON или другая ошибка выполнения - emit(ChatErrorState('Ошибка при получении данных о чатах: $e')); - } - } - - List fillMessages(List messagesJSON) { - List messages = []; - for (int i = 0; i < messagesJSON.length; i++) { - messages.add(ChatMessageModel( - id: messagesJSON[i]['id'], - data: messagesJSON[i]['data'], - mediaData: messagesJSON[i]['mediaData'] ?? [], - updatedAt: messagesJSON[i]['updated_at'], - authorId: messagesJSON[i]['author_id'])); - } - - return messages; - } -} diff --git a/lib/presentation/chat/chat_screen/bloc/chat_event.dart b/lib/presentation/chat/chat_screen/bloc/chat_event.dart deleted file mode 100644 index 1fe6df94..00000000 --- a/lib/presentation/chat/chat_screen/bloc/chat_event.dart +++ /dev/null @@ -1,16 +0,0 @@ -// ignore_for_file: must_be_immutable - -part of 'chat_bloc.dart'; - -/// Abstract class for all events that can be dispatched from the -///Chat widget. -/// -/// Events must be immutable and implement the [Equatable] interface. -@immutable -abstract class ChatEvent extends Equatable {} - -/// Event that is dispatched when the Chat widget is first created. -class ChatInitialEvent extends ChatEvent { - @override - List get props => []; -} diff --git a/lib/presentation/chat/chat_screen/bloc/chat_state.dart b/lib/presentation/chat/chat_screen/bloc/chat_state.dart deleted file mode 100644 index 6f5718d1..00000000 --- a/lib/presentation/chat/chat_screen/bloc/chat_state.dart +++ /dev/null @@ -1,53 +0,0 @@ -// ignore_for_file: must_be_immutable - -part of 'chat_bloc.dart'; - -/// Represents the state of Chat in the application. -/// - -class ChatState extends Equatable { - ChatState({ - this.chatModelObj, - }); - - ChatModel? chatModelObj; - - @override - List get props => [chatModelObj]; - ChatState copyWith({ChatModel? chatModelObj}) { - return ChatState( - chatModelObj: chatModelObj ?? this.chatModelObj, - ); - } -} - -class ChatLoadedState extends ChatState { - ChatLoadedState({this.chatModelObj, this.messages}); - - ChatModel? chatModelObj; - List? messages; - - @override - List get props => [chatModelObj, messages]; - ChatLoadedState copyWith( - {ChatModel? chatModelObj, List? messages}) { - return ChatLoadedState( - chatModelObj: chatModelObj ?? this.chatModelObj, - messages: messages ?? this.messages, - ); - } -} - -class ChatErrorState extends ChatState { - final String errorMessage; - - ChatErrorState(this.errorMessage); - - @override - List get props => [errorMessage]; -} - -class ChatInitialState extends ChatState { - ChatInitialState({required ChatModel chatModelObj}) - : super(chatModelObj: chatModelObj); -} diff --git a/lib/presentation/chat/chat_screen/chat_screen.dart b/lib/presentation/chat/chat_screen/chat_screen.dart deleted file mode 100644 index 8e34fc40..00000000 --- a/lib/presentation/chat/chat_screen/chat_screen.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:go_router/go_router.dart'; - -import 'bloc/chat_bloc.dart'; -import 'models/chat_model.dart'; -import 'package:eatvio_app/core/app_export.dart'; -import 'package:flutter/material.dart'; -import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart'; -import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart'; -import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart'; - -class ChatScreen extends StatelessWidget { - const ChatScreen({Key? key}) : super(key: key); - - static Widget builder(BuildContext context, chatID) { - return BlocProvider( - create: (context) => ChatBloc( - ChatState(chatModelObj: ChatModel(chatID: chatID.toString()))) - ..add(ChatInitialEvent()), - child: ChatScreen()); - } - - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is ChatInitialState) { - return Scaffold( - body: Center(child: CircularProgressIndicator(),), - ); - } else if (state is ChatLoadedState) { - // Данные загружены, можно строить экран. - return _buildChat(context, state); - } else if (state is ChatErrorState) { - // Произошла ошибка при загрузке данных. - return Center(child: Text(state.errorMessage)); - } else { - return Scaffold( - body: Center(child: CircularProgressIndicator(),), - ); - } - }, - ); - } - - _buildChat(BuildContext context, ChatLoadedState state) { - mediaQueryData = MediaQuery.of(context); - return Scaffold( - resizeToAvoidBottomInset: false, - appBar: _buildAppbar(context), - body: SizedBox( - width: double.maxFinite, - child: Container( - width: double.maxFinite, - decoration: AppDecoration.fillGray, - child: ListView( - children: [ - SizedBox(height: 30.v), - ], - ), - ), - ), - ); - } - - /// Section Widget - _buildAppbar(BuildContext context) { - return CustomAppBar( - leadingWidth: 26.h, - leading: AppbarLeadingImage( - imagePath: ImageConstant.imgArrowLeft, - margin: EdgeInsets.only( - left: 16.h, - top: 18.v, - bottom: 25.v, - ), - onTap: () => onTapArrowleftone(context), - ), - title: Padding( - padding: EdgeInsets.only(left: 12.h), - child: Row( - children: [ - Padding( - padding: EdgeInsets.only( - left: 12.h, - top: 2.v, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AppbarSubtitle( - text: "Валерий Леонтьев", - ), - SizedBox(height: 3.v), - Text("Был в сети вчера", style: theme.textTheme.bodySmall!) - ], - ), - ) - ], - ), - ), - ); - } - - /// Navigates to the previous screen. - onTapArrowleftone(BuildContext context) { - GoRouter.of(context).pop(); - } -} diff --git a/lib/presentation/chat/chat_screen/models/chat_message_model.dart b/lib/presentation/chat/chat_screen/models/chat_message_model.dart deleted file mode 100644 index e91596f3..00000000 --- a/lib/presentation/chat/chat_screen/models/chat_message_model.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:equatable/equatable.dart'; - -class ChatMessageModel extends Equatable { - final int id; - final String data; - final List mediaData; - final int authorId; - final String updatedAt; - - ChatMessageModel({ - required this.id, - required this.data, - required this.mediaData, - required this.authorId, - required this.updatedAt, - }); - - ChatMessageModel copyWith({ - int? id, - String? data, - List? mediaData, - int? authorId, - String? updatedAt, - }) { - return ChatMessageModel( - id: id ?? this.id, - data: data ?? this.data, - mediaData: mediaData ?? this.mediaData, - authorId: authorId ?? this.authorId, - updatedAt: updatedAt ?? this.updatedAt, - ); - } - - @override - List get props => [id, data, mediaData, authorId, updatedAt]; -} diff --git a/lib/presentation/chat/chat_screen/models/chat_model.dart b/lib/presentation/chat/chat_screen/models/chat_model.dart deleted file mode 100644 index 970b9ada..00000000 --- a/lib/presentation/chat/chat_screen/models/chat_model.dart +++ /dev/null @@ -1,19 +0,0 @@ -// ignore_for_file: must_be_immutable - -import 'package:equatable/equatable.dart'; - -/// This class defines the variables used in the [chat_screen], -/// and is typically used to hold data that is passed between different parts of the application. - -class ChatModel extends Equatable { - final String chatID; - - ChatModel({required String chatID}) : chatID = chatID; - - ChatModel copyWith(String chatID) { - return ChatModel(chatID: chatID); - } - - @override - List get props => [chatID]; -} diff --git a/lib/presentation/chat/chat_select_screen/chat_select_screen.dart b/lib/presentation/chat/chat_select_screen/chat_select_screen.dart deleted file mode 100644 index ba6c8869..00000000 --- a/lib/presentation/chat/chat_select_screen/chat_select_screen.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'bloc/chat_select_bloc.dart'; -import 'models/chat_select_model.dart'; -import 'package:eatvio_app/core/app_export.dart'; -import '../chat_select_screen/chat_search_view.dart'; -import 'package:flutter/material.dart'; -import 'package:eatvio_app/presentation/chat/chat_select_screen/models/chat_entry.dart'; // ignore: must_be_immutable -import 'package:eatvio_app/presentation/chat/chat_select_screen/widgets/chat_entry_widget.dart'; // ignore: must_be_immutable - -class ChatSelectScreen extends StatelessWidget { - const ChatSelectScreen({Key? key}) : super(key: key); - - static Widget builder(BuildContext context) { - return BlocProvider( - create: (context) => ChatSelectBloc(ChatSelectState(chatSelectModelObj: ChatSelectModel())) - ..add( - ChatSelectInitialEvent(), - ), - child: ChatSelectScreen(), - ); - } - - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is ChatSelectInitialState) { - // Это начальное состояние, данные ещё не загружены. - return Container( - color: Colors.white, - ); - } else if (state is ChatSelectLoadedState) { - // Данные загружены, можно строить экран. - return _buildScreen(context, state); - } else if (state is ChatSelectErrorState) { - // Произошла ошибка при загрузке данных. - return Center(child: Text(state.errorMessage)); - } else { - // Другие состояния, если они есть. - return Container(); // Пустой контейнер для необработанных состояний. - } - }, - ); - } - - _buildScreen(BuildContext context, ChatSelectLoadedState state) { - return Scaffold( - resizeToAvoidBottomInset: false, - body: Container( - width: double.maxFinite, - decoration: AppDecoration.fillGray, - child: ListView( - children: [ - SizedBox(height: 30.v), - Container( - margin: EdgeInsets.only( - left: 16.h, - right: 20.h, - bottom: 10.h, - ), - padding: EdgeInsets.all(12.h), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(12)), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), - ), - ], - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - CustomSearchView( - controller: state.searchController, - hintText: "Поиск", - ), - SizedBox(height: 20.v), - _buildChatList(context, state), - ], - ), - ) - ], - ), - ), - ); - } -} - -_buildChatList(BuildContext context, ChatSelectLoadedState state) { - return ListView.separated( - shrinkWrap: true, - separatorBuilder: (context, index) { - return SizedBox( - height: 10.v, - ); - }, - itemCount: state.chatRooms?.length ?? 0, - itemBuilder: (context, index) { - ChatItemModel model = state.chatRooms?[index]; - return ChatEntryWidget( - model, - ); - }, - ); -} diff --git a/lib/presentation/chat/chat_select_screen/widgets/chat_entry_widget.dart b/lib/presentation/chat/chat_select_screen/widgets/chat_entry_widget.dart deleted file mode 100644 index 688999b9..00000000 --- a/lib/presentation/chat/chat_select_screen/widgets/chat_entry_widget.dart +++ /dev/null @@ -1,108 +0,0 @@ -// ignore_for_file: must_be_immutable - -import 'package:flutter/material.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:eatvio_app/core/app_export.dart'; -import 'package:eatvio_app/presentation/chat/chat_select_screen/models/chat_entry.dart'; -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:go_router/go_router.dart'; - -class ChatEntryWidget extends StatelessWidget { - ChatEntryWidget(this.chatEntryObj, {Key? key}) - : super( - key: key, - ); - - ChatItemModel chatEntryObj; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => GoRouter.of(context).goNamed( - 'chatScreen', - extra: chatEntryObj.id.toString(), - ), - child: Container( - padding: EdgeInsets.all(8.h), - decoration: AppDecoration.fillGray100.copyWith( - borderRadius: BorderRadiusStyle.circleBorder8, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CachedNetworkImage( - imageUrl: dotenv.env['STORAGE_URL']! + chatEntryObj.userImage!, - imageBuilder: (context, imageProvider) => CircleAvatar( - backgroundImage: imageProvider, - radius: 25, - ), - placeholder: (context, url) => CircularProgressIndicator(), - errorWidget: (context, url, error) => CircleAvatar( - child: Icon(Icons.person), - backgroundColor: Colors.white, - radius: 25, - ), - ), - /*CustomImageView( - imagePath: - dotenv.env['STORAGE_URL']! + chatEntryObj.userImage!, - height: 60.adaptSize, - width: 60.adaptSize, - radius: BorderRadius.circular( - 30.h, - ), - ),*/ - Expanded( - child: Padding( - padding: EdgeInsets.only( - left: 12.h, - top: 2.v, - bottom: 11.v, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: 227.h, - child: Row( - children: [ - Text( - chatEntryObj.username!.length <= 15 ? chatEntryObj.username! : chatEntryObj.username!.substring(0, 15) + '...', - style: theme.textTheme.titleMedium, - ), - Spacer(), - CustomImageView( - imagePath: ImageConstant.imageNotFound, - height: 9.v, - width: 16.h, - margin: EdgeInsets.only( - top: 4.v, - bottom: 6.v, - ), - ), - Padding( - padding: EdgeInsets.only(left: 6.h), - child: Text( - chatEntryObj.dateText!, - style: theme.textTheme.bodyMedium, - ), - ) - ], - ), - ), - SizedBox(height: 6.v), - Text( - chatEntryObj.greetingText!, - style: CustomTextStyles.bodyMediumBluegray800, - ) - ], - ), - ), - ) - ], - ), - ), - ); - } -} diff --git a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart index 4644a7fb..e2be9487 100644 --- a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart +++ b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart @@ -1,6 +1,5 @@ // ignore_for_file: unused_local_variable - import 'bloc/diary_edit_bloc.dart'; import 'models/diary_edit_model.dart'; import 'package:flutter/material.dart'; @@ -90,7 +89,7 @@ class DiaryEditScreen extends StatelessWidget { ], ), ), - bottomNavigationBar: _buildBottomBar(context) ), + ), ), _buildEndDay(context), ], diff --git a/lib/presentation/diets/diet_select_screen/diet_select_screen.dart b/lib/presentation/diets/diet_select_screen/diet_select_screen.dart index 01d69fb0..68834d09 100644 --- a/lib/presentation/diets/diet_select_screen/diet_select_screen.dart +++ b/lib/presentation/diets/diet_select_screen/diet_select_screen.dart @@ -108,7 +108,6 @@ class DietSelectScreen extends StatelessWidget { ), ), ), - bottomNavigationBar: _buildBottomBar(context), ); } diff --git a/lib/presentation/plans/plan_select_screen/plan_select_screen.dart b/lib/presentation/plans/plan_select_screen/plan_select_screen.dart index b876f224..810bd3ae 100644 --- a/lib/presentation/plans/plan_select_screen/plan_select_screen.dart +++ b/lib/presentation/plans/plan_select_screen/plan_select_screen.dart @@ -108,7 +108,6 @@ class PlanSelectScreen extends StatelessWidget { ), ), ), - bottomNavigationBar: _buildBottomBar(context), ); } diff --git a/lib/presentation/profile/my_profile_screen/my_settings_screen.dart b/lib/presentation/profile/my_profile_screen/my_settings_screen.dart index 6f63f7e3..a5be7c00 100644 --- a/lib/presentation/profile/my_profile_screen/my_settings_screen.dart +++ b/lib/presentation/profile/my_profile_screen/my_settings_screen.dart @@ -170,7 +170,6 @@ class MySettingsScreen extends StatelessWidget { ), ), ), - bottomNavigationBar: _buildBottomBar(context), ); } diff --git a/lib/presentation/recipe/recipe_edit_screen/recipe_edit_screen.dart b/lib/presentation/recipe/recipe_edit_screen/recipe_edit_screen.dart index 95aee171..c9ce2669 100644 --- a/lib/presentation/recipe/recipe_edit_screen/recipe_edit_screen.dart +++ b/lib/presentation/recipe/recipe_edit_screen/recipe_edit_screen.dart @@ -14,12 +14,11 @@ import 'package:eatvio_app/widgets/custom_outlined_button.dart'; class RecipeEditScreen extends StatelessWidget { const RecipeEditScreen({Key? key}) : super(key: key); + static Widget builder(BuildContext context) { final recipeID = ModalRoute.of(context)!.settings.arguments; return BlocProvider( - create: (context) => RecipeEditBloc(RecipeEditState( - recipeModelObj: RecipeEditModel(recipeID: recipeID.toString()))) - ..add(RecipeEditInitialEvent()), + create: (context) => RecipeEditBloc(RecipeEditState(recipeModelObj: RecipeEditModel(recipeID: recipeID.toString())))..add(RecipeEditInitialEvent()), child: RecipeEditScreen()); } @@ -55,18 +54,19 @@ class RecipeEditScreen extends StatelessWidget { } return RefreshIndicator( - onRefresh: refreshPage(context), - child: Scaffold( - appBar: AppBar( - toolbarHeight: 0, - automaticallyImplyLeading: false, - ), - resizeToAvoidBottomInset: false, - body: SafeArea( - child: Container( - width: double.maxFinite, - decoration: BoxDecoration(color: Colors.grey[200]), - child: ListView(children: [ + onRefresh: refreshPage(context), + child: Scaffold( + appBar: AppBar( + toolbarHeight: 0, + automaticallyImplyLeading: false, + ), + resizeToAvoidBottomInset: false, + body: SafeArea( + child: Container( + width: double.maxFinite, + decoration: BoxDecoration(color: Colors.grey[200]), + child: ListView( + children: [ _buldAppBar(context), SizedBox(height: 20.v), _buildRecipeInfo(state, context), @@ -77,8 +77,12 @@ class RecipeEditScreen extends StatelessWidget { ), SizedBox(height: 20.v), _buildAdditional(context, state) - ]), - ))); + ], + ), + ), + ), + ), + ); } _buldAppBar(BuildContext context) { @@ -139,8 +143,8 @@ class RecipeEditScreen extends StatelessWidget { if (state.recipe?['image'] != null) CachedNetworkImage( imageUrl: dotenv.env['STORAGE_URL']! + (state.recipe?['image']), - width: double - .infinity, // Установите ширину равной ширине родительского элемента + width: double.infinity, + // Установите ширину равной ширине родительского элемента height: 200.0, fit: BoxFit.cover, placeholder: (context, url) => CircularProgressIndicator(), @@ -294,9 +298,10 @@ class RecipeEditScreen extends StatelessWidget { if (state.steps?[i]['image'] != null) CachedNetworkImage( imageUrl: dotenv.env['STORAGE_URL']! + (state.steps?[i]['image']), - width: double - .infinity, // Установите ширину равной ширине родительского элемента - height: 200.0, // Задайте постоянную высоту элемента + width: double.infinity, + // Установите ширину равной ширине родительского элемента + height: 200.0, + // Задайте постоянную высоту элемента fit: BoxFit.cover, placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), @@ -310,8 +315,7 @@ class RecipeEditScreen extends StatelessWidget { text: "Добавить шаг", buttonStyle: CustomButtonStyles.outlinePrimaryTL15, ), - if (isLastStep && - !isFirstStep) // Добавляем кнопку "Добавить шаг" только для последнего шага + if (isLastStep && !isFirstStep) // Добавляем кнопку "Добавить шаг" только для последнего шага Column( children: [ SizedBox(height: 16.0), @@ -320,8 +324,7 @@ class RecipeEditScreen extends StatelessWidget { Flexible( flex: 5, child: CustomOutlinedButton( - onPressed: () => - context.read().add(AddStep()), + onPressed: () => context.read().add(AddStep()), text: "Добавить шаг", buttonStyle: CustomButtonStyles.outlinePrimaryTL15, ), @@ -331,8 +334,7 @@ class RecipeEditScreen extends StatelessWidget { flex: 1, child: CustomOutlinedButton( text: "х", - onPressed: () => - context.read().add(RemoveStep(i)), + onPressed: () => context.read().add(RemoveStep(i)), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, ), ), @@ -374,9 +376,7 @@ class RecipeEditScreen extends StatelessWidget { Align(alignment: Alignment.centerLeft, child: Text("Теги")), _buildEditableTags(state.tags), SizedBox(height: 8.0), - Align( - alignment: Alignment.centerLeft, - child: Text("Ссылка на видео (необязательно)")), + Align(alignment: Alignment.centerLeft, child: Text("Ссылка на видео (необязательно)")), CustomTextFormField( controller: state.video_url, hintText: "YouTube", @@ -416,12 +416,10 @@ class RecipeEditScreen extends StatelessWidget { } void _trimTags(newValue) { - List newTagNames = - newValue!.split(',').map((tag) => tag.trim()).toList(); - List> newTags = - newTagNames.map((tagName) => {'name': tagName}).toList(); - print(newTags); - } + List newTagNames = newValue!.split(',').map((tag) => tag.trim()).toList(); + List> newTags = newTagNames.map((tagName) => {'name': tagName}).toList(); + print(newTags); + } Function onTapBack(BuildContext context) { return () { diff --git a/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart b/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart index 1881d3b6..2a3a108e 100644 --- a/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart +++ b/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart @@ -107,7 +107,6 @@ class RecipeSelectScreen extends StatelessWidget { ), ), ), - bottomNavigationBar: _buildBottomBar(context), ); } diff --git a/lib/theme/custom_text_style.dart b/lib/theme/custom_text_style.dart index ff969f92..de3bee78 100644 --- a/lib/theme/custom_text_style.dart +++ b/lib/theme/custom_text_style.dart @@ -8,155 +8,171 @@ import '../core/app_export.dart'; class CustomTextStyles { // Headline style static get headlineSmallMulish => theme.textTheme.headlineSmall!.mulish; + // Title text style - static get titleMediumSegoeUI => - theme.textTheme.titleMedium!.segoeUI.copyWith( + static get titleMediumSegoeUI => theme.textTheme.titleMedium!.segoeUI.copyWith( fontWeight: FontWeight.w600, ); + static get bodyMediumBlack900 => theme.textTheme.bodyMedium!.copyWith( color: appTheme.black900, ); + static get bodyMediumBlack90013 => theme.textTheme.bodyMedium!.copyWith( color: appTheme.black900, fontSize: 13.fSize, ); + static get bodyMediumBluegray800 => theme.textTheme.bodyMedium!.copyWith( color: appTheme.blueGray800, ); + static get bodyMediumBluegray800_1 => theme.textTheme.bodyMedium!.copyWith( color: appTheme.blueGray800, ); + static get bodyMediumBluegray800_2 => theme.textTheme.bodyMedium!.copyWith( color: appTheme.blueGray800, ); + static get bodyMediumWhiteA700 => theme.textTheme.bodyMedium!.copyWith( color: appTheme.whiteA700, ); - static get bodySmallMulishBlack900 => - theme.textTheme.bodySmall!.mulish.copyWith( + + static get bodySmallMulishBlack900 => theme.textTheme.bodySmall!.mulish.copyWith( color: appTheme.black900, fontSize: 12.fSize, ); - static get bodySmallMulishBluegray800 => - theme.textTheme.bodySmall!.mulish.copyWith( + + static get bodySmallMulishBluegray800 => theme.textTheme.bodySmall!.mulish.copyWith( color: appTheme.blueGray800, fontSize: 12.fSize, ); - static get bodySmallMulishBluegray80012 => - theme.textTheme.bodySmall!.mulish.copyWith( + + static get bodySmallMulishBluegray80012 => theme.textTheme.bodySmall!.mulish.copyWith( color: appTheme.blueGray800, fontSize: 12.fSize, ); - static get bodySmallMulishBluegray80012_1 => - theme.textTheme.bodySmall!.mulish.copyWith( + + static get bodySmallMulishBluegray80012_1 => theme.textTheme.bodySmall!.mulish.copyWith( color: appTheme.blueGray800, fontSize: 12.fSize, ); - static get bodySmallMulishBluegray900 => - theme.textTheme.bodySmall!.mulish.copyWith( + + static get bodySmallMulishBluegray900 => theme.textTheme.bodySmall!.mulish.copyWith( color: appTheme.blueGray900, fontSize: 12.fSize, ); - static get bodySmallMulishGray600 => - theme.textTheme.bodySmall!.mulish.copyWith( + + static get bodySmallMulishGray600 => theme.textTheme.bodySmall!.mulish.copyWith( color: appTheme.gray600, ); - static get bodySmallMulishPrimary => - theme.textTheme.bodySmall!.mulish.copyWith( + + static get bodySmallMulishPrimary => theme.textTheme.bodySmall!.mulish.copyWith( color: theme.colorScheme.primary, fontSize: 12.fSize, ); - static get bodySmallSegoeUIBlack900 => - theme.textTheme.bodySmall!.segoeUI.copyWith( + + static get bodySmallSegoeUIBlack900 => theme.textTheme.bodySmall!.segoeUI.copyWith( color: appTheme.black900, fontSize: 12.fSize, ); + // Label text style static get labelLargeGray60003 => theme.textTheme.labelLarge!.copyWith( color: appTheme.gray60003, ); + static get labelLargeWhiteA700 => theme.textTheme.labelLarge!.copyWith( color: appTheme.whiteA700, ); + // Title text style static get titleMedium16 => theme.textTheme.titleMedium!.copyWith( fontSize: 16.fSize, ); + static get titleMediumBlack => theme.textTheme.titleMedium!.copyWith( fontSize: 17.fSize, fontWeight: FontWeight.w900, ); + static get titleMediumBlack900 => theme.textTheme.titleMedium!.copyWith( color: appTheme.black900, ); + static get titleMediumSemiBold => theme.textTheme.titleMedium!.copyWith( fontWeight: FontWeight.w600, ); + static get titleMediumWhiteA700 => theme.textTheme.titleMedium!.copyWith( color: appTheme.whiteA700, fontWeight: FontWeight.w600, ); + static get titleSmallBlue800 => theme.textTheme.titleSmall!.copyWith( color: appTheme.blue800, fontWeight: FontWeight.w800, ); + static get titleSmallBluegray800 => theme.textTheme.titleSmall!.copyWith( color: appTheme.blueGray800, ); + static get titleSmallBluegray800Bold => theme.textTheme.titleSmall!.copyWith( color: appTheme.blueGray800, fontWeight: FontWeight.w700, ); - static get titleSmallBluegray800ExtraBold => - theme.textTheme.titleSmall!.copyWith( + + static get titleSmallBluegray800ExtraBold => theme.textTheme.titleSmall!.copyWith( color: appTheme.blueGray800, fontWeight: FontWeight.w800, ); + static get titleSmallBold => theme.textTheme.titleSmall!.copyWith( fontSize: 15.fSize, fontWeight: FontWeight.w700, ); + static get titleSmallGreenA70001 => theme.textTheme.titleSmall!.copyWith( color: appTheme.greenA70001, ); - static get titleSmallGreenA70001ExtraBold => - theme.textTheme.titleSmall!.copyWith( + + static get titleSmallGreenA70001ExtraBold => theme.textTheme.titleSmall!.copyWith( color: appTheme.greenA70001, fontWeight: FontWeight.w800, ); + static get titleSmallLightgreen300 => theme.textTheme.titleSmall!.copyWith( color: appTheme.lightGreen300, fontWeight: FontWeight.w800, ); + static get titleSmallRed200 => theme.textTheme.titleSmall!.copyWith( color: appTheme.red200, fontWeight: FontWeight.w800, ); - static get titleSmallSecondaryContainer => - theme.textTheme.titleSmall!.copyWith( + + static get titleSmallSecondaryContainer => theme.textTheme.titleSmall!.copyWith( color: theme.colorScheme.secondaryContainer, fontWeight: FontWeight.w800, ); - static get titleSmallSecondaryContainer_1 => - theme.textTheme.titleSmall!.copyWith( + + static get titleSmallSecondaryContainer_1 => theme.textTheme.titleSmall!.copyWith( color: theme.colorScheme.secondaryContainer, ); - static get titleSmallSegoeUIOnPrimary => - theme.textTheme.titleSmall!.segoeUI.copyWith( + + static get titleSmallSegoeUIOnPrimary => theme.textTheme.titleSmall!.segoeUI.copyWith( color: theme.colorScheme.onPrimary, ); } extension on TextStyle { TextStyle get mulish { - return copyWith( - fontFamily: 'Mulish', - ); + return copyWith(); } TextStyle get segoeUI { - return copyWith( - fontFamily: 'Segoe UI', - ); + return copyWith(); } } diff --git a/lib/theme/theme_helper.dart b/lib/theme/theme_helper.dart index 68aa928a..7226e373 100644 --- a/lib/theme/theme_helper.dart +++ b/lib/theme/theme_helper.dart @@ -195,6 +195,8 @@ class PrimaryColors { // White Color get whiteA700 => Color(0XFFFFFFFF); + + Color get backgroundAvatar => Color(0xFFEBF4FF); } PrimaryColors get appTheme => ThemeHelper().themeColor(); diff --git a/pubspec.yaml b/pubspec.yaml index 35f447c7..6000794d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,17 +1,21 @@ name: eatvio_app description: A new Flutter project. version: 0.0.0+1 + environment: sdk: '>=3.3.4 <4.0.0' -dependencies: - flutter: + +dependencies: + flutter: sdk: flutter - flutter_localizations: + + flutter_localizations: sdk: flutter + flutter_bloc: ^8.0.0 connectivity_plus: ^5.0.2 shared_preferences: ^2.0.15 - cached_network_image: ^3.2.1 + cached_network_image: ^3.3.1 flutter_svg: ^2.0.9 equatable: ^2.0.0 validators: ^3.0.0 @@ -31,27 +35,44 @@ dependencies: go_router: ^14.1.4 keyboard_dismisser: ^3.0.0 -dev_dependencies: + freezed_annotation: ^2.4.1 + json_annotation: ^4.9.0 + retrofit: ^4.1.0 + get_it: ^7.7.0 + dio: ^5.4.3+1 + + # photo viewer + photo_view: ^0.15.0 + #url launcher + url_launcher: ^6.3.0 + +dev_dependencies: dart_code_metrics: ^5.7.6 dart_code_metrics_presets: ^1.8.0 - flutter_test: + build_runner: ^2.4.11 + freezed: ^2.5.2 + json_serializable: ^6.8.0 + retrofit_generator: ^8.1.0 + + flutter_test: sdk: flutter -flutter: + +flutter: uses-material-design: true - assets: + assets: - assets/ - assets/images/ - .env - fonts: + fonts: - family: Segoe UI - fonts: + fonts: - asset: assets/fonts/SegoeUISemibold.ttf weight: 600 - family: Mulish - fonts: + fonts: - asset: assets/fonts/MulishBold.ttf weight: 700 - asset: assets/fonts/MulishRegular.ttf weight: 400 - asset: assets/fonts/MulishSemiBold.ttf - weight: 600 + weight: 600 \ No newline at end of file