Merge pull request #743 from ente-io/user_details
Render cached user details while fetch is in progress
This commit is contained in:
commit
e2f1879f27
|
@ -1,3 +1,5 @@
|
|||
import 'dart:convert';
|
||||
|
||||
const freeProductID = "free";
|
||||
const stripe = "stripe";
|
||||
const appStore = "appstore";
|
||||
|
@ -43,10 +45,28 @@ class Subscription {
|
|||
price: map['price'],
|
||||
period: map['period'],
|
||||
attributes: map["attributes"] != null
|
||||
? Attributes.fromJson(map["attributes"])
|
||||
? Attributes.fromMap(map["attributes"])
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'productID': productID,
|
||||
'storage': storage,
|
||||
'originalTransactionID': originalTransactionID,
|
||||
'paymentProvider': paymentProvider,
|
||||
'expiryTime': expiryTime,
|
||||
'price': price,
|
||||
'period': period,
|
||||
'attributes': attributes?.toMap(),
|
||||
};
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory Subscription.fromJson(String source) =>
|
||||
Subscription.fromMap(json.decode(source));
|
||||
}
|
||||
|
||||
class Attributes {
|
||||
|
@ -58,8 +78,22 @@ class Attributes {
|
|||
this.customerID,
|
||||
});
|
||||
|
||||
Attributes.fromJson(dynamic json) {
|
||||
isCancelled = json["isCancelled"];
|
||||
customerID = json["customerID"];
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'isCancelled': isCancelled,
|
||||
'customerID': customerID,
|
||||
};
|
||||
}
|
||||
|
||||
factory Attributes.fromMap(Map<String, dynamic> map) {
|
||||
return Attributes(
|
||||
isCancelled: map['isCancelled'],
|
||||
customerID: map['customerID'],
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory Attributes.fromJson(String source) =>
|
||||
Attributes.fromMap(json.decode(source));
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
@ -62,6 +63,22 @@ class UserDetails {
|
|||
FamilyData.fromMap(map['familyData']),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'email': email,
|
||||
'usage': usage,
|
||||
'fileCount': fileCount,
|
||||
'sharedCollectionsCount': sharedCollectionsCount,
|
||||
'subscription': subscription.toMap(),
|
||||
'familyData': familyData?.toMap(),
|
||||
};
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory UserDetails.fromJson(String source) =>
|
||||
UserDetails.fromMap(json.decode(source));
|
||||
}
|
||||
|
||||
class FamilyMember {
|
||||
|
@ -70,7 +87,12 @@ class FamilyMember {
|
|||
final String id;
|
||||
final bool isAdmin;
|
||||
|
||||
FamilyMember(this.email, this.usage, this.id, this.isAdmin);
|
||||
FamilyMember(
|
||||
this.email,
|
||||
this.usage,
|
||||
this.id,
|
||||
this.isAdmin,
|
||||
);
|
||||
|
||||
factory FamilyMember.fromMap(Map<String, dynamic> map) {
|
||||
return FamilyMember(
|
||||
|
@ -80,6 +102,20 @@ class FamilyMember {
|
|||
map['isAdmin'] as bool,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'email': email,
|
||||
'usage': usage,
|
||||
'id': id,
|
||||
'isAdmin': isAdmin,
|
||||
};
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory FamilyMember.fromJson(String source) => FamilyMember.fromMap(json.decode(source));
|
||||
}
|
||||
|
||||
class FamilyData {
|
||||
|
@ -89,7 +125,11 @@ class FamilyData {
|
|||
final int storage;
|
||||
final int expiryTime;
|
||||
|
||||
FamilyData(this.members, this.storage, this.expiryTime);
|
||||
FamilyData(
|
||||
this.members,
|
||||
this.storage,
|
||||
this.expiryTime,
|
||||
);
|
||||
|
||||
int getTotalUsage() {
|
||||
return members!.map((e) => e.usage).toList().sum;
|
||||
|
@ -107,6 +147,18 @@ class FamilyData {
|
|||
map['expiryTime'] as int,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'members': members?.map((x) => x.toMap()).toList(),
|
||||
'storage': storage,
|
||||
'expiryTime': expiryTime,
|
||||
};
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory FamilyData.fromJson(String source) => FamilyData.fromMap(json.decode(source));
|
||||
}
|
||||
|
||||
class FilesCount {
|
||||
|
|
|
@ -39,6 +39,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
|
||||
class UserService {
|
||||
static const keyHasEnabledTwoFactor = "has_enabled_two_factor";
|
||||
static const keyUserDetails = "user_details";
|
||||
final _dio = Network.instance.getDio();
|
||||
final _enteDio = Network.instance.enteDio;
|
||||
final _logger = Logger((UserService).toString());
|
||||
|
@ -134,7 +135,18 @@ class UserService {
|
|||
}
|
||||
}
|
||||
|
||||
Future<UserDetails> getUserDetailsV2({bool memoryCount = true}) async {
|
||||
UserDetails getCachedUserDetails() {
|
||||
if (_preferences.containsKey(keyUserDetails)) {
|
||||
return UserDetails.fromJson(_preferences.getString(keyUserDetails));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<UserDetails> getUserDetailsV2({
|
||||
bool memoryCount = true,
|
||||
bool shouldCache = false,
|
||||
}) async {
|
||||
_logger.info("Fetching user details");
|
||||
try {
|
||||
final response = await _enteDio.get(
|
||||
"/users/details/v2",
|
||||
|
@ -142,7 +154,12 @@ class UserService {
|
|||
"memoryCount": memoryCount,
|
||||
},
|
||||
);
|
||||
return UserDetails.fromMap(response.data);
|
||||
final userDetails = UserDetails.fromMap(response.data);
|
||||
if (shouldCache) {
|
||||
await _preferences.setString(keyUserDetails, userDetails.toJson());
|
||||
}
|
||||
_logger.info("User details fetched: " + userDetails.toJson());
|
||||
return userDetails;
|
||||
} on DioError catch (e) {
|
||||
_logger.info(e);
|
||||
rethrow;
|
||||
|
|
|
@ -19,20 +19,16 @@ class UserDetailsStateWidget extends StatefulWidget {
|
|||
}
|
||||
|
||||
class UserDetailsStateWidgetState extends State<UserDetailsStateWidget> {
|
||||
late UserDetails? userDetails;
|
||||
late UserDetails? _userDetails;
|
||||
late StreamSubscription<OpenedSettingsEvent> _openedSettingsEventSubscription;
|
||||
bool _isCached = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
userDetails = null;
|
||||
_userDetails = UserService.instance.getCachedUserDetails();
|
||||
_openedSettingsEventSubscription =
|
||||
Bus.instance.on<OpenedSettingsEvent>().listen((event) {
|
||||
Future.delayed(
|
||||
const Duration(
|
||||
milliseconds: 750,
|
||||
),
|
||||
_fetchUserDetails,
|
||||
);
|
||||
_fetchUserDetails();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
@ -46,13 +42,17 @@ class UserDetailsStateWidgetState extends State<UserDetailsStateWidget> {
|
|||
@override
|
||||
Widget build(BuildContext context) => InheritedUserDetails(
|
||||
userDetailsState: this,
|
||||
userDetails: userDetails,
|
||||
userDetails: _userDetails,
|
||||
isCached: _isCached,
|
||||
child: widget.child,
|
||||
);
|
||||
|
||||
void _fetchUserDetails() async {
|
||||
userDetails =
|
||||
await UserService.instance.getUserDetailsV2(memoryCount: true);
|
||||
_userDetails = await UserService.instance.getUserDetailsV2(
|
||||
memoryCount: true,
|
||||
shouldCache: true,
|
||||
);
|
||||
_isCached = false;
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
|
@ -62,11 +62,13 @@ class UserDetailsStateWidgetState extends State<UserDetailsStateWidget> {
|
|||
class InheritedUserDetails extends InheritedWidget {
|
||||
final UserDetailsStateWidgetState userDetailsState;
|
||||
final UserDetails? userDetails;
|
||||
final bool isCached;
|
||||
|
||||
const InheritedUserDetails({
|
||||
Key? key,
|
||||
required Widget child,
|
||||
required this.userDetails,
|
||||
required this.isCached,
|
||||
required this.userDetailsState,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
|
@ -76,6 +78,7 @@ class InheritedUserDetails extends InheritedWidget {
|
|||
@override
|
||||
bool updateShouldNotify(covariant InheritedUserDetails oldWidget) {
|
||||
return (userDetails?.usage != oldWidget.userDetails?.usage) ||
|
||||
(userDetails?.fileCount != oldWidget.userDetails?.fileCount);
|
||||
(userDetails?.fileCount != oldWidget.userDetails?.fileCount) ||
|
||||
(isCached != oldWidget.isCached);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,12 @@ class SettingsTitleBarWidget extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final logger = Logger((SettingsTitleBarWidget).toString());
|
||||
final userDetails = InheritedUserDetails.of(context)?.userDetails;
|
||||
final inheritedDetails = InheritedUserDetails.of(context);
|
||||
final userDetails = inheritedDetails?.userDetails;
|
||||
bool isCached = false;
|
||||
if (inheritedDetails != null) {
|
||||
isCached = inheritedDetails.isCached;
|
||||
}
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Padding(
|
||||
|
@ -27,7 +32,7 @@ class SettingsTitleBarWidget extends StatelessWidget {
|
|||
},
|
||||
icon: const Icon(Icons.keyboard_double_arrow_left_outlined),
|
||||
),
|
||||
userDetails is UserDetails
|
||||
userDetails is UserDetails && !isCached
|
||||
? Text(
|
||||
"${NumberFormat().format(userDetails.fileCount)} memories",
|
||||
style: getEnteTextTheme(context).largeBold,
|
||||
|
|
Loading…
Reference in a new issue