Merge remote-tracking branch 'upstream/main' into issue-975

This commit is contained in:
misha
2026-07-15 15:17:44 +03:00
74 changed files with 3880 additions and 251 deletions

View File

@@ -42,10 +42,11 @@ import 'package:intl/intl.dart';
import 'package:visibility_detector/visibility_detector.dart';
class ChatScreen extends StatefulWidget {
final String uuid;
final String? uuid;
final String? orderId;
final bool? sendInitialMessage;
const ChatScreen({required this.uuid, super.key, required this.sendInitialMessage});
const ChatScreen({this.uuid, this.orderId, super.key, required this.sendInitialMessage});
@override
State<ChatScreen> createState() => _ChatScreenState();
@@ -61,6 +62,11 @@ class _ChatScreenState extends State<ChatScreen> {
late AppLifecycleListener _listener;
List<File> pickedFiles = [];
/// Conversation uuid после успешного init (в т.ч. после resolve по orderId).
String? _effectiveUuid;
String? get conversationUuid => _effectiveUuid ?? widget.uuid;
void _onHide() {
chatRoomCubit.cancelTimer();
}
@@ -70,26 +76,41 @@ class _ChatScreenState extends State<ChatScreen> {
chatRoomCubit.setTimer();
}
Future<void> _initChatRoom() async {
final uuid = await chatRoomCubit.initChat(
conversationUuid,
orderId: _effectiveUuid == null ? widget.orderId : null,
sendInitialMessage: widget.sendInitialMessage ?? false,
);
if (uuid != null && mounted) {
setState(() => _effectiveUuid = uuid);
}
}
@override
void didUpdateWidget(ChatScreen oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.uuid != widget.uuid) {
chatRoomCubit.initChat(widget.uuid, sendInitialMessage: widget.sendInitialMessage ?? false);
if (oldWidget.uuid != widget.uuid || oldWidget.orderId != widget.orderId) {
_effectiveUuid = widget.uuid;
_initChatRoom();
}
}
@override
void initState() {
super.initState();
_effectiveUuid = widget.uuid;
scrollController.addListener(_onScroll);
chatRoomCubit.initChat(widget.uuid, sendInitialMessage: widget.sendInitialMessage ?? false);
_initChatRoom();
chatRoomCubit.setTimer();
_listener = AppLifecycleListener(onHide: _onHide, onResume: _onResume);
}
void _onScroll() {
final uuid = conversationUuid;
if (uuid == null) return;
if (scrollController.offset >= scrollController.position.maxScrollExtent && !scrollController.position.outOfRange) {
chatRoomCubit.loadNextPage(widget.uuid);
chatRoomCubit.loadNextPage(uuid);
}
}
@@ -123,7 +144,7 @@ class _ChatScreenState extends State<ChatScreen> {
},
),
),
body: DataErrorWidget(onReload: () => chatRoomCubit.initChat(widget.uuid)),
body: DataErrorWidget(onReload: _initChatRoom),
);
} else {
return Scaffold(
@@ -147,10 +168,7 @@ class _ChatScreenState extends State<ChatScreen> {
}
Scaffold _buildChat(BuildContext context, ChatRoomStateLoaded state) {
final chatCounterpart = resolveChatCounterpart(
users: state.chatRoom.users,
currentUserId: sl<WsManager>().userId,
);
final chatCounterpart = resolveChatCounterpart(users: state.chatRoom.users, currentUserId: sl<WsManager>().userId);
final chosenPlan = state.chatRoom.chosenPlan;
final amIConsultant = chosenPlan?.consultantId == state.chatRoom.users.sender.id;
final showUserDetailsButton = [
@@ -238,12 +256,7 @@ class _ChatScreenState extends State<ChatScreen> {
style: CustomTextStyles.titleSmallBold.copyWith(color: PrimaryColors().black),
),
AnimatedCrossFade(
firstChild: Text(
'Печатает...',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: CustomTextStyles.bodySmallMulishPrimary,
),
firstChild: Text('Печатает...', maxLines: 1, overflow: TextOverflow.ellipsis, style: CustomTextStyles.bodySmallMulishPrimary),
firstCurve: Curves.easeIn,
secondCurve: Curves.easeOut,
secondChild: ChatUserOnlineStatusLine(
@@ -264,9 +277,7 @@ class _ChatScreenState extends State<ChatScreen> {
actions: [
Padding(
padding: const EdgeInsetsDirectional.only(end: 8),
child: _ChatHeaderVideoCallButton(
onPressed: () => _openVideoCall(context, state, chatCounterpart),
),
child: _ChatHeaderVideoCallButton(onPressed: () => _openVideoCall(context, state, chatCounterpart)),
),
],
),
@@ -771,7 +782,7 @@ class _ChatScreenState extends State<ChatScreen> {
GoRouter.of(context).pushNamed(
VideoCallScreen.name,
extra: <String, dynamic>{
'conversationUuid': widget.uuid,
'conversationUuid': conversationUuid,
'counterpartName': chatCounterpart.name,
'counterpartAvatarUrl': chatCounterpart.profilePhotoUrl,
'counterpartUserId': chatCounterpart.id,
@@ -833,9 +844,7 @@ class _ChatHeaderVideoCallButton extends StatelessWidget {
child: SizedBox(
width: 40,
height: 40,
child: Center(
child: Icon(Icons.videocam_rounded, color: appTheme.blueA400, size: 18),
),
child: Center(child: Icon(Icons.videocam_rounded, color: appTheme.blueA400, size: 18)),
),
),
);