OpenContacts/lib/widgets/my_profile_dialog.dart
2024-02-23 16:34:22 -05:00

288 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:recon/apis/user_api.dart';
import 'package:recon/auxiliary.dart';
import 'package:recon/blend_mask.dart';
import 'package:recon/client_holder.dart';
import 'package:recon/models/personal_profile.dart';
import 'package:recon/widgets/default_error_widget.dart';
import 'package:recon/widgets/generic_avatar.dart';
class MyProfileDialog extends StatefulWidget {
const MyProfileDialog({super.key});
@override
State<MyProfileDialog> createState() => _MyProfileDialogState();
}
class _MyProfileDialogState extends State<MyProfileDialog> {
ClientHolder? _clientHolder;
Future<PersonalProfile>? _personalProfileFuture;
Future<StorageQuota>? _storageQuotaFuture;
@override
void didChangeDependencies() async {
super.didChangeDependencies();
final clientHolder = ClientHolder.of(context);
if (_clientHolder != clientHolder) {
_clientHolder = clientHolder;
final apiClient = _clientHolder!.apiClient;
_personalProfileFuture = UserApi.getPersonalProfile(apiClient);
_storageQuotaFuture = UserApi.getStorageQuota(apiClient);
}
}
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
DateFormat dateFormat = DateFormat.yMd();
return Dialog(
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
backgroundColor: const Color(0xFF11151D),
child: FutureBuilder(
future: _personalProfileFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
final profile = snapshot.data as PersonalProfile;
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Color(0x6611151D),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GenericAvatar(
imageUri: Aux.resdbToHttp(profile.userProfile.iconUrl),
radius: 32,
),
const SizedBox(
width: 12,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(profile.username, style: textTheme.titleLarge),
Text(
profile.accountType.label,
style: textTheme.labelMedium?.copyWith(color: profile.accountType.color),
),
],
),
],
),
),
const SizedBox(
height: 12,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Account Info",
style: textTheme.titleMedium,
),
const SizedBox(
height: 8,
),
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
color: const Color(0x6611151D),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"User ID",
style: textTheme.titleSmall,
),
Text(profile.id,
style: textTheme.bodySmall?.copyWith(color: const Color(0xFFE1E1E0)))
],
),
const SizedBox(
height: 8,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Email",
style: textTheme.titleSmall,
),
Text(profile.email,
style: textTheme.bodySmall?.copyWith(color: const Color(0xFFE1E1E0)))
],
),
const SizedBox(
height: 8,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Patreon Supporter",
style: textTheme.titleSmall,
),
Text(profile.isPatreonSupporter ? "Yes" : "No",
style: textTheme.bodySmall?.copyWith(color: const Color(0xFFE1E1E0)))
],
),
const SizedBox(
height: 8,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Two Factor Authentication",
style: textTheme.titleSmall,
),
Text(profile.twoFactor ? "Enabled" : "Disabled",
style: textTheme.bodySmall?.copyWith(color: const Color(0xFFE1E1E0)))
],
),
])),
),
],
),
),
if (profile.publicBanExpiration?.isAfter(DateTime.now()) ?? false)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Ban Expiration: ",
style: textTheme.labelLarge,
),
Text(dateFormat.format(profile.publicBanExpiration!))
],
),
const SizedBox(
height: 12,
),
FutureBuilder(
future: _storageQuotaFuture,
builder: (context, snapshot) {
final storage = snapshot.data;
return StorageIndicator(
usedBytes: storage?.usedBytes ?? 0,
maxBytes: storage?.fullQuotaBytes ?? 1,
);
}),
],
),
);
} else if (snapshot.hasError) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
DefaultErrorWidget(
message: snapshot.error.toString(),
onRetry: () {
setState(() {
_personalProfileFuture = UserApi.getPersonalProfile(ClientHolder.of(context).apiClient);
});
},
),
],
);
} else {
return const Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.symmetric(vertical: 96, horizontal: 64),
child: CircularProgressIndicator(),
),
],
);
}
},
),
);
}
}
class StorageIndicator extends StatelessWidget {
const StorageIndicator({required this.usedBytes, required this.maxBytes, super.key});
final int usedBytes;
final int maxBytes;
@override
Widget build(BuildContext context) {
final value = usedBytes / maxBytes;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Storage", style: Theme.of(context).textTheme.titleMedium),
const SizedBox(
height: 8,
),
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Stack(children: [
LinearProgressIndicator(
value: value,
minHeight: 48,
color: value > 0.95 ? Theme.of(context).colorScheme.error : const Color(0xFF61D1FA),
backgroundColor: const Color(0xFF284C5D),
),
Container(
height: 48,
padding: const EdgeInsets.symmetric(horizontal: 12),
alignment: Alignment.center,
child: BlendMask(
blendMode: BlendMode.colorDodge,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"${(value * 100).toStringAsFixed(0)}%",
style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// Displayed in GiB instead of GB for consistency with Resonite
Text(
"${(usedBytes * 9.3132257461548e-10).toStringAsFixed(2)} GB of ${(maxBytes * 9.3132257461548e-10).toStringAsFixed(2)} GB"),
Text("Storage Space Used",
style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 10)),
]),
],
),
)),
]),
)
],
),
);
}
}