From e04c5c97f95395ea4fa617c77d2ca6794d3c067a Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Tue, 8 Jul 2025 11:35:41 +0300 Subject: [PATCH] feat(chat): chat without delay --- lib/core/const.dart | 3 +- .../bloc/chat_room_cubit/chat_room_cubit.dart | 22 +- .../widgets/chat_bubble_widget.dart | 16 +- .../consultant_review_model.freezed.dart | 652 +++++++++++++++++- .../profile/models/profile_model.dart | 1 + .../services/profile_service_test.dart | 1 + 6 files changed, 685 insertions(+), 10 deletions(-) diff --git a/lib/core/const.dart b/lib/core/const.dart index 8f5ddb59..5d1aded6 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,4 +1,5 @@ -String baseUrl = 'https://mobile-stage.eatvio.ru'; +// String baseUrl = 'https://mobile-stage.eatvio.ru'; +String baseUrl = 'https://eatvio.ru'; const List durationValues = [7, 14, 21]; 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 index 40ee862d..32244e78 100644 --- 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 @@ -189,12 +189,30 @@ class ChatRoomCubit extends Cubit { required String userSlug, }) async { try { + final compressedFiles = files != null ? await ImageCompressUtility.compressFilesList(List.from(files)) : null; + AudioPlayer().play((AssetSource('audio/send_message.wav'))); + emit( + (state as ChatRoomStateLoaded).copyWith( + chatMessages: [ + ChatMessageModel( + id: 1, + data: data ?? '', + mediaData: compressedFiles, + authorId: 1, + updatedAt: DateTime.now(), + isRead: true, + isCurrentUserAuthor: true, + roomId: '', + ), + ...(state as ChatRoomStateLoaded).chatMessages, + ], + ), + ); await chatRepository.sendMessage( data: data, - files: files != null ? await ImageCompressUtility.compressFilesList(List.from(files)) : null, + files: compressedFiles, slug: userSlug, ); - AudioPlayer().play((AssetSource('audio/send_message.wav'))); return true; } catch (e, st) { log(e.toString(), stackTrace: st); diff --git a/lib/features/chat/presentation/widgets/chat_bubble_widget.dart b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart index 74e6f6a6..dd6c7b4c 100644 --- a/lib/features/chat/presentation/widgets/chat_bubble_widget.dart +++ b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:dismissible_page/dismissible_page.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; @@ -59,8 +61,9 @@ class ChatBubbleWidget extends StatelessWidget { ), ), child: Padding( - padding: - mediaData.isNotEmpty ? const EdgeInsets.all(8) : EdgeInsets.only(top: 8, bottom: 8, left: isCurrentUser ? 16 : 8, right: isCurrentUser ? 8 : 16), + padding: mediaData.isNotEmpty + ? const EdgeInsets.all(8) + : EdgeInsets.only(top: 8, bottom: 8, left: isCurrentUser ? 16 : 8, right: isCurrentUser ? 8 : 16), child: Column( crossAxisAlignment: isCurrentUser ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ @@ -109,6 +112,7 @@ class ChatBubbleWidget extends StatelessWidget { PhotoViewScreen( imageUrls: mediaData.map((e) => '$e').toList(), index: index, + isLocal: mediaData[index] is File ? true : false, ), ); }, @@ -117,9 +121,11 @@ class ChatBubbleWidget extends StatelessWidget { transitionOnUserGestures: true, child: ClipRRect( borderRadius: BorderRadius.circular(16), - child: CachedNetworkImage( - imageUrl: '${mediaData[index]}', - ), + child: mediaData[index] is File + ? Image.file(mediaData[index]) + : CachedNetworkImage( + imageUrl: '${mediaData[index]}', + ), ), ), ); diff --git a/lib/features/diary/models/consultant_review_model.freezed.dart b/lib/features/diary/models/consultant_review_model.freezed.dart index a6f79469..36c72066 100644 --- a/lib/features/diary/models/consultant_review_model.freezed.dart +++ b/lib/features/diary/models/consultant_review_model.freezed.dart @@ -1,6 +1,5 @@ -// dart format width=80 -// coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file // 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 @@ -121,6 +120,172 @@ class _$ConsultantReviewModelCopyWithImpl<$Res> } } +/// Adds pattern-matching-related methods to [ConsultantReviewModel]. +extension ConsultantReviewModelPatterns on ConsultantReviewModel { + /// A variant of `map` that fallback to returning `orElse`. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case _: + /// return orElse(); + /// } + /// ``` + + @optionalTypeArgs + TResult maybeMap( + TResult Function(_ConsultantReviewModel value)? $default, { + required TResult orElse(), + }) { + final _that = this; + switch (_that) { + case _ConsultantReviewModel() when $default != null: + return $default(_that); + case _: + return orElse(); + } + } + + /// A `switch`-like method, using callbacks. + /// + /// Callbacks receives the raw object, upcasted. + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case final Subclass2 value: + /// return ...; + /// } + /// ``` + + @optionalTypeArgs + TResult map( + TResult Function(_ConsultantReviewModel value) $default, + ) { + final _that = this; + switch (_that) { + case _ConsultantReviewModel(): + return $default(_that); + case _: + throw StateError('Unexpected subclass'); + } + } + + /// A variant of `map` that fallback to returning `null`. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case _: + /// return null; + /// } + /// ``` + + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_ConsultantReviewModel value)? $default, + ) { + final _that = this; + switch (_that) { + case _ConsultantReviewModel() when $default != null: + return $default(_that); + case _: + return null; + } + } + + /// A variant of `when` that fallback to an `orElse` callback. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case _: + /// return orElse(); + /// } + /// ``` + + @optionalTypeArgs + TResult maybeWhen( + TResult Function(String planName, int planDuration, int currentPlanDay, + ConsultantComments consultantComments)? + $default, { + required TResult orElse(), + }) { + final _that = this; + switch (_that) { + case _ConsultantReviewModel() when $default != null: + return $default(_that.planName, _that.planDuration, + _that.currentPlanDay, _that.consultantComments); + case _: + return orElse(); + } + } + + /// A `switch`-like method, using callbacks. + /// + /// As opposed to `map`, this offers destructuring. + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case Subclass2(:final field2): + /// return ...; + /// } + /// ``` + + @optionalTypeArgs + TResult when( + TResult Function(String planName, int planDuration, int currentPlanDay, + ConsultantComments consultantComments) + $default, + ) { + final _that = this; + switch (_that) { + case _ConsultantReviewModel(): + return $default(_that.planName, _that.planDuration, + _that.currentPlanDay, _that.consultantComments); + case _: + throw StateError('Unexpected subclass'); + } + } + + /// A variant of `when` that fallback to returning `null` + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case _: + /// return null; + /// } + /// ``` + + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function(String planName, int planDuration, int currentPlanDay, + ConsultantComments consultantComments)? + $default, + ) { + final _that = this; + switch (_that) { + case _ConsultantReviewModel() when $default != null: + return $default(_that.planName, _that.planDuration, + _that.currentPlanDay, _that.consultantComments); + case _: + return null; + } + } +} + /// @nodoc @JsonSerializable() class _ConsultantReviewModel implements ConsultantReviewModel { @@ -340,6 +505,172 @@ class _$ConsultantCommentsCopyWithImpl<$Res> } } +/// Adds pattern-matching-related methods to [ConsultantComments]. +extension ConsultantCommentsPatterns on ConsultantComments { + /// A variant of `map` that fallback to returning `orElse`. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case _: + /// return orElse(); + /// } + /// ``` + + @optionalTypeArgs + TResult maybeMap( + TResult Function(_ConsultantComments value)? $default, { + required TResult orElse(), + }) { + final _that = this; + switch (_that) { + case _ConsultantComments() when $default != null: + return $default(_that); + case _: + return orElse(); + } + } + + /// A `switch`-like method, using callbacks. + /// + /// Callbacks receives the raw object, upcasted. + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case final Subclass2 value: + /// return ...; + /// } + /// ``` + + @optionalTypeArgs + TResult map( + TResult Function(_ConsultantComments value) $default, + ) { + final _that = this; + switch (_that) { + case _ConsultantComments(): + return $default(_that); + case _: + throw StateError('Unexpected subclass'); + } + } + + /// A variant of `map` that fallback to returning `null`. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case _: + /// return null; + /// } + /// ``` + + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_ConsultantComments value)? $default, + ) { + final _that = this; + switch (_that) { + case _ConsultantComments() when $default != null: + return $default(_that); + case _: + return null; + } + } + + /// A variant of `when` that fallback to an `orElse` callback. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case _: + /// return orElse(); + /// } + /// ``` + + @optionalTypeArgs + TResult maybeWhen( + TResult Function(String? dayComment, List mealsComments, + List portionsComments)? + $default, { + required TResult orElse(), + }) { + final _that = this; + switch (_that) { + case _ConsultantComments() when $default != null: + return $default( + _that.dayComment, _that.mealsComments, _that.portionsComments); + case _: + return orElse(); + } + } + + /// A `switch`-like method, using callbacks. + /// + /// As opposed to `map`, this offers destructuring. + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case Subclass2(:final field2): + /// return ...; + /// } + /// ``` + + @optionalTypeArgs + TResult when( + TResult Function(String? dayComment, List mealsComments, + List portionsComments) + $default, + ) { + final _that = this; + switch (_that) { + case _ConsultantComments(): + return $default( + _that.dayComment, _that.mealsComments, _that.portionsComments); + case _: + throw StateError('Unexpected subclass'); + } + } + + /// A variant of `when` that fallback to returning `null` + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case _: + /// return null; + /// } + /// ``` + + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function(String? dayComment, List mealsComments, + List portionsComments)? + $default, + ) { + final _that = this; + switch (_that) { + case _ConsultantComments() when $default != null: + return $default( + _that.dayComment, _that.mealsComments, _that.portionsComments); + case _: + return null; + } + } +} + /// @nodoc @JsonSerializable() class _ConsultantComments implements ConsultantComments { @@ -532,6 +863,163 @@ class _$MealCommentCopyWithImpl<$Res> implements $MealCommentCopyWith<$Res> { } } +/// Adds pattern-matching-related methods to [MealComment]. +extension MealCommentPatterns on MealComment { + /// A variant of `map` that fallback to returning `orElse`. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case _: + /// return orElse(); + /// } + /// ``` + + @optionalTypeArgs + TResult maybeMap( + TResult Function(_MealComment value)? $default, { + required TResult orElse(), + }) { + final _that = this; + switch (_that) { + case _MealComment() when $default != null: + return $default(_that); + case _: + return orElse(); + } + } + + /// A `switch`-like method, using callbacks. + /// + /// Callbacks receives the raw object, upcasted. + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case final Subclass2 value: + /// return ...; + /// } + /// ``` + + @optionalTypeArgs + TResult map( + TResult Function(_MealComment value) $default, + ) { + final _that = this; + switch (_that) { + case _MealComment(): + return $default(_that); + case _: + throw StateError('Unexpected subclass'); + } + } + + /// A variant of `map` that fallback to returning `null`. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case _: + /// return null; + /// } + /// ``` + + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_MealComment value)? $default, + ) { + final _that = this; + switch (_that) { + case _MealComment() when $default != null: + return $default(_that); + case _: + return null; + } + } + + /// A variant of `when` that fallback to an `orElse` callback. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case _: + /// return orElse(); + /// } + /// ``` + + @optionalTypeArgs + TResult maybeWhen( + TResult Function(String comment, int typeNo)? $default, { + required TResult orElse(), + }) { + final _that = this; + switch (_that) { + case _MealComment() when $default != null: + return $default(_that.comment, _that.typeNo); + case _: + return orElse(); + } + } + + /// A `switch`-like method, using callbacks. + /// + /// As opposed to `map`, this offers destructuring. + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case Subclass2(:final field2): + /// return ...; + /// } + /// ``` + + @optionalTypeArgs + TResult when( + TResult Function(String comment, int typeNo) $default, + ) { + final _that = this; + switch (_that) { + case _MealComment(): + return $default(_that.comment, _that.typeNo); + case _: + throw StateError('Unexpected subclass'); + } + } + + /// A variant of `when` that fallback to returning `null` + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case _: + /// return null; + /// } + /// ``` + + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function(String comment, int typeNo)? $default, + ) { + final _that = this; + switch (_that) { + case _MealComment() when $default != null: + return $default(_that.comment, _that.typeNo); + case _: + return null; + } + } +} + /// @nodoc @JsonSerializable() class _MealComment implements MealComment { @@ -692,6 +1180,166 @@ class _$PortionCommentCopyWithImpl<$Res> } } +/// Adds pattern-matching-related methods to [PortionComment]. +extension PortionCommentPatterns on PortionComment { + /// A variant of `map` that fallback to returning `orElse`. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case _: + /// return orElse(); + /// } + /// ``` + + @optionalTypeArgs + TResult maybeMap( + TResult Function(_PortionComment value)? $default, { + required TResult orElse(), + }) { + final _that = this; + switch (_that) { + case _PortionComment() when $default != null: + return $default(_that); + case _: + return orElse(); + } + } + + /// A `switch`-like method, using callbacks. + /// + /// Callbacks receives the raw object, upcasted. + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case final Subclass2 value: + /// return ...; + /// } + /// ``` + + @optionalTypeArgs + TResult map( + TResult Function(_PortionComment value) $default, + ) { + final _that = this; + switch (_that) { + case _PortionComment(): + return $default(_that); + case _: + throw StateError('Unexpected subclass'); + } + } + + /// A variant of `map` that fallback to returning `null`. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case final Subclass value: + /// return ...; + /// case _: + /// return null; + /// } + /// ``` + + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_PortionComment value)? $default, + ) { + final _that = this; + switch (_that) { + case _PortionComment() when $default != null: + return $default(_that); + case _: + return null; + } + } + + /// A variant of `when` that fallback to an `orElse` callback. + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case _: + /// return orElse(); + /// } + /// ``` + + @optionalTypeArgs + TResult maybeWhen( + TResult Function(@JsonKey(name: 'id') int portionId, String comment)? + $default, { + required TResult orElse(), + }) { + final _that = this; + switch (_that) { + case _PortionComment() when $default != null: + return $default(_that.portionId, _that.comment); + case _: + return orElse(); + } + } + + /// A `switch`-like method, using callbacks. + /// + /// As opposed to `map`, this offers destructuring. + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case Subclass2(:final field2): + /// return ...; + /// } + /// ``` + + @optionalTypeArgs + TResult when( + TResult Function(@JsonKey(name: 'id') int portionId, String comment) + $default, + ) { + final _that = this; + switch (_that) { + case _PortionComment(): + return $default(_that.portionId, _that.comment); + case _: + throw StateError('Unexpected subclass'); + } + } + + /// A variant of `when` that fallback to returning `null` + /// + /// It is equivalent to doing: + /// ```dart + /// switch (sealedClass) { + /// case Subclass(:final field): + /// return ...; + /// case _: + /// return null; + /// } + /// ``` + + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function(@JsonKey(name: 'id') int portionId, String comment)? + $default, + ) { + final _that = this; + switch (_that) { + case _PortionComment() when $default != null: + return $default(_that.portionId, _that.comment); + case _: + return null; + } + } +} + /// @nodoc @JsonSerializable() class _PortionComment implements PortionComment { diff --git a/lib/features/profile/models/profile_model.dart b/lib/features/profile/models/profile_model.dart index 8d13bc7e..72e91d0d 100644 --- a/lib/features/profile/models/profile_model.dart +++ b/lib/features/profile/models/profile_model.dart @@ -17,6 +17,7 @@ abstract class ProfileModel with _$ProfileModel { @JsonKey(name: "created_at") DateTime? createdAt, @JsonKey(name: "updated_at") DateTime? updatedAt, @JsonKey(name: "is_public_profile", fromJson: parseBool) required bool isPublicProfile, + @JsonKey(name: "is_blocked_by_me", fromJson: parseBool) required bool isBlockedByMe, @JsonKey(name: 'is_entity_subscribed_by_me') required bool isEntitySubscribedByMe, @JsonKey(name: "slug") required String slug, @JsonKey(name: "profile_photo_url") required String profilePhotoUrl, diff --git a/test/features/profile/services/profile_service_test.dart b/test/features/profile/services/profile_service_test.dart index e735602c..b2c51b01 100644 --- a/test/features/profile/services/profile_service_test.dart +++ b/test/features/profile/services/profile_service_test.dart @@ -21,6 +21,7 @@ void main() { email: 'mso7nueh@mail.ru', isPublicProfile: true, isEntitySubscribedByMe: false, + isBlockedByMe: false, slug: 'slug', profilePhotoUrl: '', roles: ['user'],