Files
sendico/frontend/pweb/lib/providers/address_book_recipient_form.dart
2026-01-29 19:22:30 +03:00

171 lines
5.3 KiB
Dart

import 'package:flutter/foundation.dart';
import 'package:pshared/data/mapper/recipient/recipient.dart';
import 'package:pshared/models/describable.dart';
import 'package:pshared/models/payment/methods/data.dart';
import 'package:pshared/models/payment/methods/type.dart';
import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/recipient/payment_method_draft.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/recipient/methods_cache.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pweb/models/seed_state.dart';
class AddressBookRecipientFormProvider extends ChangeNotifier {
RecipientMethodsCacheProvider? _methodsCache;
RecipientsProvider? _recipientsProvider;
final Recipient? _recipient;
final List<PaymentType> _supportedTypes;
final Map<PaymentType, List<RecipientMethodDraft>> _methods;
SeedState _seedState = SeedState.idle;
AddressBookRecipientFormProvider({
required List<PaymentType> supportedTypes,
Recipient? recipient,
}) : _recipient = recipient,
_supportedTypes = supportedTypes,
_methods = {
for (final type in supportedTypes) type: <RecipientMethodDraft>[],
};
void updateProviders({
required RecipientMethodsCacheProvider methodsCache,
required RecipientsProvider recipientsProvider,
}) {
_recipientsProvider = recipientsProvider;
if (identical(_methodsCache, methodsCache)) return;
_methodsCache?.removeListener(_handleCacheChange);
_methodsCache = methodsCache;
_methodsCache?.addListener(_handleCacheChange);
_maybeSeedFromCache();
}
List<PaymentType> get supportedTypes => List.unmodifiable(_supportedTypes);
Map<PaymentType, List<RecipientMethodDraft>> get methods => {
for (final entry in _methods.entries)
entry.key: List<RecipientMethodDraft>.unmodifiable(entry.value),
};
PaymentType? get preferredType =>
_supportedTypes.firstWhere((type) => _methods[type]?.isNotEmpty == true, orElse: () => _supportedTypes.first);
bool get hasAnyMethod => _methods.values.any(
(entries) => entries.any((entry) => entry.data != null || entry.existing != null),
);
List<RecipientMethodDraft> allDrafts() =>
_methods.values.expand((entries) => entries).toList();
int? addMethod(PaymentType type) {
final entries = _methods[type];
if (entries == null) return null;
entries.add(RecipientMethodDraft(type: type));
notifyListeners();
return entries.length - 1;
}
void removeMethod(PaymentType type, int index) {
final entries = _methods[type];
if (entries == null) return;
if (index < 0 || index >= entries.length) return;
entries.removeAt(index);
notifyListeners();
}
void updateMethod(PaymentType type, int index, PaymentMethodData data) {
final entries = _methods[type];
if (entries == null) return;
if (index < 0 || index >= entries.length) return;
entries[index].data = data;
notifyListeners();
}
void _handleCacheChange() {
_maybeSeedFromCache();
}
void _maybeSeedFromCache() {
final recipient = _recipient;
final methodsCache = _methodsCache;
if (recipient == null || methodsCache == null) return;
if (_seedState == SeedState.seeded) return;
if (!methodsCache.hasMethodsFor(recipient.id)) return;
_seedState = SeedState.seeded;
_seedMethodsFromExisting(methodsCache.methodsForRecipient(recipient.id));
}
void _seedMethodsFromExisting(List<PaymentMethod> existing) {
if (existing.isEmpty) return;
final next = <PaymentType, List<RecipientMethodDraft>>{
for (final type in _supportedTypes) type: <RecipientMethodDraft>[],
};
for (final method in existing) {
final type = method.type;
final entries = next[type];
if (entries == null) continue;
entries.add(
RecipientMethodDraft(
type: type,
existing: method,
data: method.data,
),
);
}
_methods
..clear()
..addAll(next);
notifyListeners();
}
Future<Recipient> save({
required String name,
required String email,
required Map<PaymentType, String> methodNames,
}) async {
final recipientsProvider = _recipientsProvider;
final methodsCache = _methodsCache;
if (recipientsProvider == null || methodsCache == null) {
throw StateError('Form provider dependencies are not set');
}
final trimmedName = name.trim();
final trimmedEmail = email.trim();
if (_recipient == null) {
final created = await recipientsProvider.create(
name: trimmedName,
email: trimmedEmail,
);
await methodsCache.syncRecipientMethods(
recipientId: created.id,
methods: allDrafts(),
names: methodNames,
);
return created;
}
final updated = _recipient.copyWith(
describable: newDescribable(
name: trimmedName,
description: _recipient.description,
),
email: trimmedEmail,
);
await recipientsProvider.update(updated.toDTO().toJson());
await methodsCache.syncRecipientMethods(
recipientId: updated.id,
methods: allDrafts(),
names: methodNames,
);
return updated;
}
@override
void dispose() {
_methodsCache?.removeListener(_handleCacheChange);
super.dispose();
}
}