|
...
|
...
|
@@ -49,109 +49,47 @@ class FriendsTab extends GetView<FriendsController> { |
|
|
|
final stressValue = stressScore?.state == 0
|
|
|
|
? null
|
|
|
|
: stressScore?.comprehensiveScore?.toDouble();
|
|
|
|
|
|
|
|
return Stack(
|
|
|
|
children: [
|
|
|
|
RefreshIndicator(
|
|
|
|
onRefresh: controller.refreshData,
|
|
|
|
child: ListView(
|
|
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
|
|
padding: EdgeInsets.fromLTRB(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
hasFriends ? 188.dp : 172,
|
|
|
|
final selfHealthCard = _SelfHealthCard(
|
|
|
|
name: currentUser?.nickname?.trim().isNotEmpty == true
|
|
|
|
? currentUser!.nickname!.trim()
|
|
|
|
: '-',
|
|
|
|
avatarUrl: currentUser?.avatar,
|
|
|
|
updatedAt: updatedAt == null
|
|
|
|
? context.l10n.friendsWaitingForData
|
|
|
|
: context.l10n.friendsUpdatedAt(
|
|
|
|
DateFormat('HH:mm').format(updatedAt),
|
|
|
|
),
|
|
|
|
children: [
|
|
|
|
_SelfHealthCard(
|
|
|
|
name:
|
|
|
|
currentUser?.nickname?.trim().isNotEmpty ==
|
|
|
|
true
|
|
|
|
? currentUser!.nickname!.trim()
|
|
|
|
: '-',
|
|
|
|
avatarUrl: currentUser?.avatar,
|
|
|
|
updatedAt: updatedAt == null
|
|
|
|
? context.l10n.friendsWaitingForData
|
|
|
|
: context.l10n.friendsUpdatedAt(
|
|
|
|
DateFormat('HH:mm').format(updatedAt),
|
|
|
|
),
|
|
|
|
sleepQualityScore:
|
|
|
|
_normalizedScore(healthData?.sleepScore),
|
|
|
|
steps: healthData?.steps == null
|
|
|
|
? null
|
|
|
|
: context.l10n.friendsStepCount(
|
|
|
|
healthData!.steps!,
|
|
|
|
),
|
|
|
|
statusText: stressValue == null
|
|
|
|
? context.l10n.friendsWaitingForData
|
|
|
|
: HrvStressLevel.fromRealtimeStress(
|
|
|
|
stressValue,
|
|
|
|
).label,
|
|
|
|
stressValue: stressValue,
|
|
|
|
isLoading: isSelfInitialLoading,
|
|
|
|
),
|
|
|
|
if (isFriendsInitialLoading)
|
|
|
|
const _FriendsLoadingIndicator()
|
|
|
|
else if (!hasFriends)
|
|
|
|
const _EmptyFriendsView()
|
|
|
|
else ...[
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
...friends.map(
|
|
|
|
(friend) => Padding(
|
|
|
|
padding: const EdgeInsets.fromLTRB(
|
|
|
|
16,
|
|
|
|
0,
|
|
|
|
16,
|
|
|
|
12,
|
|
|
|
),
|
|
|
|
child: FriendHealthCard(
|
|
|
|
name: friend.name,
|
|
|
|
remark: friend.remark,
|
|
|
|
avatarUrl: friend.avatarUrl,
|
|
|
|
updatedAt: friend.updatedAt,
|
|
|
|
sleepQualityScore:
|
|
|
|
friend.sleepQualityScore,
|
|
|
|
steps: friend.steps,
|
|
|
|
statusText: friend.statusText,
|
|
|
|
stressValue: friend.stressValue,
|
|
|
|
isOnWatchFace: friend.isOnWatchFace,
|
|
|
|
onTap: () => _openFriendHome(friend),
|
|
|
|
onMoreSelected: (action) {
|
|
|
|
_handleFriendAction(action, friend);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
if (!hasFriends && !isFriendsInitialLoading) ...[
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
_AddFriendButton(
|
|
|
|
enabled: !isFull,
|
|
|
|
onTap: isFull ? null : _handleAddFriend,
|
|
|
|
),
|
|
|
|
if (isFull) const _FullFriendsTip(),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
if (hasFriends)
|
|
|
|
Positioned(
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
bottom: _floatingAddButtonBottom(context),
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
_AddFriendButton(
|
|
|
|
enabled: !isFull,
|
|
|
|
friendCount: friends.length,
|
|
|
|
maxFriends: FriendsController.maxFriends,
|
|
|
|
onTap: isFull ? null : _handleAddFriend,
|
|
|
|
),
|
|
|
|
if (isFull) const _FullFriendsTip(),
|
|
|
|
],
|
|
|
|
sleepQualityScore:
|
|
|
|
_normalizedScore(healthData?.sleepScore),
|
|
|
|
steps: healthData?.steps == null
|
|
|
|
? null
|
|
|
|
: context.l10n.friendsStepCount(
|
|
|
|
healthData!.steps!,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
statusText: stressValue == null
|
|
|
|
? context.l10n.friendsWaitingForData
|
|
|
|
: HrvStressLevel.fromRealtimeStress(
|
|
|
|
stressValue,
|
|
|
|
).label,
|
|
|
|
stressValue: stressValue,
|
|
|
|
isLoading: isSelfInitialLoading,
|
|
|
|
);
|
|
|
|
|
|
|
|
if (isFriendsInitialLoading) {
|
|
|
|
return _buildLoadingFriendsView(context, selfHealthCard);
|
|
|
|
}
|
|
|
|
if (!hasFriends) {
|
|
|
|
return _buildEmptyFriendsView(
|
|
|
|
context,
|
|
|
|
selfHealthCard,
|
|
|
|
isFull: isFull,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return _buildFriendsListView(
|
|
|
|
context,
|
|
|
|
selfHealthCard,
|
|
|
|
friends: friends,
|
|
|
|
isFull: isFull,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
|
...
|
...
|
@@ -163,13 +101,127 @@ class FriendsTab extends GetView<FriendsController> { |
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildLoadingFriendsView(
|
|
|
|
BuildContext context,
|
|
|
|
Widget selfHealthCard,
|
|
|
|
) {
|
|
|
|
return RefreshIndicator(
|
|
|
|
onRefresh: controller.refreshData,
|
|
|
|
child: ListView(
|
|
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
|
|
padding: EdgeInsets.fromLTRB(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
_emptyListBottomPadding(context),
|
|
|
|
),
|
|
|
|
children: [
|
|
|
|
selfHealthCard,
|
|
|
|
const _FriendsLoadingIndicator(),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildEmptyFriendsView(
|
|
|
|
BuildContext context,
|
|
|
|
Widget selfHealthCard, {
|
|
|
|
required bool isFull,
|
|
|
|
}) {
|
|
|
|
return RefreshIndicator(
|
|
|
|
onRefresh: controller.refreshData,
|
|
|
|
child: ListView(
|
|
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
|
|
padding: EdgeInsets.fromLTRB(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
_emptyListBottomPadding(context),
|
|
|
|
),
|
|
|
|
children: [
|
|
|
|
selfHealthCard,
|
|
|
|
_EmptyFriendsView(
|
|
|
|
enabled: !isFull,
|
|
|
|
onTap: isFull ? null : _handleAddFriend,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildFriendsListView(
|
|
|
|
BuildContext context,
|
|
|
|
Widget selfHealthCard, {
|
|
|
|
required List<FriendHealthData> friends,
|
|
|
|
required bool isFull,
|
|
|
|
}) {
|
|
|
|
return Stack(
|
|
|
|
children: [
|
|
|
|
RefreshIndicator(
|
|
|
|
onRefresh: controller.refreshData,
|
|
|
|
child: ListView(
|
|
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
|
|
padding: EdgeInsets.fromLTRB(0, 0, 0, 188.dp),
|
|
|
|
children: [
|
|
|
|
selfHealthCard,
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
...friends.map(
|
|
|
|
(friend) => Padding(
|
|
|
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
|
|
|
child: FriendHealthCard(
|
|
|
|
name: friend.name,
|
|
|
|
remark: friend.remark,
|
|
|
|
avatarUrl: friend.avatarUrl,
|
|
|
|
updatedAt: friend.updatedAt,
|
|
|
|
sleepQualityScore: friend.sleepQualityScore,
|
|
|
|
steps: friend.steps,
|
|
|
|
statusText: friend.statusText,
|
|
|
|
stressValue: friend.stressValue,
|
|
|
|
isOnWatchFace: friend.isOnWatchFace,
|
|
|
|
onTap: () => _openFriendHome(friend),
|
|
|
|
onMoreSelected: (action) {
|
|
|
|
_handleFriendAction(action, friend);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Positioned(
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
bottom: _floatingAddButtonBottom(context),
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
_AddFriendButton(
|
|
|
|
enabled: !isFull,
|
|
|
|
friendCount: friends.length,
|
|
|
|
maxFriends: FriendsController.maxFriends,
|
|
|
|
onTap: isFull ? null : _handleAddFriend,
|
|
|
|
),
|
|
|
|
if (isFull) const _FullFriendsTip(),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
double _emptyListBottomPadding(BuildContext context) {
|
|
|
|
return _tabBarAvoidanceBottom(context) + 24;
|
|
|
|
}
|
|
|
|
|
|
|
|
double _floatingAddButtonBottom(BuildContext context) {
|
|
|
|
return _tabBarAvoidanceBottom(context) + 23.dp;
|
|
|
|
}
|
|
|
|
|
|
|
|
double _tabBarAvoidanceBottom(BuildContext context) {
|
|
|
|
const tabBarHeight = 56.0;
|
|
|
|
const tabBarBottomPadding = 8.0;
|
|
|
|
return MediaQuery.viewPaddingOf(context).bottom +
|
|
|
|
tabBarHeight +
|
|
|
|
tabBarBottomPadding +
|
|
|
|
23.dp;
|
|
|
|
tabBarBottomPadding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _handleAddFriend() async {
|
|
...
|
...
|
@@ -324,7 +376,13 @@ _navigatorToPrivacySettings() { |
|
|
|
}
|
|
|
|
|
|
|
|
class _EmptyFriendsView extends StatelessWidget {
|
|
|
|
const _EmptyFriendsView();
|
|
|
|
const _EmptyFriendsView({
|
|
|
|
required this.enabled,
|
|
|
|
this.onTap,
|
|
|
|
});
|
|
|
|
|
|
|
|
final bool enabled;
|
|
|
|
final VoidCallback? onTap;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
...
|
...
|
@@ -351,6 +409,12 @@ class _EmptyFriendsView extends StatelessWidget { |
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
_AddFriendButton(
|
|
|
|
enabled: enabled,
|
|
|
|
onTap: onTap,
|
|
|
|
),
|
|
|
|
if (!enabled) const _FullFriendsTip(),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
...
|
...
|
|