2024-08-10 16:36:27 +00:00
|
|
|
import 'dart:math' as math;
|
|
|
|
|
2024-07-12 03:39:44 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2024-07-12 05:15:46 +00:00
|
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:solian/models/account_status.dart';
|
|
|
|
import 'package:solian/providers/account_status.dart';
|
|
|
|
import 'package:solian/providers/auth.dart';
|
2024-08-02 15:15:28 +00:00
|
|
|
import 'package:solian/providers/relation.dart';
|
2024-07-12 03:39:44 +00:00
|
|
|
import 'package:solian/router.dart';
|
|
|
|
import 'package:solian/shells/root_shell.dart';
|
2024-07-12 15:43:41 +00:00
|
|
|
import 'package:solian/theme.dart';
|
2024-07-12 05:15:46 +00:00
|
|
|
import 'package:solian/widgets/account/account_avatar.dart';
|
|
|
|
import 'package:solian/widgets/account/account_status_action.dart';
|
2024-07-12 03:39:44 +00:00
|
|
|
import 'package:solian/widgets/navigation/app_navigation.dart';
|
2024-07-12 05:15:46 +00:00
|
|
|
import 'package:badges/badges.dart' as badges;
|
2024-08-21 11:11:27 +00:00
|
|
|
import 'package:solian/widgets/navigation/app_navigation_region.dart';
|
2024-07-12 03:39:44 +00:00
|
|
|
|
|
|
|
class AppNavigationDrawer extends StatefulWidget {
|
2024-07-12 05:15:46 +00:00
|
|
|
final String? routeName;
|
|
|
|
|
|
|
|
const AppNavigationDrawer({super.key, this.routeName});
|
2024-07-12 03:39:44 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
State<AppNavigationDrawer> createState() => _AppNavigationDrawerState();
|
|
|
|
}
|
|
|
|
|
2024-08-21 11:11:27 +00:00
|
|
|
class _AppNavigationDrawerState extends State<AppNavigationDrawer>
|
|
|
|
with TickerProviderStateMixin {
|
|
|
|
bool _isCollapsed = false;
|
|
|
|
|
|
|
|
late final AnimationController _drawerAnimationController =
|
|
|
|
AnimationController(
|
|
|
|
duration: const Duration(milliseconds: 500),
|
|
|
|
vsync: this,
|
|
|
|
);
|
|
|
|
late final Animation<double> _drawerAnimation = Tween<double>(
|
|
|
|
begin: 80.0,
|
|
|
|
end: 304.0,
|
|
|
|
).animate(CurvedAnimation(
|
|
|
|
parent: _drawerAnimationController,
|
|
|
|
curve: Curves.easeInOut,
|
|
|
|
));
|
2024-08-21 09:00:59 +00:00
|
|
|
|
2024-07-12 05:15:46 +00:00
|
|
|
AccountStatus? _accountStatus;
|
|
|
|
|
2024-07-31 05:29:26 +00:00
|
|
|
Future<void> _getStatus() async {
|
2024-07-12 05:15:46 +00:00
|
|
|
final StatusProvider provider = Get.find();
|
|
|
|
|
|
|
|
final resp = await provider.getCurrentStatus();
|
|
|
|
final status = AccountStatus.fromJson(resp.body);
|
|
|
|
|
2024-08-19 11:56:44 +00:00
|
|
|
if (mounted) {
|
|
|
|
setState(() {
|
|
|
|
_accountStatus = status;
|
|
|
|
});
|
|
|
|
}
|
2024-07-12 05:15:46 +00:00
|
|
|
}
|
|
|
|
|
2024-08-21 11:11:27 +00:00
|
|
|
Color get _unFocusColor =>
|
|
|
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
|
|
|
|
|
2024-08-21 09:00:59 +00:00
|
|
|
Widget _buildUserInfo() {
|
|
|
|
return Obx(() {
|
|
|
|
final AuthProvider auth = Get.find();
|
|
|
|
if (auth.isAuthorized.isFalse || auth.userProfile.value == null) {
|
|
|
|
if (_isCollapsed) {
|
|
|
|
return InkWell(
|
2024-08-21 11:11:27 +00:00
|
|
|
child: const Icon(Icons.account_circle).paddingSymmetric(
|
|
|
|
horizontal: 28,
|
|
|
|
vertical: 20,
|
|
|
|
),
|
2024-08-21 09:00:59 +00:00
|
|
|
onTap: () {
|
|
|
|
AppRouter.instance.goNamed('account');
|
|
|
|
_closeDrawer();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2024-07-12 05:15:46 +00:00
|
|
|
|
2024-08-21 09:00:59 +00:00
|
|
|
return ListTile(
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 28),
|
|
|
|
leading: const Icon(Icons.account_circle),
|
|
|
|
title: !_isCollapsed ? Text('guest'.tr) : null,
|
|
|
|
subtitle: !_isCollapsed ? Text('unsignedIn'.tr) : null,
|
|
|
|
onTap: () {
|
|
|
|
AppRouter.instance.goNamed('account');
|
|
|
|
_closeDrawer();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2024-07-12 05:15:46 +00:00
|
|
|
|
2024-08-21 09:00:59 +00:00
|
|
|
final leading = Obx(() {
|
|
|
|
final statusBadgeColor = _accountStatus != null
|
2024-08-21 11:11:27 +00:00
|
|
|
? StatusProvider.determineStatus(_accountStatus!).$2
|
2024-08-21 09:00:59 +00:00
|
|
|
: Colors.grey;
|
2024-07-12 05:15:46 +00:00
|
|
|
|
2024-08-21 09:00:59 +00:00
|
|
|
final RelationshipProvider relations = Get.find();
|
|
|
|
final accountNotifications = relations.friendRequestCount.value;
|
|
|
|
|
|
|
|
return badges.Badge(
|
|
|
|
badgeContent: Text(
|
|
|
|
accountNotifications.toString(),
|
|
|
|
style: const TextStyle(color: Colors.white),
|
|
|
|
),
|
|
|
|
showBadge: accountNotifications > 0,
|
|
|
|
position: badges.BadgePosition.topEnd(
|
|
|
|
top: -10,
|
|
|
|
end: -6,
|
|
|
|
),
|
|
|
|
child: badges.Badge(
|
|
|
|
showBadge: _accountStatus != null,
|
|
|
|
badgeStyle: badges.BadgeStyle(badgeColor: statusBadgeColor),
|
|
|
|
position: badges.BadgePosition.bottomEnd(
|
|
|
|
bottom: 0,
|
|
|
|
end: -2,
|
|
|
|
),
|
|
|
|
child: AccountAvatar(
|
|
|
|
content: auth.userProfile.value!['avatar'],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
});
|
2024-08-07 10:24:16 +00:00
|
|
|
|
2024-08-21 09:00:59 +00:00
|
|
|
return InkWell(
|
|
|
|
child: !_isCollapsed
|
2024-08-21 11:11:27 +00:00
|
|
|
? Row(
|
|
|
|
children: [
|
|
|
|
leading,
|
|
|
|
Expanded(
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
auth.userProfile.value!['nick'],
|
|
|
|
maxLines: 1,
|
|
|
|
overflow: TextOverflow.fade,
|
|
|
|
style: Theme.of(context).textTheme.bodyLarge,
|
|
|
|
).paddingOnly(left: 16),
|
|
|
|
Builder(
|
|
|
|
builder: (context) {
|
|
|
|
if (_accountStatus == null) {
|
|
|
|
return Text('loading'.tr).paddingOnly(left: 16);
|
|
|
|
}
|
|
|
|
final info = StatusProvider.determineStatus(
|
|
|
|
_accountStatus!,
|
|
|
|
);
|
|
|
|
return Text(
|
|
|
|
info.$3,
|
|
|
|
maxLines: 1,
|
|
|
|
overflow: TextOverflow.fade,
|
|
|
|
style: TextStyle(
|
|
|
|
color: _unFocusColor,
|
|
|
|
),
|
|
|
|
).paddingOnly(left: 16);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
).paddingSymmetric(horizontal: 20, vertical: 16)
|
|
|
|
: leading.paddingSymmetric(horizontal: 20, vertical: 16),
|
2024-08-21 09:00:59 +00:00
|
|
|
onTap: () {
|
|
|
|
AppRouter.instance.goNamed('account');
|
|
|
|
_closeDrawer();
|
|
|
|
},
|
|
|
|
onLongPress: () {
|
|
|
|
showModalBottomSheet(
|
|
|
|
useRootNavigator: true,
|
|
|
|
context: context,
|
|
|
|
builder: (context) => AccountStatusAction(
|
|
|
|
currentStatus: _accountStatus!.status,
|
|
|
|
),
|
|
|
|
).then((val) {
|
|
|
|
if (val == true) _getStatus();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-08-21 11:11:27 +00:00
|
|
|
void _expandDrawer() {
|
|
|
|
_drawerAnimationController.animateTo(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _collapseDrawer() {
|
|
|
|
_drawerAnimationController.animateTo(0);
|
|
|
|
}
|
|
|
|
|
2024-08-21 09:00:59 +00:00
|
|
|
void _closeDrawer() {
|
2024-08-21 11:11:27 +00:00
|
|
|
_autoResize();
|
2024-08-21 09:00:59 +00:00
|
|
|
rootScaffoldKey.currentState!.closeDrawer();
|
|
|
|
}
|
|
|
|
|
2024-08-21 11:11:27 +00:00
|
|
|
void _autoResize() {
|
|
|
|
if (SolianTheme.isExtraLargeScreen(context)) {
|
|
|
|
_expandDrawer();
|
|
|
|
} else if (SolianTheme.isLargeScreen(context)) {
|
|
|
|
_collapseDrawer();
|
2024-08-21 12:55:22 +00:00
|
|
|
} else {
|
2024-08-23 15:16:41 +00:00
|
|
|
_drawerAnimationController.value = 1;
|
2024-08-21 11:11:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-21 09:00:59 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
_getStatus();
|
2024-08-21 11:11:27 +00:00
|
|
|
Future.delayed(Duration.zero, () => _autoResize());
|
|
|
|
_drawerAnimationController.addListener(() {
|
|
|
|
if (_drawerAnimation.value > 180 && _isCollapsed) {
|
|
|
|
setState(() => _isCollapsed = false);
|
|
|
|
} else if (_drawerAnimation.value < 180 && !_isCollapsed) {
|
|
|
|
setState(() => _isCollapsed = true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_drawerAnimationController.dispose();
|
|
|
|
super.dispose();
|
2024-08-21 09:00:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2024-08-21 11:11:27 +00:00
|
|
|
return AnimatedBuilder(
|
|
|
|
animation: _drawerAnimation,
|
|
|
|
builder: (context, child) {
|
|
|
|
return Drawer(
|
|
|
|
width: _drawerAnimation.value,
|
|
|
|
backgroundColor:
|
|
|
|
SolianTheme.isLargeScreen(context) ? Colors.transparent : null,
|
|
|
|
child: child,
|
|
|
|
);
|
|
|
|
},
|
2024-08-21 09:00:59 +00:00
|
|
|
child: SafeArea(
|
|
|
|
bottom: false,
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
_buildUserInfo().paddingSymmetric(vertical: 8),
|
2024-08-07 10:24:16 +00:00
|
|
|
const Divider(thickness: 0.3, height: 1),
|
|
|
|
Column(
|
|
|
|
children: AppNavigation.destinations
|
|
|
|
.map(
|
2024-08-21 09:00:59 +00:00
|
|
|
(e) => _isCollapsed
|
2024-08-21 11:35:29 +00:00
|
|
|
? Tooltip(
|
|
|
|
message: e.label,
|
|
|
|
child: InkWell(
|
|
|
|
child: Icon(e.icon, size: 20).paddingSymmetric(
|
|
|
|
horizontal: 28,
|
|
|
|
vertical: 16,
|
|
|
|
),
|
|
|
|
onTap: () {
|
|
|
|
AppRouter.instance.goNamed(e.page);
|
|
|
|
_closeDrawer();
|
|
|
|
},
|
2024-08-21 09:00:59 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
: ListTile(
|
|
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 20,
|
|
|
|
),
|
|
|
|
leading: Icon(e.icon, size: 20).paddingAll(2),
|
|
|
|
title: !_isCollapsed ? Text(e.label) : null,
|
|
|
|
enabled: true,
|
|
|
|
onTap: () {
|
|
|
|
AppRouter.instance.goNamed(e.page);
|
|
|
|
_closeDrawer();
|
|
|
|
},
|
|
|
|
),
|
2024-08-07 10:24:16 +00:00
|
|
|
)
|
|
|
|
.toList(),
|
2024-08-21 09:00:59 +00:00
|
|
|
),
|
2024-08-07 10:24:16 +00:00
|
|
|
const Divider(thickness: 0.3, height: 1),
|
|
|
|
Expanded(
|
2024-08-21 11:11:27 +00:00
|
|
|
child: AppNavigationRegion(
|
2024-08-21 09:00:59 +00:00
|
|
|
isCollapsed: _isCollapsed,
|
2024-08-07 10:24:16 +00:00
|
|
|
onSelected: (item) {
|
|
|
|
_closeDrawer();
|
|
|
|
},
|
2024-07-24 17:18:47 +00:00
|
|
|
),
|
2024-08-07 10:24:16 +00:00
|
|
|
),
|
2024-08-09 16:51:54 +00:00
|
|
|
const Divider(thickness: 0.3, height: 1),
|
|
|
|
Column(
|
|
|
|
children: [
|
2024-08-21 11:11:27 +00:00
|
|
|
if (_isCollapsed)
|
2024-08-21 11:35:29 +00:00
|
|
|
Tooltip(
|
|
|
|
message: 'settings'.tr,
|
|
|
|
child: InkWell(
|
|
|
|
child: const Icon(
|
|
|
|
Icons.settings,
|
|
|
|
size: 20,
|
|
|
|
).paddingSymmetric(
|
|
|
|
horizontal: 28,
|
|
|
|
vertical: 10,
|
|
|
|
),
|
|
|
|
onTap: () {
|
|
|
|
AppRouter.instance.pushNamed('settings');
|
|
|
|
_closeDrawer();
|
|
|
|
},
|
2024-08-21 11:11:27 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
else
|
|
|
|
ListTile(
|
|
|
|
minTileHeight: 0,
|
|
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 20,
|
|
|
|
),
|
|
|
|
leading: const Icon(Icons.settings, size: 20).paddingAll(2),
|
|
|
|
title: Text('settings'.tr),
|
|
|
|
onTap: () {
|
|
|
|
AppRouter.instance.pushNamed('settings');
|
|
|
|
_closeDrawer();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
if (_isCollapsed)
|
2024-08-21 11:35:29 +00:00
|
|
|
Tooltip(
|
|
|
|
message: 'expand'.tr,
|
|
|
|
child: InkWell(
|
|
|
|
child: const Icon(Icons.chevron_right, size: 20)
|
|
|
|
.paddingSymmetric(
|
|
|
|
horizontal: 28,
|
|
|
|
vertical: 10,
|
|
|
|
),
|
|
|
|
onTap: () {
|
|
|
|
_expandDrawer();
|
|
|
|
},
|
2024-08-21 11:11:27 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
else
|
|
|
|
ListTile(
|
|
|
|
minTileHeight: 0,
|
|
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 20,
|
|
|
|
),
|
|
|
|
leading:
|
|
|
|
const Icon(Icons.chevron_left, size: 20).paddingAll(2),
|
|
|
|
title: Text('collapse'.tr),
|
|
|
|
onTap: () {
|
|
|
|
_collapseDrawer();
|
|
|
|
},
|
|
|
|
),
|
2024-08-09 16:51:54 +00:00
|
|
|
],
|
2024-08-10 16:36:27 +00:00
|
|
|
).paddingOnly(
|
|
|
|
top: 8,
|
|
|
|
bottom: math.max(8, MediaQuery.of(context).padding.bottom),
|
|
|
|
),
|
2024-08-07 10:24:16 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
2024-07-12 03:39:44 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|