Newer
Older
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
import '../../../data/models/contact.dart';
import '../../../data/models/contact_cubit.dart';
import '../../../data/models/contact_state.dart';
import '../../qr_manager.dart';
class ContactSearchPage extends StatefulWidget {
const ContactSearchPage({super.key, this.uri, this.forPayment = true});
State<ContactSearchPage> createState() => _ContactSearchPageState();
class _ContactSearchPageState extends State<ContactSearchPage> {
final TextEditingController _searchController = TextEditingController();
String _searchTerm = '';
if (_searchTerm.length < 3) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(tr('search_limitation'))),
);
return;
}
final bool isConnected = await ConnectivityWidgetWrapperWrapper.isConnected;
final ContactsCubit contactsCubit = context.read<ContactsCubit>();
_results = contactsCubit.search(_searchTerm);
if (inDevelopment) {
logger('Found: ${_results.length} in contacts');
}
if (isConnected) {
final Response cPlusResponse = await searchCPlusUser(_searchTerm);
if (cPlusResponse.statusCode != 404) {
// Add cplus users
final List<dynamic> hits = ((const JsonDecoder()
.convert(cPlusResponse.body) as Map<String, dynamic>)['hits']
as Map<String, dynamic>)['hits'] as List<dynamic>;
await contactFromResultSearch(hit as Map<String, dynamic>);
logger('Contact retrieved in c+ search $c');
ContactsCache().addContact(c);
setState(() {
_addIfNotPresent(c);
});
}
logger('Found: ${_results.length}');
if (isConnected) {
final List<Contact> wotResults = await searchWot(_searchTerm);
// ignore: prefer_foreach
for (final Contact c in wotResults) {
ContactsCache().addContact(c);
_addIfNotPresent(c);
// retrieve extra results with c+ profile
for (final Contact wotC in wotResults) {
final Contact cachedWotProfile =
if (cachedWotProfile.name == null) {
// Users without c+ profile
final Contact cPlusProfile =
await getProfile(cachedWotProfile.pubKey, true);
if (_results.isEmpty && validateKey(_searchTerm)) {
logger('$_searchTerm looks like a plain pub key');
final Contact contact = Contact(pubKey: _searchTerm);
setState(() {
_isLoading = false;
});
}
void _addIfNotPresent(Contact contact) {
if (_results
.where((Contact c) => c.pubKey == contact.pubKey)
.toList()
.isEmpty) {
_results.add(contact);
}
return FutureBuilder<NFCAvailability>(
future: FlutterNfcKit.nfcAvailability,
builder:
(BuildContext context, AsyncSnapshot<NFCAvailability> snapshot) {
final bool nft = hasNft(snapshot);
final PaymentCubit paymentCubit = context.read<PaymentCubit>();
return Scaffold(
appBar: AppBar(
title: Text(widget.forPayment
? tr('search_user_title')
: tr('search_user_title_in_contacts')),
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.inversePrimary,
if (nft)
IconButton(
icon: const Icon(Icons.nfc),
onPressed: () async {
final String? nfcUrl = await readNfcUrl();
if (nfcUrl is String &&
nfcUrl != null &&
nfcUrl != '-1') {
await _onKeyScanned(nfcUrl, paymentCubit);
if (!mounted) {
return;
}
Navigator.pop(context);
}
},
),
IconButton(
icon: const Icon(Icons.qr_code_scanner),
onPressed: () async {
final String? scannedKey =
await QrManager.qrScan(context);
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
if (scannedKey is String &&
scannedKey != null &&
scannedKey != '-1') {
await _onKeyScanned(scannedKey, paymentCubit);
if (!mounted) {
return;
}
Navigator.pop(context);
}
}),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
)
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextField(
controller: _searchController,
decoration: InputDecoration(
filled: true,
//fillColor: Colors.white,
labelText: tr('search_user'),
suffixIcon: IconButton(
icon: const Icon(Icons.search),
onPressed: () =>
),
),
onChanged: (String value) {
_searchTerm = value;
},
onSubmitted: (_) {
_search();
},
),
if (_isLoading)
const LoadingBox(simple: false)
else if (_searchTerm.isNotEmpty &&
_results.isEmpty &&
_isLoading)
const NoElements(text: 'nothing_found')
Expanded(
child: ListView.builder(
itemCount: _results.length,
itemBuilder: (BuildContext context, int index) {
final Contact contact = _results[index];
return FutureBuilder<Contact>(
future:
ContactsCache().getContact(contact.pubKey),
builder: (BuildContext context,
AsyncSnapshot<Contact> snapshot) {
Widget widget;
if (snapshot.hasData) {
widget = _buildItem(
snapshot.data!, index, context);
} else if (snapshot.hasError) {
widget = CustomErrorWidget(snapshot.error);
} else {
// Contact without wot
widget =
_buildItem(contact, index, context);
}
return widget;
});
}),
)
Future<void> _onKeyScanned(
String scannedKey, PaymentCubit paymentCubit) async {
if (!widget.forPayment) {
return;
}
final PaymentState? pay = parseScannedUri(scannedKey);
if (pay != null) {
logger('Scanned $pay');
_searchTerm = extractPublicKey(pay.contact!.pubKey);
await _search();
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(tr('qr_invalid_payment'))));
}
logger('QR result length ${_results.length}');
if (_results.length == 1 && pay != null) {
final Contact contact = _results[0];
final double? currentAmount = paymentCubit.state.amount;
paymentCubit.selectUser(contact);
if (pay.amount != null) {
paymentCubit.selectKeyAmount(contact, pay.amount);
} else {
paymentCubit.selectKeyAmount(contact, currentAmount);
}
if (pay.comment != null) {
paymentCubit.setComment(pay.comment);
}
} else if (_results.isEmpty) {
if (!mounted) {
return;
}
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(tr('cant_find_qr_contact'))));
@override
void initState() {
super.initState();
_handleUri(widget.uri);
}
Future<void> _handleUri(String? uri) async {
if (uri != null) {
final PaymentCubit paymentCubit = context.read<PaymentCubit>();
await _onKeyScanned(uri, paymentCubit);
if (mounted) {
Navigator.pop(context);
}
}
}
Widget _buildItem(Contact contact, int index, BuildContext context) {
builder: (BuildContext context, ContactsState state) {
return ContactFavIcon(
contact: contact, contactsCubit: context.read<ContactsCubit>());
}),