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.
cloudsolutions-atoms/lib/modules/cx_module/chat/chat_widget.dart

167 lines
5.5 KiB
Dart

11 months ago
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';
11 months ago
import 'chat_page.dart';
import 'chat_provider.dart';
class ChatWidget extends StatefulWidget {
4 weeks ago
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);
11 months ago
@override
4 weeks ago
State<ChatWidget> createState() => _ChatWidgetState();
11 months ago
}
class _ChatWidgetState extends State<ChatWidget> {
@override
void initState() {
super.initState();
4 weeks ago
if (!widget.isShow) return;
9 months ago
WidgetsBinding.instance.addPostFrameCallback((_) {
4 weeks ago
if (!mounted) return;
_prepareChatSession();
9 months ago
});
11 months ago
}
4 weeks ago
@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();
}
11 months ago
}
4 weeks ago
/// 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,
),
),
);
11 months ago
}
@override
Widget build(BuildContext context) {
4 weeks ago
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),
),
);
11 months ago
}
}
4 weeks ago
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);
}