305 lines
8.5 KiB
Dart
305 lines
8.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:qpay/http/http.dart';
|
|
import 'package:qpay/models/api_response.dart';
|
|
import 'package:qpay/models/pageable_model.dart';
|
|
import 'package:qpay/screens/groups/models/create_group_from_file_response.dart';
|
|
import 'package:qpay/screens/groups/models/recipient_group_model.dart';
|
|
|
|
class GroupScreenModel {
|
|
List<RecipientGroup> groups = [];
|
|
List<RecipientGroupMember> members = [];
|
|
PageableModel? membersPage;
|
|
bool isLoading = false;
|
|
bool isLoadingMore = false;
|
|
String? errorMessage;
|
|
int currentPage = 0;
|
|
int totalPages = 0;
|
|
int totalElements = 0;
|
|
String searchQuery = '';
|
|
bool selectMode = false;
|
|
Set<String> selectedGroupIds = {};
|
|
}
|
|
|
|
class GroupController extends ChangeNotifier {
|
|
final GroupScreenModel model = GroupScreenModel();
|
|
final Http http = Http();
|
|
BuildContext context;
|
|
bool _disposed = false;
|
|
|
|
GroupController(this.context);
|
|
|
|
@override
|
|
void dispose() {
|
|
_disposed = true;
|
|
super.dispose();
|
|
}
|
|
|
|
dynamic _unwrap(dynamic response) {
|
|
if (response is Map && response.containsKey('body')) {
|
|
return response['body'];
|
|
}
|
|
return response;
|
|
}
|
|
|
|
String buildQueryParameters(Map<String, String> params) {
|
|
if (params.isEmpty) {
|
|
return '';
|
|
}
|
|
|
|
String query = '';
|
|
params.forEach((key, value) {
|
|
query += '$key=$value&';
|
|
});
|
|
return query.substring(0, query.length - 1);
|
|
}
|
|
|
|
Future<ApiResponse<PageableModel>> listGroups({
|
|
required String workspaceId,
|
|
int page = 0,
|
|
int size = 20,
|
|
String? name,
|
|
}) async {
|
|
if (page == 0) {
|
|
model.isLoading = true;
|
|
model.groups = [];
|
|
model.currentPage = 0;
|
|
} else {
|
|
model.isLoadingMore = true;
|
|
}
|
|
if (!_disposed) notifyListeners();
|
|
|
|
try {
|
|
final params = <String, String>{
|
|
'workspaceId': workspaceId,
|
|
'page': page.toString(),
|
|
'size': size.toString(),
|
|
};
|
|
if (name != null && name.isNotEmpty) {
|
|
params['name'] = name;
|
|
}
|
|
|
|
final raw = await http.get(
|
|
'/recipient-groups?${buildQueryParameters(params)}',
|
|
);
|
|
final response = _unwrap(raw);
|
|
final pageableModel = PageableModel.fromJson(
|
|
response as Map<String, dynamic>,
|
|
);
|
|
|
|
final newGroups = pageableModel.content
|
|
.map((e) => RecipientGroup.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
|
|
if (page == 0) {
|
|
model.groups = newGroups;
|
|
} else {
|
|
model.groups.addAll(newGroups);
|
|
}
|
|
model.currentPage = pageableModel.number;
|
|
model.totalPages = pageableModel.totalPages;
|
|
model.totalElements = pageableModel.totalElements;
|
|
model.isLoading = false;
|
|
model.isLoadingMore = false;
|
|
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.success(pageableModel);
|
|
} catch (e) {
|
|
logger.e(e);
|
|
model.errorMessage = e.toString();
|
|
if (page == 0) {
|
|
model.groups = [];
|
|
}
|
|
model.isLoading = false;
|
|
model.isLoadingMore = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.failure('Failed to load groups');
|
|
}
|
|
}
|
|
|
|
Future<void> searchGroups({required String workspaceId, String? name}) async {
|
|
model.searchQuery = name ?? '';
|
|
await listGroups(workspaceId: workspaceId, page: 0, name: name);
|
|
}
|
|
|
|
Future<void> loadNextPage({required String workspaceId}) async {
|
|
if (model.isLoadingMore || model.currentPage + 1 >= model.totalPages) {
|
|
return;
|
|
}
|
|
await listGroups(
|
|
workspaceId: workspaceId,
|
|
page: model.currentPage + 1,
|
|
name: model.searchQuery.isNotEmpty ? model.searchQuery : null,
|
|
);
|
|
}
|
|
|
|
Future<ApiResponse<List<RecipientGroupMember>>> listMembers(
|
|
String groupId,
|
|
) async {
|
|
model.isLoading = true;
|
|
if (!_disposed) notifyListeners();
|
|
try {
|
|
final raw = await http.get(
|
|
'/recipient-groups/members?recipientGroupId=$groupId',
|
|
);
|
|
final response = _unwrap(raw);
|
|
model.membersPage = PageableModel.fromJson(response);
|
|
final List<dynamic> content;
|
|
if (response is List) {
|
|
content = response;
|
|
} else if (response is Map && response.containsKey('content')) {
|
|
content = response['content'] as List<dynamic>;
|
|
} else {
|
|
content = [];
|
|
}
|
|
model.members = content
|
|
.map((e) => RecipientGroupMember.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
model.isLoading = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.success(model.members);
|
|
} catch (e, s) {
|
|
logger.e(e);
|
|
logger.e(s);
|
|
model.errorMessage = e.toString();
|
|
model.members = [];
|
|
model.isLoading = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.failure('Failed to load members');
|
|
}
|
|
}
|
|
|
|
Future<ApiResponse<RecipientGroup>> createGroup(
|
|
CreateGroupRequest req,
|
|
) async {
|
|
model.isLoading = true;
|
|
if (!_disposed) notifyListeners();
|
|
try {
|
|
final raw = await http.post('/recipient-groups', req.toJson());
|
|
final response = _unwrap(raw);
|
|
final group = RecipientGroup.fromJson(response as Map<String, dynamic>);
|
|
model.groups = [group, ...model.groups];
|
|
model.isLoading = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.success(group);
|
|
} catch (e) {
|
|
logger.e(e);
|
|
model.errorMessage = e.toString();
|
|
model.isLoading = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.failure('Failed to create group');
|
|
}
|
|
}
|
|
|
|
Future<void> deleteGroup({
|
|
required String groupId,
|
|
required String workspaceId,
|
|
}) async {
|
|
try {
|
|
await http.delete(
|
|
'/recipient-groups/delete/$groupId?workspaceId=$workspaceId',
|
|
);
|
|
model.groups.removeWhere((g) => g.id == groupId);
|
|
model.selectedGroupIds.remove(groupId);
|
|
if (!_disposed) notifyListeners();
|
|
} catch (e) {
|
|
logger.e(e);
|
|
model.errorMessage = e.toString();
|
|
if (!_disposed) notifyListeners();
|
|
}
|
|
}
|
|
|
|
void toggleSelectMode() {
|
|
model.selectMode = !model.selectMode;
|
|
if (!model.selectMode) {
|
|
model.selectedGroupIds.clear();
|
|
}
|
|
if (!_disposed) notifyListeners();
|
|
}
|
|
|
|
void toggleGroupSelection(String groupId) {
|
|
if (model.selectedGroupIds.contains(groupId)) {
|
|
model.selectedGroupIds.remove(groupId);
|
|
} else {
|
|
model.selectedGroupIds.add(groupId);
|
|
}
|
|
if (!_disposed) notifyListeners();
|
|
}
|
|
|
|
Future<void> removeMember({
|
|
required String groupId,
|
|
required String recipientId,
|
|
required String workspaceId,
|
|
}) async {
|
|
try {
|
|
await http.delete(
|
|
'/recipient-groups/$groupId/members/$recipientId?workspaceId=$workspaceId',
|
|
);
|
|
model.members.removeWhere((m) => m.id == recipientId);
|
|
if (!_disposed) notifyListeners();
|
|
} catch (e) {
|
|
logger.e(e);
|
|
model.errorMessage = e.toString();
|
|
if (!_disposed) notifyListeners();
|
|
}
|
|
}
|
|
|
|
Future<void> deleteSelectedGroups({required String workspaceId}) async {
|
|
final idsToDelete = List<String>.from(model.selectedGroupIds);
|
|
model.selectMode = false;
|
|
model.selectedGroupIds.clear();
|
|
if (!_disposed) notifyListeners();
|
|
|
|
for (final id in idsToDelete) {
|
|
await deleteGroup(groupId: id, workspaceId: workspaceId);
|
|
}
|
|
}
|
|
|
|
Future<ApiResponse<CreateGroupFromFileResponse>> createGroupFromFile({
|
|
required String groupName,
|
|
required String workspaceId,
|
|
required String userId,
|
|
String? description,
|
|
required String currency,
|
|
required List<int> fileBytes,
|
|
required String fileName,
|
|
}) async {
|
|
model.isLoading = true;
|
|
if (!_disposed) notifyListeners();
|
|
|
|
try {
|
|
final fields = <String, String>{
|
|
'groupName': groupName,
|
|
'workspaceId': workspaceId,
|
|
'userId': userId,
|
|
'currency': currency,
|
|
};
|
|
if (description != null && description.isNotEmpty) {
|
|
fields['description'] = description;
|
|
}
|
|
|
|
final raw = await http.multipartPost(
|
|
'/recipient-groups/create-from-file',
|
|
fields,
|
|
'file',
|
|
fileBytes,
|
|
fileName,
|
|
);
|
|
final response = _unwrap(raw);
|
|
final result = CreateGroupFromFileResponse.fromJson(
|
|
response as Map<String, dynamic>,
|
|
);
|
|
|
|
model.isLoading = false;
|
|
if (!_disposed) notifyListeners();
|
|
|
|
return ApiResponse.success(result);
|
|
} catch (e) {
|
|
logger.e(e);
|
|
model.errorMessage = e.toString();
|
|
model.isLoading = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.failure('Failed to create group from file');
|
|
}
|
|
}
|
|
}
|