You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
5.5 KiB
Dart
167 lines
5.5 KiB
Dart
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:test_sa/extensions/context_extension.dart';
|
|
import 'package:test_sa/extensions/widget_extensions.dart';
|
|
import 'package:test_sa/modules/cm_module/cm_detail_provider.dart';
|
|
import 'package:test_sa/new_views/common_widgets/custom_badge.dart';
|
|
|
|
import 'chat_page.dart';
|
|
import 'chat_provider.dart';
|
|
|
|
class ChatWidget extends StatefulWidget {
|
|
final int moduleId;
|
|
final int requestId;
|
|
final String? assigneeEmployeeNumber;
|
|
final String? myLoginUserID;
|
|
final String? contactEmployeeINumber;
|
|
final bool isReadOnly;
|
|
final bool isShow;
|
|
final String title;
|
|
|
|
const ChatWidget({
|
|
Key? key,
|
|
this.isReadOnly = false,
|
|
this.title = "Chat",
|
|
required this.moduleId,
|
|
required this.requestId,
|
|
this.assigneeEmployeeNumber,
|
|
this.myLoginUserID,
|
|
this.contactEmployeeINumber,
|
|
this.isShow = false,
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
State<ChatWidget> createState() => _ChatWidgetState();
|
|
}
|
|
|
|
class _ChatWidgetState extends State<ChatWidget> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
if (!widget.isShow) return;
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (!mounted) return;
|
|
_prepareChatSession();
|
|
});
|
|
}
|
|
|
|
@override
|
|
void didUpdateWidget(covariant ChatWidget oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
// Re-prepare when the widget is pointed at a different request.
|
|
if (widget.isShow && (oldWidget.requestId != widget.requestId || oldWidget.moduleId != widget.moduleId)) {
|
|
_prepareChatSession();
|
|
}
|
|
}
|
|
|
|
/// Fetches the chat token + participants, unless the exact same conversation
|
|
/// is already loaded in the (globally shared) [ChatProvider].
|
|
///
|
|
/// NOTE: `reset()` is intentionally NOT called here. `ChatProvider` is a
|
|
/// singleton, so resetting on every mount used to drop a live SignalR socket
|
|
/// and made the "already loaded" guard below dead code (it always re-fetched).
|
|
/// `getUserAutoLoginTokenSilent()` performs its own reset internally.
|
|
void _prepareChatSession() {
|
|
final ChatProvider cProvider = context.read<ChatProvider>();
|
|
final String sessionKey = ChatProvider.buildSessionKey(widget.moduleId, widget.requestId);
|
|
if (cProvider.chatLoginTokenLoading) return;
|
|
if (cProvider.chatLoginResponse != null && cProvider.sessionKey == sessionKey) return;
|
|
|
|
final String? myEmployeeId = widget.myLoginUserID ?? context.userProvider.user?.username;
|
|
final String? receiver = _resolveReceiver();
|
|
if (myEmployeeId == null || myEmployeeId.isEmpty || receiver == null || receiver.isEmpty) {
|
|
// Not enough information to open a conversation (e.g. no assignee /
|
|
// contact person yet) - keep the icon idle instead of crashing on a
|
|
// null assertion.
|
|
return;
|
|
}
|
|
|
|
cProvider.getUserAutoLoginTokenSilent(
|
|
widget.moduleId,
|
|
widget.requestId,
|
|
widget.title,
|
|
myEmployeeId,
|
|
receiver,
|
|
isMounted: mounted,
|
|
);
|
|
}
|
|
|
|
/// Nurse -> chats the assigned engineer. Engineer -> chats the contact person.
|
|
String? _resolveReceiver() {
|
|
final workOrder = context.read<CMDetailProvider>().currentWorkOrder?.data;
|
|
|
|
if (context.userProvider.isNurse) {
|
|
return widget.assigneeEmployeeNumber ?? workOrder?.assignedEmployee?.employeeId;
|
|
}
|
|
if (context.userProvider.isEngineer) {
|
|
if (widget.contactEmployeeINumber != null && widget.contactEmployeeINumber!.isNotEmpty) {
|
|
return widget.contactEmployeeINumber;
|
|
}
|
|
final contacts = workOrder?.workOrderContactPerson;
|
|
if (contacts != null && contacts.isNotEmpty) {
|
|
return contacts.first.employeeId;
|
|
}
|
|
return workOrder?.workOrderCreatedBy?.employeeId;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void _openChat() {
|
|
Navigator.push(
|
|
context,
|
|
CupertinoPageRoute<void>(
|
|
builder: (_) => ChatPage(
|
|
moduleId: widget.moduleId,
|
|
requestId: widget.requestId,
|
|
title: widget.title,
|
|
readOnly: widget.isReadOnly,
|
|
assigneeEmployeeNumber: widget.assigneeEmployeeNumber,
|
|
contactEmployeeINumber: widget.contactEmployeeINumber,
|
|
myLoginUserID: widget.myLoginUserID,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (!widget.isShow) return const SizedBox();
|
|
|
|
// Selector (instead of Consumer) so the badge does not rebuild on every
|
|
// incoming message / typing event pushed through ChatProvider.
|
|
return Selector<ChatProvider, _ChatBadgeState>(
|
|
selector: (_, provider) => _ChatBadgeState(
|
|
isLoading: provider.chatLoginTokenLoading,
|
|
unreadCount: provider.chatParticipantModel?.unreadCount ?? 0,
|
|
),
|
|
builder: (_, state, __) => CustomBadge2(
|
|
value: state.effectiveUnread,
|
|
top: 2,
|
|
right: 2,
|
|
minSize: 18,
|
|
child: IconButton(
|
|
icon: const Icon(Icons.chat_bubble),
|
|
onPressed: _openChat,
|
|
).toShimmer(context: context, isShow: state.isLoading, radius: 30, height: 30, width: 30),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ChatBadgeState {
|
|
final bool isLoading;
|
|
final int unreadCount;
|
|
|
|
const _ChatBadgeState({required this.isLoading, required this.unreadCount});
|
|
|
|
int get effectiveUnread => isLoading ? 0 : unreadCount;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) || (other is _ChatBadgeState && other.isLoading == isLoading && other.effectiveUnread == effectiveUnread);
|
|
|
|
@override
|
|
int get hashCode => Object.hash(isLoading, effectiveUnread);
|
|
}
|