Skip to content
Snippets Groups Projects
Commit 8d14778f authored by vjrj's avatar vjrj
Browse files

Improve contact page and certifications page

parent d3b690e6
No related branches found
No related tags found
No related merge requests found
import 'package:copy_with_extension/copy_with_extension.dart';
import '../../ui/ui_helpers.dart';
import 'contact.dart';
part 'contact_wot_info.g.dart';
@CopyWith()
class ContactWotInfo {
ContactWotInfo({
required this.me,
......@@ -21,6 +26,7 @@ class ContactWotInfo {
bool? distRuleOk;
double? distRuleRatio;
int? currentBlockHeight;
bool loaded = false;
bool get isme => isMe(you, me.pubKey);
......
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'contact_wot_info.dart';
// **************************************************************************
// CopyWithGenerator
// **************************************************************************
abstract class _$ContactWotInfoCWProxy {
ContactWotInfo me(Contact me);
ContactWotInfo you(Contact you);
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContactWotInfo(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// ContactWotInfo(...).copyWith(id: 12, name: "My name")
/// ````
ContactWotInfo call({
Contact? me,
Contact? you,
});
}
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfContactWotInfo.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfContactWotInfo.copyWith.fieldName(...)`
class _$ContactWotInfoCWProxyImpl implements _$ContactWotInfoCWProxy {
const _$ContactWotInfoCWProxyImpl(this._value);
final ContactWotInfo _value;
@override
ContactWotInfo me(Contact me) => this(me: me);
@override
ContactWotInfo you(Contact you) => this(you: you);
@override
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContactWotInfo(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// ContactWotInfo(...).copyWith(id: 12, name: "My name")
/// ````
ContactWotInfo call({
Object? me = const $CopyWithPlaceholder(),
Object? you = const $CopyWithPlaceholder(),
}) {
return ContactWotInfo(
me: me == const $CopyWithPlaceholder() || me == null
? _value.me
// ignore: cast_nullable_to_non_nullable
: me as Contact,
you: you == const $CopyWithPlaceholder() || you == null
? _value.you
// ignore: cast_nullable_to_non_nullable
: you as Contact,
);
}
}
extension $ContactWotInfoCopyWith on ContactWotInfo {
/// Returns a callable class that can be used as follows: `instanceOfContactWotInfo.copyWith(...)` or like so:`instanceOfContactWotInfo.copyWith.fieldName(...)`.
// ignore: library_private_types_in_public_api
_$ContactWotInfoCWProxy get copyWith => _$ContactWotInfoCWProxyImpl(this);
}
......@@ -183,11 +183,21 @@ Cert _buildCert(dynamic cert) {
issuerId: Contact.withAddress(
name: (issuer as dynamic).name as String,
createdOn: ((issuer as dynamic).account as dynamic).createdOn as int,
address: (issuer as dynamic).accountId as String),
address: (issuer as dynamic).accountId as String,
status: parseIdentityStatus(
((issuer as dynamic)?.status as dynamic)?.name as String?),
isMember: (issuer as dynamic)?.isMember as bool?,
expireOn: (issuer as dynamic).expireOn as int?,
index: (issuer as dynamic).index as int?),
receiverId: Contact.withAddress(
name: (receiver as dynamic).name as String,
createdOn: ((receiver as dynamic).account as dynamic).createdOn as int,
address: (receiver as dynamic).accountId as String),
address: (receiver as dynamic).accountId as String,
status: parseIdentityStatus(
((issuer as dynamic)?.status as dynamic)?.name as String?),
isMember: (receiver as dynamic)?.isMember as bool?,
expireOn: (receiver as dynamic).expireOn as int?,
index: (receiver as dynamic).index as int?),
createdOn: (cert as dynamic).createdOn as int,
expireOn: (cert as dynamic).expireOn as int,
isActive: (cert as dynamic).isActive as bool,
......
......@@ -421,3 +421,11 @@ Uint8List decryptAes(Uint8List encryptedData, Uint8List key) {
}
const Duration defPolkadotTimeout = Duration(seconds: 20);
// Based on duniter-vue
DateTime estimateDateFromBlock(
{required int futureBlock, required int currentBlockHeight}) {
const int millisPerBlock = 6000;
final int diff = futureBlock - currentBlockHeight;
return DateTime.now().add(Duration(milliseconds: diff * millisPerBlock));
}
......@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import '../../data/models/cert.dart';
import '../../data/models/contact.dart';
import '../contact_list_item.dart';
import '../ui_helpers.dart';
import 'contacts_actions.dart';
class CertificationsPage extends StatelessWidget {
......@@ -21,6 +20,7 @@ class CertificationsPage extends StatelessWidget {
final bool issued;
final int currentBlockHeight;
final List<Cert> certifications;
static const int limit = 201600;
@override
Widget build(BuildContext context) {
......@@ -51,27 +51,39 @@ class CertificationsPage extends StatelessWidget {
final Cert cert = certifications[index];
final Contact contact =
issued ? cert.receiverId : cert.issuerId;
// FIXME Use block estimation to calculate the cert date
String? certDate = inDevelopment
? humanizeTimeFull(
utcDateTime: DateTime.now()
.add(Duration(seconds: cert.updatedOn)),
locale: currentLocale(context))
: null;
certDate = null;
final bool isExpired = cert.expireOn <= currentBlockHeight;
final bool isExpiringSoon = cert.isActive &&
(cert.expireOn - currentBlockHeight < limit);
final bool isMember = contact.isMember ?? false;
/* final DateTime updateOn = estimateDateFromBlock(
futureBlock: cert.updatedOn,
currentBlockHeight: currentBlockHeight); */
/* final String certDate = humanizeTimeFull(
locale: currentLocale(context), utcDateTime: updateOn); */
final String statusMsg =
tr('idty_status_${contact.status!.name}');
return ContactListItem(
contact: contact,
subtitleExtra: certDate,
// subtitleExtra: statusMsg,
index: index,
isV2: true,
onTap: () {
showContactPage(context, contact);
},
trailing: cert.expireOn > 0
? Text(humanizeTimeFuture(
currentLocale(context), cert.expireOn) ??
'')
: Text(tr('cert_expired')));
trailing: Tooltip(
message: statusMsg,
child: Icon(
isMember
? isExpiringSoon
? Icons.timelapse
: Icons.check_circle_outline
: Icons.warning_amber_outlined,
color: isMember
? isExpiringSoon
? Colors.orange.shade300
: Theme.of(context).colorScheme.primary
: Colors.red.shade300,
)));
},
),
)
......
......@@ -20,6 +20,7 @@ import '../../g1/api.dart';
import '../../g1/distance_precompute.dart';
import '../../g1/distance_precompute_provider.dart';
import '../../g1/duniter_indexer_helper.dart';
import '../../g1/g1_helper.dart';
import '../../g1/g1_v2_helper.dart';
import '../../g1/sing_and_send.dart';
import '../../g1/wot_actions.dart';
......@@ -70,26 +71,25 @@ class _ContactPageState extends State<ContactPage> {
@override
Widget build(BuildContext context) {
final Contact contact = widget.contact;
return FutureBuilder<ContactWotInfo>(
future: _getWotInfo(),
final Contact me = Contact(pubKey: SharedPreferencesHelper().getPubKey());
return StreamBuilder<ContactWotInfo>(
stream: _getWotInfo(),
initialData: ContactWotInfo(
me: me,
you: widget.contact,
),
builder: (BuildContext context, AsyncSnapshot<ContactWotInfo> snapshot) {
if (snapshot.hasData) {
return _buildContactWidget(snapshot.data!, context, true);
return _buildContactWidget(snapshot.requireData, context);
}
return _buildContactWidget(
ContactWotInfo(
me: Contact(pubKey: SharedPreferencesHelper().getPubKey()),
you: contact),
context,
false);
ContactWotInfo(me: me, you: widget.contact), context);
},
);
}
DefaultTabController _buildContactWidget(
ContactWotInfo contactWotInfo, BuildContext context, bool loaded) {
ContactWotInfo contactWotInfo, BuildContext context) {
final Contact contact = contactWotInfo.you;
final bool isContact =
context.read<ContactsCubit>().isContact(contact.pubKey);
......@@ -191,7 +191,7 @@ class _ContactPageState extends State<ContactPage> {
Expanded(
child: TabBarView(
children: <Widget>[
_buildInfoTab(contact, contactWotInfo, loaded),
_buildInfoTab(contact, contactWotInfo, contactWotInfo.loaded),
_buildTransactionsTab(contact),
],
),
......@@ -512,7 +512,7 @@ class _ContactPageState extends State<ContactPage> {
);
}
Future<ContactWotInfo> _getWotInfo() async {
Stream<ContactWotInfo> _getWotInfo() async* {
final Contact you =
await getProfile(widget.contact.pubKey, resize: false, complete: true);
final Contact me = await getProfile(SharedPreferencesHelper().getPubKey(),
......@@ -539,6 +539,8 @@ class _ContactPageState extends State<ContactPage> {
(await getIdentity(address: you.address)) != null;
wotInfo.canCreateIdty = iAmMember && enoughBalance && !identityUsed;
}
yield wotInfo;
final int currentBlock = await polkadotCurrentBlock();
wotInfo.currentBlockHeight = currentBlock;
final bool youAMember = you.isMember ?? false;
......@@ -546,11 +548,13 @@ class _ContactPageState extends State<ContactPage> {
final IdtyValue? youIdty = await polkadotIdentity(you);
final IdtyCertMeta? youCertMeta = await polkadotIdtyCertMeta(you);
if (youIdty != null && youCertMeta != null) {
wotInfo.canCertOn = estimateDate(
wotInfo.canCertOn = estimateDateFromBlock(
futureBlock: youCertMeta.nextIssuableOn,
currentBlockHeight: currentBlock);
}
}
yield wotInfo;
// Can Certificate
final IdtyValue? myIdty = await polkadotIdentity(me);
final IdtyCertMeta? idtyCertMeta = await polkadotIdtyCertMeta(me);
......@@ -563,7 +567,10 @@ class _ContactPageState extends State<ContactPage> {
idtyCertMeta.nextIssuableOn < currentBlock &&
idtyCertMeta.issuedCount < Constants().maxByIssuer;
wotInfo.canCert = canCert;
} else {
wotInfo.canCert = false;
}
yield wotInfo;
// Waiting for Certifications
if (you.certsReceived != null &&
......@@ -574,9 +581,10 @@ class _ContactPageState extends State<ContactPage> {
} else {
wotInfo.waitingForCerts = false;
}
yield wotInfo;
final int membershipExpireOn = you.expireOn!;
wotInfo.expireOn = estimateDate(
wotInfo.expireOn = estimateDateFromBlock(
futureBlock: membershipExpireOn, currentBlockHeight: currentBlock);
// Can call distance
......@@ -592,6 +600,7 @@ class _ContactPageState extends State<ContactPage> {
} else {
wotInfo.canCalcDistance = false;
}
yield wotInfo;
// Can call distanceFor
if (!wotInfo.isme &&
......@@ -601,14 +610,16 @@ class _ContactPageState extends State<ContactPage> {
} else {
wotInfo.canCalcDistanceFor = false;
}
yield wotInfo;
final bool alreadyCert = you.certsReceived != null &&
you.certsReceived!.isNotEmpty &&
you.certsReceived!
.any((Cert cert) => cert.issuerId.pubKey == me.pubKey);
wotInfo.alreadyCert = alreadyCert;
yield wotInfo;
if (!mounted) {
return wotInfo;
yield wotInfo;
}
final AppCubit appCubit = context.read<AppCubit>();
DistancePrecompute? distancePrecompute = appCubit.distancePrecompute;
......@@ -632,7 +643,8 @@ class _ContactPageState extends State<ContactPage> {
wotInfo.distRuleRatio = distRuleRatio;
}
}
return wotInfo;
wotInfo.loaded = true;
yield wotInfo;
}
}
......@@ -653,14 +665,6 @@ Widget _buildBadge(BuildContext context, int count) {
);
}
// Based on duniter-vue
DateTime estimateDate(
{required int futureBlock, required int currentBlockHeight}) {
const int millisPerBlock = 6000;
final int diff = futureBlock - currentBlockHeight;
return DateTime.now().add(Duration(milliseconds: diff * millisPerBlock));
}
String _getIdentityStatusDescription(IdentityStatus status, bool waitingCerts) {
switch (status) {
case IdentityStatus.UNCONFIRMED:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment