Preisanzeige in der App entfernen und Auftragsbezeichnungen vereinheitlichen. Stationsabschlüsse und Benachrichtigungen absichern sowie Historie um Aufgabenbeschreibungen und ausführende Nutzer erweitern. Regressionstests ergänzen und App sowie Backend auf Version 0.9.24 anheben.
190 lines
5.4 KiB
Dart
190 lines
5.4 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'app_theme.dart';
|
|
import 'l10n/app_localizations.dart';
|
|
import 'l10n/localization_helpers.dart';
|
|
import 'models/chat.dart';
|
|
import 'services/chat_service.dart';
|
|
import 'widgets/offline_banner.dart';
|
|
|
|
class ChatsView extends StatefulWidget {
|
|
const ChatsView({super.key});
|
|
|
|
@override
|
|
State<ChatsView> createState() => _ChatsViewState();
|
|
}
|
|
|
|
class _ChatsViewState extends State<ChatsView> {
|
|
final ChatService _chatService = ChatService();
|
|
List<Chat> _chats = const [];
|
|
StreamSubscription<List<Chat>>? _chatSubscription;
|
|
bool _isInitializing = true;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_initializeChats();
|
|
}
|
|
|
|
Future<void> _initializeChats() async {
|
|
await _chatService.initialize();
|
|
if (!mounted) return;
|
|
|
|
setState(() {
|
|
_chats = _chatService.currentChats;
|
|
_isInitializing = false;
|
|
});
|
|
|
|
_chatSubscription = _chatService.chatsStream.listen((chats) {
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_chats = chats;
|
|
});
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_chatSubscription?.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(title: Text(AppLocalizations.of(context).chats)),
|
|
body: Column(
|
|
children: [const OfflineBanner(), Expanded(child: _buildBody())],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildBody() {
|
|
if (_isInitializing) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
|
|
if (_chats.isEmpty) {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Icon(
|
|
Icons.chat_outlined,
|
|
size: 64,
|
|
color: AppColors.textMuted,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
AppLocalizations.of(context).noChatsAvailable,
|
|
style: const TextStyle(fontSize: 16, color: AppColors.textMuted),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
return ListView.builder(
|
|
itemCount: _chats.length,
|
|
itemBuilder: (context, index) {
|
|
final chat = _chats[index];
|
|
return _buildChatTile(chat);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildChatTile(Chat chat) {
|
|
final isJobChat = chat.type == ChatType.jobSpecific;
|
|
final hasMessages = chat.messages.isNotEmpty;
|
|
final previewText =
|
|
hasMessages
|
|
? chat.lastMessagePreview
|
|
: AppLocalizations.of(context).noMessagesYet;
|
|
final timeLabel = hasMessages ? _formatTime(chat.lastMessageTime) : '--';
|
|
final jobId = chat.jobId?.trim();
|
|
final jobNumber = chat.jobNumber?.trim();
|
|
final showJobId = isJobChat && jobId != null && jobId.isNotEmpty;
|
|
|
|
return Card(
|
|
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
child: ListTile(
|
|
leading: CircleAvatar(
|
|
backgroundColor:
|
|
isJobChat ? AppColors.primarySoft : AppColors.secondarySoft,
|
|
child: Icon(
|
|
isJobChat ? Icons.work : Icons.support_agent,
|
|
color: isJobChat ? AppColors.primaryStrong : AppColors.secondary,
|
|
),
|
|
),
|
|
title: Text(() {
|
|
if (isJobChat) {
|
|
if (jobNumber != null && jobNumber.isNotEmpty) {
|
|
return 'Auftrag $jobNumber';
|
|
}
|
|
if (showJobId) {
|
|
return 'Auftrag $jobId';
|
|
}
|
|
}
|
|
return localizedChatTitle(context, chat);
|
|
}(), style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
|
|
subtitle: Text(
|
|
previewText,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(fontSize: 14, color: AppColors.textMuted),
|
|
),
|
|
trailing: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
timeLabel,
|
|
style: const TextStyle(fontSize: 12, color: AppColors.textMuted),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
|
decoration: BoxDecoration(
|
|
color:
|
|
isJobChat ? AppColors.primarySoft : AppColors.secondarySoft,
|
|
borderRadius: BorderRadius.circular(10),
|
|
border: Border.all(
|
|
color: isJobChat ? AppColors.primary : AppColors.secondary,
|
|
),
|
|
),
|
|
child: Text(
|
|
isJobChat ? 'AUFTRAG' : 'ALLG',
|
|
style: TextStyle(
|
|
fontSize: 10,
|
|
fontWeight: FontWeight.w600,
|
|
color:
|
|
isJobChat ? AppColors.primaryStrong : AppColors.secondary,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
onTap: () {
|
|
Navigator.of(context).pushNamed('/chat_details', arguments: chat);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
String _formatTime(DateTime dateTime) {
|
|
final now = DateTime.now();
|
|
final difference = now.difference(dateTime);
|
|
|
|
if (difference.inDays > 0) {
|
|
return '${difference.inDays}T';
|
|
} else if (difference.inHours > 0) {
|
|
return '${difference.inHours}h';
|
|
} else if (difference.inMinutes > 0) {
|
|
return '${difference.inMinutes}m';
|
|
} else {
|
|
return 'jetzt';
|
|
}
|
|
}
|
|
}
|