Chepuhagram/lib/presentation/screens/admin_broadcast_screen.dart

315 lines
10 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import '/domain/services/api_service.dart';
import 'package:http/http.dart' as http;
import '/core/constants.dart';
import 'dart:convert';
class AdminBroadcastScreen extends StatefulWidget {
const AdminBroadcastScreen({Key? key}) : super(key: key);
@override
State<AdminBroadcastScreen> createState() => _AdminBroadcastScreenState();
}
class _AdminBroadcastScreenState extends State<AdminBroadcastScreen> {
final TextEditingController _messageController = TextEditingController();
final ApiService _apiService = ApiService();
bool _isLoading = false;
bool _broadcastToAll = true;
Set<int> _selectedUserIds = {};
List<Map<String, dynamic>> _allUsers = [];
bool _isLoadingUsers = false;
@override
void initState() {
super.initState();
_loadAllUsers();
}
@override
void dispose() {
_messageController.dispose();
super.dispose();
}
Future<void> _loadAllUsers() async {
setState(() => _isLoadingUsers = true);
try {
final token = await _apiService.getAccessToken();
final response = await http.get(
Uri.parse('${AppConstants.baseUrl}/users/all'),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(utf8.decode(response.bodyBytes));
setState(() {
_allUsers = data.map((user) {
return {
'id': user['id'],
'username': user['username'],
'name': user['name'],
};
}).toList();
});
} else {
_showErrorDialog('Не удалось загрузить список пользователей');
}
} catch (e) {
_showErrorDialog('Ошибка при загрузке пользователей: $e');
} finally {
setState(() => _isLoadingUsers = false);
}
}
Future<void> _sendBroadcast() async {
final message = _messageController.text.trim();
if (message.isEmpty) {
_showErrorDialog('Введите текст сообщения');
return;
}
if (!_broadcastToAll && _selectedUserIds.isEmpty) {
_showErrorDialog('Выберите хотя бы одного пользователя');
return;
}
setState(() => _isLoading = true);
try {
final token = await _apiService.getAccessToken();
final payload = {
'content': message,
if (!_broadcastToAll) 'user_ids': _selectedUserIds.toList(),
};
final response = await http.post(
Uri.parse('${AppConstants.baseUrl}/admin/broadcast'),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode(payload),
);
if (response.statusCode == 200) {
final data = jsonDecode(utf8.decode(response.bodyBytes));
_messageController.clear();
setState(() => _selectedUserIds.clear());
_showSuccessDialog(data['message'] ?? 'Рассылка отправлена успешно');
} else {
final errorData = jsonDecode(utf8.decode(response.bodyBytes));
_showErrorDialog(
errorData['detail'] ?? 'Не удалось отправить рассылку',
);
}
} catch (e) {
_showErrorDialog('Ошибка при отправке рассылки: $e');
} finally {
setState(() => _isLoading = false);
}
}
void _showErrorDialog(String message) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Ошибка'),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('OK'),
),
],
),
);
}
void _showSuccessDialog(String message) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Успешно'),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('OK'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Message input section
Text(
'Текст сообщения',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
TextField(
controller: _messageController,
decoration: InputDecoration(
hintText: 'Введите текст для рассылки...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
filled: true,
fillColor: colorScheme.surface,
),
maxLines: 5,
minLines: 3,
),
const SizedBox(height: 24),
// Recipient selection section
Text(
'Получатели',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
// "All users" toggle
Card(
child: ListTile(
title: const Text('Отправить всем'),
leading: Radio<bool>(
value: true,
groupValue: _broadcastToAll,
onChanged: (value) {
setState(() {
_broadcastToAll = value ?? true;
if (_broadcastToAll) {
_selectedUserIds.clear();
}
});
},
),
),
),
const SizedBox(height: 12),
// "Selected users" toggle
Card(
child: ListTile(
title: const Text('Отправить выбранным'),
leading: Radio<bool>(
value: false,
groupValue: _broadcastToAll,
onChanged: (value) {
setState(() {
_broadcastToAll = value ?? false;
});
},
),
),
),
const SizedBox(height: 16),
// User list for selection
if (!_broadcastToAll) ...[
if (_isLoadingUsers)
const Center(child: CircularProgressIndicator())
else if (_allUsers.isEmpty)
const Padding(
padding: EdgeInsets.all(16),
child: Text('Нет пользователей'),
)
else
Card(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Выбрано: ${_selectedUserIds.length}',
style: Theme.of(context).textTheme.bodyMedium,
),
if (_selectedUserIds.isNotEmpty)
TextButton(
onPressed: () {
setState(() => _selectedUserIds.clear());
},
child: const Text('Очистить'),
),
],
),
),
Divider(height: 0, color: colorScheme.outlineVariant),
SizedBox(
height: 250,
child: ListView.builder(
itemCount: _allUsers.length,
itemBuilder: (context, index) {
final user = _allUsers[index];
final userId = user['id'] as int;
final isSelected = _selectedUserIds.contains(userId);
return CheckboxListTile(
value: isSelected,
onChanged: (selected) {
setState(() {
if (selected == true) {
_selectedUserIds.add(userId);
} else {
_selectedUserIds.remove(userId);
}
});
},
title: Text(user['name'] ?? 'Неизвестный'),
subtitle: Text('@${user['username']}'),
);
},
),
),
],
),
),
],
const SizedBox(height: 24),
// Send button
SizedBox(
width: double.infinity,
child: FilledButton.icon(
onPressed: _isLoading ? null : _sendBroadcast,
icon: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.send),
label: Text(
_isLoading ? 'Отправка...' : 'Отправить рассылку',
),
),
),
],
),
),
);
}
}