2024-01-27 10:55:55 -04:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:intl/intl.dart';
|
2024-07-15 00:23:04 -04:00
|
|
|
import 'package:OpenContacts/apis/user_api.dart';
|
|
|
|
import 'package:OpenContacts/auxiliary.dart';
|
|
|
|
import 'package:OpenContacts/client_holder.dart';
|
|
|
|
import 'package:OpenContacts/models/personal_profile.dart';
|
|
|
|
import 'package:OpenContacts/widgets/default_error_widget.dart';
|
|
|
|
import 'package:OpenContacts/widgets/generic_avatar.dart';
|
2024-07-30 14:54:38 -04:00
|
|
|
import 'package:OpenContacts/models/users/friend.dart';
|
2023-05-05 09:05:06 -04:00
|
|
|
|
2023-05-17 02:10:52 -04:00
|
|
|
class MyProfileDialog extends StatefulWidget {
|
|
|
|
const MyProfileDialog({super.key});
|
2023-05-05 09:05:06 -04:00
|
|
|
|
2023-05-17 02:10:52 -04:00
|
|
|
@override
|
|
|
|
State<MyProfileDialog> createState() => _MyProfileDialogState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _MyProfileDialogState extends State<MyProfileDialog> {
|
|
|
|
ClientHolder? _clientHolder;
|
|
|
|
Future<PersonalProfile>? _personalProfileFuture;
|
2023-10-10 04:33:51 -04:00
|
|
|
Future<StorageQuota>? _storageQuotaFuture;
|
2023-05-17 02:10:52 -04:00
|
|
|
|
|
|
|
@override
|
|
|
|
void didChangeDependencies() async {
|
|
|
|
super.didChangeDependencies();
|
|
|
|
final clientHolder = ClientHolder.of(context);
|
|
|
|
if (_clientHolder != clientHolder) {
|
|
|
|
_clientHolder = clientHolder;
|
|
|
|
final apiClient = _clientHolder!.apiClient;
|
|
|
|
_personalProfileFuture = UserApi.getPersonalProfile(apiClient);
|
2023-10-10 04:33:51 -04:00
|
|
|
_storageQuotaFuture = UserApi.getStorageQuota(apiClient);
|
2023-05-17 02:10:52 -04:00
|
|
|
}
|
|
|
|
}
|
2024-07-25 17:02:29 -04:00
|
|
|
|
2023-05-05 09:05:06 -04:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2024-01-27 10:55:55 -04:00
|
|
|
final tt = Theme.of(context).textTheme;
|
2023-05-05 09:05:06 -04:00
|
|
|
DateFormat dateFormat = DateFormat.yMd();
|
|
|
|
return Dialog(
|
2023-05-17 02:10:52 -04:00
|
|
|
child: FutureBuilder(
|
|
|
|
future: _personalProfileFuture,
|
|
|
|
builder: (context, snapshot) {
|
|
|
|
if (snapshot.hasData) {
|
|
|
|
final profile = snapshot.data as PersonalProfile;
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.all(24),
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
|
|
children: [
|
|
|
|
Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text(profile.username, style: tt.titleLarge),
|
2024-01-27 10:55:55 -04:00
|
|
|
Text(
|
|
|
|
profile.email,
|
|
|
|
style:
|
|
|
|
tt.labelMedium?.copyWith(color: Theme.of(context).colorScheme.onSurface.withAlpha(150)),
|
|
|
|
)
|
2023-05-17 02:10:52 -04:00
|
|
|
],
|
|
|
|
),
|
2024-01-27 10:55:55 -04:00
|
|
|
GenericAvatar(
|
|
|
|
imageUri: Aux.resdbToHttp(profile.userProfile.iconUrl),
|
|
|
|
radius: 24,
|
|
|
|
)
|
2023-05-17 02:10:52 -04:00
|
|
|
],
|
|
|
|
),
|
2024-01-27 10:55:55 -04:00
|
|
|
const SizedBox(
|
|
|
|
height: 16,
|
|
|
|
),
|
2023-05-17 02:10:52 -04:00
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
2024-01-27 10:55:55 -04:00
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
"User ID: ",
|
|
|
|
style: tt.labelLarge,
|
|
|
|
),
|
|
|
|
Text(profile.id)
|
|
|
|
],
|
2023-05-17 02:10:52 -04:00
|
|
|
),
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
2024-01-27 10:55:55 -04:00
|
|
|
Text(
|
|
|
|
"2FA: ",
|
|
|
|
style: tt.labelLarge,
|
|
|
|
),
|
2023-05-17 02:10:52 -04:00
|
|
|
Text(profile.twoFactor ? "Enabled" : "Disabled")
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
2024-01-27 10:55:55 -04:00
|
|
|
Text(
|
|
|
|
"Patreon Supporter: ",
|
|
|
|
style: tt.labelLarge,
|
|
|
|
),
|
2023-05-17 02:10:52 -04:00
|
|
|
Text(profile.isPatreonSupporter ? "Yes" : "No")
|
|
|
|
],
|
|
|
|
),
|
|
|
|
if (profile.publicBanExpiration?.isAfter(DateTime.now()) ?? false)
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
2024-01-27 10:55:55 -04:00
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
"Ban Expiration: ",
|
|
|
|
style: tt.labelLarge,
|
|
|
|
),
|
|
|
|
Text(dateFormat.format(profile.publicBanExpiration!))
|
|
|
|
],
|
2023-05-17 02:10:52 -04:00
|
|
|
),
|
2023-10-10 04:33:51 -04:00
|
|
|
FutureBuilder(
|
2024-01-27 10:55:55 -04:00
|
|
|
future: _storageQuotaFuture,
|
|
|
|
builder: (context, snapshot) {
|
|
|
|
final storage = snapshot.data;
|
|
|
|
return StorageIndicator(
|
|
|
|
usedBytes: storage?.usedBytes ?? 0,
|
|
|
|
maxBytes: storage?.fullQuotaBytes ?? 1,
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
const SizedBox(
|
|
|
|
height: 12,
|
2023-10-10 04:33:51 -04:00
|
|
|
),
|
2023-05-17 02:10:52 -04:00
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
2024-01-27 10:55:55 -04:00
|
|
|
} else if (snapshot.hasError) {
|
2023-05-17 02:10:52 -04:00
|
|
|
return Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
DefaultErrorWidget(
|
|
|
|
message: snapshot.error.toString(),
|
|
|
|
onRetry: () {
|
|
|
|
setState(() {
|
2024-01-27 10:55:55 -04:00
|
|
|
_personalProfileFuture = UserApi.getPersonalProfile(ClientHolder.of(context).apiClient);
|
2023-05-17 02:10:52 -04:00
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return const Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
2023-05-05 09:05:06 -04:00
|
|
|
children: [
|
2023-05-17 02:10:52 -04:00
|
|
|
Padding(
|
|
|
|
padding: EdgeInsets.symmetric(vertical: 96, horizontal: 64),
|
|
|
|
child: CircularProgressIndicator(),
|
2023-05-05 09:05:06 -04:00
|
|
|
),
|
|
|
|
],
|
2023-05-17 02:10:52 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
2023-05-05 09:05:06 -04:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2024-01-27 10:55:55 -04:00
|
|
|
final value = usedBytes / maxBytes;
|
2023-05-05 09:05:06 -04:00
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.only(top: 16.0),
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
|
|
|
Text("Storage:", style: Theme.of(context).textTheme.titleMedium),
|
2024-01-27 10:55:55 -04:00
|
|
|
Text(// Displayed in GiB instead of GB for consistency with Resonite
|
|
|
|
"${(usedBytes * 9.3132257461548e-10).toStringAsFixed(2)}/${(maxBytes * 9.3132257461548e-10).toStringAsFixed(2)} GB"),
|
2023-05-05 09:05:06 -04:00
|
|
|
],
|
|
|
|
),
|
2024-01-27 10:55:55 -04:00
|
|
|
const SizedBox(
|
|
|
|
height: 8,
|
|
|
|
),
|
2023-05-05 09:05:06 -04:00
|
|
|
ClipRRect(
|
|
|
|
borderRadius: BorderRadius.circular(8),
|
|
|
|
child: LinearProgressIndicator(
|
|
|
|
minHeight: 12,
|
|
|
|
color: value > 0.95 ? Theme.of(context).colorScheme.error : null,
|
|
|
|
value: value,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2024-01-27 10:55:55 -04:00
|
|
|
}
|