socket fixed

This commit is contained in:
Username1548
2025-01-19 15:10:56 +07:00
parent ab7004f9fd
commit 7eaf0bc1bb
29 changed files with 489 additions and 1006 deletions

View File

@@ -1,11 +1,16 @@
import 'package:eatvio_app/core/initialization_cubit/ws_manager.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class NotificationsCubit extends Cubit<NotificationsState> {
int _notificationsCount = 0;
List<String> notifications = [];
NotificationsCubit() : super(const NotificationsState.empty());
final WsManager wsManager;
NotificationsCubit({required this.wsManager}) : super(const NotificationsState.empty()) {
wsManager.newNotificationsStream?.listen((event) {
incrementCounter(event.data);
});
}
incrementCounter(String data) {
_notificationsCount++;

View File

@@ -2,13 +2,9 @@ import 'dart:convert';
import 'dart:io';
import 'package:audioplayers/audioplayers.dart';
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
import 'package:eatvio_app/core/initialization_cubit/initialization_cubit.dart';
import 'package:eatvio_app/core/initialization_cubit/initialization_state.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/core/initialization_cubit/ws_manager.dart';
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
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';
@@ -16,41 +12,42 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class ChatRoomCubit extends Cubit<ChatRoomState> {
final ChatRepository chatRepository;
final InitializationCubit initializationCubit;
final WsManager wsManager;
int currentPage = 1;
late final int lastPage;
bool isLoading = false;
late final PusherChannelsClient client;
late final PrivateChannel privateChatChannel;
ChatRoomCubit({
required this.chatRepository,
required this.initializationCubit,
required this.wsManager,
}) : super(ChatRoomState.loading());
loadNextPage(String chatId) async {
if (currentPage < lastPage && !isLoading) {
isLoading = true;
emit(ChatRoomState.loaded(
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
chatMessages: (state as ChatRoomStateLoaded).chatMessages,
isTyping: (state as ChatRoomStateLoaded).isTyping,
isLoading: isLoading,
),);
emit(
ChatRoomState.loaded(
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
chatMessages: (state as ChatRoomStateLoaded).chatMessages,
isTyping: (state as ChatRoomStateLoaded).isTyping,
isLoading: isLoading,
),
);
try {
final chatMessages = await chatRepository.getChatMessages(chatId, currentPage + 1);
isLoading = false;
emit(ChatRoomState.loaded(
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
chatMessages: [...(state as ChatRoomStateLoaded).chatMessages, ...chatMessages.messagesList],
isTyping: (state as ChatRoomStateLoaded).isTyping,
isLoading: isLoading,
),);
emit(
ChatRoomState.loaded(
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
chatMessages: [...(state as ChatRoomStateLoaded).chatMessages, ...chatMessages.messagesList],
isTyping: (state as ChatRoomStateLoaded).isTyping,
isLoading: isLoading,
),
);
currentPage++;
} catch (e) {
@@ -66,38 +63,29 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
final chatMessages = await chatRepository.getChatMessages(chatId, currentPage);
lastPage = chatMessages.meta.lastPage;
await _subscribeToChatChannel(initCubit: initializationCubit, chatId: chatId);
await _subscribeToChatChannel(chatId: chatId);
emit(ChatRoomState.loaded(
chatRoom: chatRoom,
chatMessages: chatMessages.messagesList,
isTyping: false,
isLoading: isLoading,
),);
emit(
ChatRoomState.loaded(
chatRoom: chatRoom,
chatMessages: chatMessages.messagesList,
isTyping: false,
isLoading: isLoading,
),
);
} catch (e) {
emit(ChatRoomState.error(errorMessage: 'Ошибка получеия данных'));
}
}
Future<void> _subscribeToChatChannel({
required InitializationCubit initCubit,
required String chatId,
}) async {
client = (initCubit.state as InitializationStateSuccess).client;
privateChatChannel = await client.privateChannel(
'private-direct-chat.$chatId',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse(baseUrl + '/broadcasting/auth'),
headers: {
'Authorization': 'Bearer ${await TokenStorage.getTokenFromStorage()}',
},
),
);
privateChatChannel.subscribe();
privateChatChannel.bind('App\\Events\\ChatMessageUpdated').listen((event) {
final client = wsManager.client;
if (client == null) return;
await wsManager.initPrivateDirectChatChannel(chatId);
wsManager.subscribeDirectChatChannel();
wsManager.directChatStream?.listen((event) {
AudioPlayer().play((AssetSource('audio/recive_message.wav')));
List<ChatMessageModel> messagesList = [
ChatMessageModel.fromJson(jsonDecode(event.data)['message']),
@@ -113,7 +101,7 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
);
});
privateChatChannel.bind('client-typing').listen((event) {
wsManager.clientTypingStream?.listen((event) {
emit(
ChatRoomState.loaded(
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
@@ -152,7 +140,7 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
@override
Future<void> close() async {
privateChatChannel.unsubscribe();
wsManager.unsubscribeDirectChatChannel();
super.close();
}
}

View File

@@ -1,4 +1,5 @@
import 'dart:io';
import 'dart:math';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
@@ -78,6 +79,7 @@ class _ChatScreenState extends State<ChatScreen> {
resizeToAvoidBottomInset: true,
appBar: AppBar(
titleSpacing: 0,
backgroundColor: appTheme.gray100,
centerTitle: false,
title: Row(
children: [
@@ -136,7 +138,7 @@ class _ChatScreenState extends State<ChatScreen> {
children: [
Expanded(
child: Container(
color: Color(0xFFF0F2F5),
color: appTheme.whiteA700,
width: double.maxFinite,
child: CustomScrollView(
physics: BouncingScrollPhysics(),
@@ -149,20 +151,16 @@ class _ChatScreenState extends State<ChatScreen> {
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index < state.chatMessages.length) {
return Column(
children: [
if ((index + 1 != state.chatMessages.length &&
DateTime.parse(state.chatMessages[index].updatedAt).day !=
DateTime.parse(state.chatMessages[index + 1].updatedAt).day) ||
index + 1 == state.chatMessages.length)
_messageDate(state.chatMessages[index].updatedAt),
ChatBubbleWidget(
sendTime: state.chatMessages[index].updatedAt,
text: state.chatMessages[index].data.trim(),
isCurrentUser: state.chatRoom.users['to_chat_user']!.id != state.chatMessages[index].authorId,
mediaData: state.chatMessages[index].mediaData ?? [],
),
],
if ((index + 1 != state.chatMessages.length &&
DateTime.parse(state.chatMessages[index].updatedAt).day != DateTime.parse(state.chatMessages[index + 1].updatedAt).day) ||
index + 1 == state.chatMessages.length) {
return _messageDate(state.chatMessages[index].updatedAt);
}
return ChatBubbleWidget(
sendTime: state.chatMessages[index].updatedAt,
text: state.chatMessages[index].data.trim(),
isCurrentUser: state.chatRoom.users['to_chat_user']!.id != state.chatMessages[index].authorId,
mediaData: state.chatMessages[index].mediaData ?? [],
);
} else {
return Padding(
@@ -181,13 +179,6 @@ class _ChatScreenState extends State<ChatScreen> {
),
Container(
padding: EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: Colors.black.withValues(alpha: 0.1),
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
@@ -231,57 +222,60 @@ class _ChatScreenState extends State<ChatScreen> {
},
customBorder: CircleBorder(),
child: Ink(
height: 32,
width: 32,
height: 40,
width: 40,
child: Transform.rotate(
angle: 3.8,
angle: pi / 4,
child: Icon(
Icons.attach_file_rounded,
size: 24,
size: 30,
color: Color(0xFF858E99),
),
),
),
),
SizedBox(width: 8),
SizedBox(
width: MediaQuery.of(context).size.width - 96,
child: TextFormField(
controller: textEditingController,
maxLines: 5,
minLines: 1,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w400, color: Colors.black, height: 1),
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
hintText: 'Сообщение',
hintStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
height: 1,
color: Color(0xFFAEAEB2),
),
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
Expanded(
child: SizedBox(
height: 40,
child: TextFormField(
expands: true,
controller: textEditingController,
maxLines: null,
minLines: null,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w400, color: Colors.black, height: 1),
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
hintText: 'Сообщение',
hintStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1,
color: Color(0xFFAEAEB2),
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
),
),
@@ -306,11 +300,11 @@ class _ChatScreenState extends State<ChatScreen> {
color: appTheme.blueA400,
shape: BoxShape.circle,
),
height: 32,
width: 32,
height: 40,
width: 40,
child: Icon(
Icons.arrow_upward_rounded,
size: 20,
size: 24,
color: Colors.white,
),
),
@@ -326,16 +320,18 @@ class _ChatScreenState extends State<ChatScreen> {
}
_messageDate(String date) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
DateFormat.MMMMd('RU').format(
DateTime.parse(date),
),
style: const TextStyle(
height: 1,
color: Color(0xFF8393A1),
fontWeight: FontWeight.w500,
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
DateFormat.yMMMMd('RU').format(
DateTime.parse(date),
),
style: const TextStyle(
height: 1,
color: Color(0xFF8393A1),
fontWeight: FontWeight.w500,
),
),
),
);

View File

@@ -1,6 +1,8 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart';
import 'package:flutter/material.dart';
@@ -28,7 +30,7 @@ class ChatBubbleWidget extends StatelessWidget {
alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft,
child: DecoratedBox(
decoration: BoxDecoration(
color: isCurrentUser ? Color(0xFF7F9CF5) : Colors.white,
color: isCurrentUser ? appTheme.gray50 : appTheme.gray60004,
borderRadius: mediaData.length > 0
? BorderRadius.circular(24)
: BorderRadius.horizontal(
@@ -108,15 +110,15 @@ class ChatBubbleWidget extends StatelessWidget {
if (text.isNotEmpty)
Text(
text,
style: TextStyle(color: isCurrentUser ? Colors.white : Color(0xFF323F4B), height: 1),
style: TextStyles.textFS13FW400.copyWith(color: appTheme.black),
),
SizedBox(height: 4),
SizedBox(height: 8),
Padding(
padding: mediaData.length > 0 ? const EdgeInsets.only(right: 8, top: 4, left: 8) : EdgeInsets.zero,
child: Text(
DateTime.parse(sendTime).format('hh:mm'),
style: TextStyle(
color: isCurrentUser ? Colors.white.withValues(alpha: 0.4) : Color(0xFF323F4B).withValues(alpha: 0.4),
color: Color(0xFF7B8794),
height: 1,
fontSize: 10,
),

View File

@@ -4,7 +4,6 @@ import 'package:dio/dio.dart';
import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_preview_model.dart';
import 'package:eatvio_app/features/chat/models/websocket_info_model.dart';
import 'package:eatvio_app/features/chat/services/chat_service.dart';
import 'package:http_parser/http_parser.dart';
@@ -67,15 +66,7 @@ class ChatRepository {
} catch (e) {
rethrow;
}
}
Future<WebsocketInfoModel> getWebsocket() async {
try {
return (await chatService.getWebsocket());
} catch (e) {
rethrow;
}
}
}
Future<ChatRoomModel> getChatRoomIdByUserSlug({required String slug}) async {
try {

View File

@@ -2,7 +2,6 @@ import 'package:dio/dio.dart';
import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_preview_model.dart';
import 'package:eatvio_app/features/chat/models/websocket_info_model.dart';
import 'package:retrofit/retrofit.dart';
part 'chat_service.g.dart';
@@ -35,9 +34,6 @@ abstract class ChatService {
@Path() required int page,
});
@GET('/api/profile/ws/params')
Future<WebsocketInfoModel> getWebsocket();
@GET('/api/chat/get_by_user/{user}')
Future<ChatRoomModel> getChatRoomIdByUserSlug({
@Path() required String user,

View File

@@ -165,39 +165,6 @@ class _ChatService implements ChatService {
return _value;
}
@override
Future<WebsocketInfoModel> getWebsocket() async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<WebsocketInfoModel>(Options(
method: 'GET',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/api/profile/ws/params',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late WebsocketInfoModel _value;
try {
_value = WebsocketInfoModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
@override
Future<ChatRoomModel> getChatRoomIdByUserSlug({required String user}) async {
final _extra = <String, dynamic>{};

View File

@@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
@@ -22,6 +24,9 @@ class _DiaryScreenState extends State<DiaryScreen> {
create: (BuildContext context) => sl<DiaryCubit>()..fetchDiary(true),
child: BlocBuilder<DiaryCubit, DiaryState>(
builder: (BuildContext context, DiaryState state) {
if (state is DiaryStateLoaded) {
log(state.diary.id.toString());
}
return Scaffold(
backgroundColor: Colors.grey.shade200,
appBar: AppBar(

View File

@@ -39,7 +39,7 @@ class PlanModel with _$PlanModel {
@JsonKey(name: "is_favorite") required bool isFavorite,
@JsonKey(name: "min_price_of_options") required double minPriceOfOptions,
@JsonKey(name: "max_price_of_options") required double maxPriceOfOptions,
@Default(false) bool wasLikedByMe,
@JsonKey(name: "was_liked_by_me") required bool wasLikedByMe,
}) = _PlanModel;
factory PlanModel.fromJson(Map<String, dynamic> json) => _$PlanModelFromJson(json);

View File

@@ -298,6 +298,7 @@ mixin _$PlanModel {
double get minPriceOfOptions => throw _privateConstructorUsedError;
@JsonKey(name: "max_price_of_options")
double get maxPriceOfOptions => throw _privateConstructorUsedError;
@JsonKey(name: "was_liked_by_me")
bool get wasLikedByMe => throw _privateConstructorUsedError;
/// Serializes this PlanModel to a JSON map.
@@ -335,7 +336,7 @@ abstract class $PlanModelCopyWith<$Res> {
@JsonKey(name: "is_favorite") bool isFavorite,
@JsonKey(name: "min_price_of_options") double minPriceOfOptions,
@JsonKey(name: "max_price_of_options") double maxPriceOfOptions,
bool wasLikedByMe});
@JsonKey(name: "was_liked_by_me") bool wasLikedByMe});
$PlanTypeCopyWith<$Res> get type;
$PacketOptionsCopyWith<$Res>? get packet1Options;
@@ -555,7 +556,7 @@ abstract class _$$PlanModelImplCopyWith<$Res>
@JsonKey(name: "is_favorite") bool isFavorite,
@JsonKey(name: "min_price_of_options") double minPriceOfOptions,
@JsonKey(name: "max_price_of_options") double maxPriceOfOptions,
bool wasLikedByMe});
@JsonKey(name: "was_liked_by_me") bool wasLikedByMe});
@override
$PlanTypeCopyWith<$Res> get type;
@@ -711,7 +712,7 @@ class _$PlanModelImpl implements _PlanModel {
@JsonKey(name: "is_favorite") required this.isFavorite,
@JsonKey(name: "min_price_of_options") required this.minPriceOfOptions,
@JsonKey(name: "max_price_of_options") required this.maxPriceOfOptions,
this.wasLikedByMe = false});
@JsonKey(name: "was_liked_by_me") required this.wasLikedByMe});
factory _$PlanModelImpl.fromJson(Map<String, dynamic> json) =>
_$$PlanModelImplFromJson(json);
@@ -774,7 +775,7 @@ class _$PlanModelImpl implements _PlanModel {
@JsonKey(name: "max_price_of_options")
final double maxPriceOfOptions;
@override
@JsonKey()
@JsonKey(name: "was_liked_by_me")
final bool wasLikedByMe;
@override
@@ -865,29 +866,30 @@ class _$PlanModelImpl implements _PlanModel {
abstract class _PlanModel implements PlanModel {
const factory _PlanModel(
{@JsonKey(name: "id") required final int id,
@JsonKey(name: "slug") required final String slug,
@JsonKey(name: "name") required final String name,
@JsonKey(name: "image") required final String image,
@JsonKey(name: "duration") required final int duration,
@JsonKey(name: "state") required final String state,
@JsonKey(name: "type", fromJson: planTypeFromJson)
required final PlanType type,
@JsonKey(name: "details") required final String details,
@JsonKey(name: "packets_count") required final int packetsCount,
@JsonKey(name: "packet1_options") final PacketOptions? packet1Options,
@JsonKey(name: "packet2_options") final PacketOptions? packet2Options,
@JsonKey(name: "packet3_options") final PacketOptions? packet3Options,
@JsonKey(name: "author") required final Author author,
@JsonKey(name: "total_likes") required final int totalLikes,
@JsonKey(name: "total_views") required final int totalViews,
@JsonKey(name: "total_comments") required final int totalComments,
@JsonKey(name: "is_favorite") required final bool isFavorite,
@JsonKey(name: "min_price_of_options")
required final double minPriceOfOptions,
@JsonKey(name: "max_price_of_options")
required final double maxPriceOfOptions,
final bool wasLikedByMe}) = _$PlanModelImpl;
{@JsonKey(name: "id") required final int id,
@JsonKey(name: "slug") required final String slug,
@JsonKey(name: "name") required final String name,
@JsonKey(name: "image") required final String image,
@JsonKey(name: "duration") required final int duration,
@JsonKey(name: "state") required final String state,
@JsonKey(name: "type", fromJson: planTypeFromJson)
required final PlanType type,
@JsonKey(name: "details") required final String details,
@JsonKey(name: "packets_count") required final int packetsCount,
@JsonKey(name: "packet1_options") final PacketOptions? packet1Options,
@JsonKey(name: "packet2_options") final PacketOptions? packet2Options,
@JsonKey(name: "packet3_options") final PacketOptions? packet3Options,
@JsonKey(name: "author") required final Author author,
@JsonKey(name: "total_likes") required final int totalLikes,
@JsonKey(name: "total_views") required final int totalViews,
@JsonKey(name: "total_comments") required final int totalComments,
@JsonKey(name: "is_favorite") required final bool isFavorite,
@JsonKey(name: "min_price_of_options")
required final double minPriceOfOptions,
@JsonKey(name: "max_price_of_options")
required final double maxPriceOfOptions,
@JsonKey(name: "was_liked_by_me") required final bool wasLikedByMe}) =
_$PlanModelImpl;
factory _PlanModel.fromJson(Map<String, dynamic> json) =
_$PlanModelImpl.fromJson;
@@ -950,6 +952,7 @@ abstract class _PlanModel implements PlanModel {
@JsonKey(name: "max_price_of_options")
double get maxPriceOfOptions;
@override
@JsonKey(name: "was_liked_by_me")
bool get wasLikedByMe;
/// Create a copy of PlanModel

View File

@@ -54,7 +54,7 @@ _$PlanModelImpl _$$PlanModelImplFromJson(Map<String, dynamic> json) =>
isFavorite: json['is_favorite'] as bool,
minPriceOfOptions: (json['min_price_of_options'] as num).toDouble(),
maxPriceOfOptions: (json['max_price_of_options'] as num).toDouble(),
wasLikedByMe: json['wasLikedByMe'] as bool? ?? false,
wasLikedByMe: json['was_liked_by_me'] as bool,
);
Map<String, dynamic> _$$PlanModelImplToJson(_$PlanModelImpl instance) =>
@@ -78,7 +78,7 @@ Map<String, dynamic> _$$PlanModelImplToJson(_$PlanModelImpl instance) =>
'is_favorite': instance.isFavorite,
'min_price_of_options': instance.minPriceOfOptions,
'max_price_of_options': instance.maxPriceOfOptions,
'wasLikedByMe': instance.wasLikedByMe,
'was_liked_by_me': instance.wasLikedByMe,
};
_$PacketOptionsImpl _$$PacketOptionsImplFromJson(Map<String, dynamic> json) =>

View File

@@ -9,7 +9,7 @@ import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_sta
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DietsCubit extends Cubit<DietsState>{
class DietsCubit extends Cubit<DietsState> {
final FeedRepository feedRepository;
final DietRepository dietsRepository;
int currentPage = 1;
@@ -52,7 +52,6 @@ class DietsCubit extends Cubit<DietsState>{
super.close();
}
void changeTypeFilter(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(typeIndex: index), errorMessage: null));
EasyDebounce.debounce(
@@ -64,7 +63,6 @@ class DietsCubit extends Cubit<DietsState>{
);
}
void changeSortingType(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(sortingIndex: index), errorMessage: null));
EasyDebounce.debounce(
@@ -76,7 +74,6 @@ class DietsCubit extends Cubit<DietsState>{
);
}
void changeDurationFilter(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(durationIndex: index), errorMessage: null));
EasyDebounce.debounce(
@@ -153,6 +150,7 @@ class DietsCubit extends Cubit<DietsState>{
Future<void> likeDiet(int id, int index) async {
final currentDiets = state.dietsList;
if (currentDiets.isEmpty) return;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[index];
if (currentDiet.wasLikedByMe) {

View File

@@ -284,4 +284,58 @@ class PlansCubit extends Cubit<PlansState> {
},
);
}
Future<void> likePlan(String slug, int index) async {
final currentPlans = state.plans;
final plansForModification = [...currentPlans];
final currentPlan = currentPlans[index];
if (currentPlan.wasLikedByMe) {
plansForModification[index] = currentPlan.copyWith(
totalLikes: currentPlan.totalLikes - 1,
wasLikedByMe: false,
);
emit(
state.copyWith(
plans: [
...plansForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
} else {
plansForModification[index] = currentPlan.copyWith(
totalLikes: currentPlan.totalLikes + 1,
wasLikedByMe: true,
);
emit(
state.copyWith(
plans: [
...plansForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
EasyDebounce.debounce(
'like_plan',
Duration(milliseconds: 200),
() async {
try {
await planRepository.likePlan(slug);
} catch (e) {
log(e.toString());
emit(
state.copyWith(
plans: [
...currentPlans,
],
isLoading: isLoading,
),
);
}
},
);
}
}

View File

@@ -5,7 +5,6 @@ import 'package:eatvio_app/core/widgets/info_row.dart';
import 'package:eatvio_app/core/widgets/feed_icon_button.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
@@ -183,7 +182,7 @@ class _PlanWidgetState extends State<PlanWidget> {
iconColor: widget.plan.wasLikedByMe ? appTheme.blueA400 : null,
backgroundColor: widget.plan.wasLikedByMe ? appTheme.transparentBlue : null,
onTap: () async {
await context.read<DietsCubit>().likeDiet(widget.plan.id, widget.index);
await context.read<PlansCubit>().likePlan(widget.plan.slug, widget.index);
},
),
SizedBox(

View File

@@ -363,13 +363,11 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
'assets/icons/heart.svg',
width: 14,
height: 12,
/// TODO задача для бека на добавление wasLokedByMe?
colorFilter: ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn),
colorFilter: state.planModel?.wasLikedByMe == true ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null,
),
SizedBox(width: 8),
Text(
widget.plan.totalLikes.toString(),
state.planModel?.totalLikes.toString() ?? '0',
style: TextStyles.textXSSemibold.copyWith(color: appTheme.black),
),
],

View File

@@ -59,7 +59,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
late bool canSub;
bool isFirstFetch = true;
int _currentBullet = 0;
@override
void initState() {
super.initState();
@@ -338,7 +337,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: InkWell(
onTap: () {
GoRouter.of(context).pushNamed('blogPostCreateScreen');
GoRouter.of(context).pushNamed('blogPostCreateScreen');
},
borderRadius: BorderRadius.circular(8),
child: Ink(
@@ -911,15 +910,15 @@ class _ProfileScreenState extends State<ProfileScreen> {
child: Text('Посмотреть'),
),
if (widget.author == null)
PopupMenuItem(
value: 'replace',
child: Text('Заменить'),
),
PopupMenuItem(
value: 'replace',
child: Text('Заменить'),
),
if (widget.author == null)
PopupMenuItem(
value: 'delete',
child: Text('Удалить'),
),
PopupMenuItem(
value: 'delete',
child: Text('Удалить'),
),
],
child: Hero(
tag: state.profile.profilePhotoUrl,
@@ -1102,7 +1101,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
);
}
_charts() {
_charts() {
return Material(
color: Colors.white,
borderRadius: BorderRadius.circular(16),