💄 Better sidebar navigation
This commit is contained in:
parent
54dee9702b
commit
8009f4ca9b
@ -52,6 +52,17 @@ class Realm {
|
||||
'is_community': isCommunity,
|
||||
'account_id': accountId,
|
||||
};
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other is Realm) {
|
||||
return other.id == id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => id;
|
||||
}
|
||||
|
||||
class RealmMember {
|
||||
|
@ -3,16 +3,15 @@ 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';
|
||||
import 'package:solian/providers/content/channel.dart';
|
||||
import 'package:solian/providers/relation.dart';
|
||||
import 'package:solian/router.dart';
|
||||
import 'package:solian/shells/root_shell.dart';
|
||||
import 'package:solian/theme.dart';
|
||||
import 'package:solian/widgets/account/account_avatar.dart';
|
||||
import 'package:solian/widgets/account/account_status_action.dart';
|
||||
import 'package:solian/widgets/channel/channel_list.dart';
|
||||
import 'package:solian/widgets/navigation/app_navigation.dart';
|
||||
import 'package:badges/badges.dart' as badges;
|
||||
import 'package:solian/widgets/navigation/app_navigation_regions.dart';
|
||||
|
||||
class AppNavigationDrawer extends StatefulWidget {
|
||||
final String? routeName;
|
||||
@ -24,12 +23,8 @@ class AppNavigationDrawer extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
int? _selectedIndex = 0;
|
||||
|
||||
AccountStatus? _accountStatus;
|
||||
|
||||
late final ChannelProvider _channels;
|
||||
|
||||
Future<void> _getStatus() async {
|
||||
final StatusProvider provider = Get.find();
|
||||
|
||||
@ -41,15 +36,6 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
});
|
||||
}
|
||||
|
||||
void _detectSelectedIndex() {
|
||||
if (widget.routeName == null) return;
|
||||
|
||||
final nameList = AppNavigation.destinations.map((x) => x.page).toList();
|
||||
final idx = nameList.indexOf(widget.routeName!);
|
||||
|
||||
_selectedIndex = idx != -1 ? idx : null;
|
||||
}
|
||||
|
||||
void _closeDrawer() {
|
||||
rootScaffoldKey.currentState!.closeDrawer();
|
||||
}
|
||||
@ -59,7 +45,6 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () {
|
||||
AppRouter.instance.pushNamed('settings');
|
||||
setState(() => _selectedIndex = null);
|
||||
_closeDrawer();
|
||||
});
|
||||
}
|
||||
@ -67,175 +52,139 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_channels = Get.find();
|
||||
_detectSelectedIndex();
|
||||
_getStatus();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_detectSelectedIndex();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final AuthProvider auth = Get.find();
|
||||
|
||||
return NavigationDrawer(
|
||||
return Drawer(
|
||||
backgroundColor:
|
||||
SolianTheme.isLargeScreen(context) ? Colors.transparent : null,
|
||||
selectedIndex: _selectedIndex,
|
||||
onDestinationSelected: (idx) {
|
||||
setState(() => _selectedIndex = idx);
|
||||
AppRouter.instance.goNamed(AppNavigation.destinations[idx].page);
|
||||
_closeDrawer();
|
||||
},
|
||||
children: [
|
||||
Obx(() {
|
||||
if (auth.isAuthorized.isFalse || auth.userProfile.value == null) {
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 28),
|
||||
leading: const Icon(Icons.account_circle),
|
||||
title: Text('guest'.tr),
|
||||
subtitle: Text('unsignedIn'.tr),
|
||||
trailing: _buildSettingButton(),
|
||||
onTap: () {
|
||||
AppRouter.instance.goNamed('account');
|
||||
setState(() => _selectedIndex = null);
|
||||
_closeDrawer();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
title: Text(
|
||||
auth.userProfile.value!['nick'],
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
subtitle: Builder(
|
||||
builder: (context) {
|
||||
if (_accountStatus == null) {
|
||||
return Text('loading'.tr);
|
||||
}
|
||||
final info = StatusProvider.determineStatus(
|
||||
_accountStatus!,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Obx(() {
|
||||
if (auth.isAuthorized.isFalse || auth.userProfile.value == null) {
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 28),
|
||||
leading: const Icon(Icons.account_circle),
|
||||
title: Text('guest'.tr),
|
||||
subtitle: Text('unsignedIn'.tr),
|
||||
trailing: _buildSettingButton(),
|
||||
onTap: () {
|
||||
AppRouter.instance.goNamed('account');
|
||||
_closeDrawer();
|
||||
},
|
||||
);
|
||||
return Text(
|
||||
info.$3,
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
title: Text(
|
||||
auth.userProfile.value!['nick'],
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
);
|
||||
},
|
||||
),
|
||||
leading: Obx(() {
|
||||
final statusBadgeColor = _accountStatus != null
|
||||
? StatusProvider.determineStatus(
|
||||
),
|
||||
subtitle: Builder(
|
||||
builder: (context) {
|
||||
if (_accountStatus == null) {
|
||||
return Text('loading'.tr);
|
||||
}
|
||||
final info = StatusProvider.determineStatus(
|
||||
_accountStatus!,
|
||||
).$2
|
||||
: Colors.grey;
|
||||
|
||||
final RelationshipProvider relations = Get.find();
|
||||
final accountNotifications = relations.friendRequestCount.value;
|
||||
|
||||
return badges.Badge(
|
||||
badgeContent: Text(
|
||||
accountNotifications.toString(),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
);
|
||||
return Text(
|
||||
info.$3,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
);
|
||||
},
|
||||
),
|
||||
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'],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
trailing: _buildSettingButton(),
|
||||
onTap: () {
|
||||
AppRouter.instance.goNamed('account');
|
||||
setState(() => _selectedIndex = null);
|
||||
_closeDrawer();
|
||||
},
|
||||
onLongPress: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
builder: (context) => AccountStatusAction(
|
||||
currentStatus: _accountStatus!.status,
|
||||
),
|
||||
).then((val) {
|
||||
if (val == true) _getStatus();
|
||||
});
|
||||
},
|
||||
);
|
||||
}).paddingOnly(top: 8),
|
||||
const Divider(thickness: 0.3, height: 1).paddingOnly(
|
||||
bottom: 12,
|
||||
top: 8,
|
||||
),
|
||||
...AppNavigation.destinations.map(
|
||||
(e) => NavigationDrawerDestination(
|
||||
icon: e.icon,
|
||||
label: Text(e.label),
|
||||
),
|
||||
),
|
||||
const Divider(thickness: 0.3, height: 1).paddingOnly(
|
||||
top: 12,
|
||||
),
|
||||
Obx(() {
|
||||
if (auth.isAuthorized.isFalse || auth.userProfile.value == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
leading: Obx(() {
|
||||
final statusBadgeColor = _accountStatus != null
|
||||
? StatusProvider.determineStatus(
|
||||
_accountStatus!,
|
||||
).$2
|
||||
: Colors.grey;
|
||||
|
||||
final selfId = auth.userProfile.value!['id'];
|
||||
final RelationshipProvider relations = Get.find();
|
||||
final accountNotifications =
|
||||
relations.friendRequestCount.value;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Theme(
|
||||
data: Theme.of(context)
|
||||
.copyWith(dividerColor: Colors.transparent),
|
||||
child: ExpansionTile(
|
||||
title: Text('channels'.tr),
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
children: [
|
||||
Obx(
|
||||
() => SizedBox(
|
||||
height: 360,
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () => _channels.refreshAvailableChannel(),
|
||||
child: ChannelListWidget(
|
||||
channels: _channels.groupChannels,
|
||||
selfId: selfId,
|
||||
isDense: true,
|
||||
useReplace: true,
|
||||
onSelected: (_) {
|
||||
setState(() => _selectedIndex = null);
|
||||
_closeDrawer();
|
||||
},
|
||||
),
|
||||
),
|
||||
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'],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
trailing: _buildSettingButton(),
|
||||
onTap: () {
|
||||
AppRouter.instance.goNamed('account');
|
||||
_closeDrawer();
|
||||
},
|
||||
onLongPress: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
builder: (context) => AccountStatusAction(
|
||||
currentStatus: _accountStatus!.status,
|
||||
),
|
||||
).then((val) {
|
||||
if (val == true) _getStatus();
|
||||
});
|
||||
},
|
||||
);
|
||||
}).paddingOnly(top: 8),
|
||||
const Divider(thickness: 0.3, height: 1),
|
||||
Column(
|
||||
children: AppNavigation.destinations
|
||||
.map(
|
||||
(e) => ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
),
|
||||
leading: e.icon,
|
||||
title: Text(e.label),
|
||||
enabled: true,
|
||||
onTap: () {
|
||||
AppRouter.instance.goNamed(e.page);
|
||||
_closeDrawer();
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
).paddingSymmetric(vertical: 8),
|
||||
const Divider(thickness: 0.3, height: 1),
|
||||
Expanded(
|
||||
child: AppNavigationRegions(
|
||||
onSelected: (item) {
|
||||
_closeDrawer();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
87
lib/widgets/navigation/app_navigation_regions.dart
Normal file
87
lib/widgets/navigation/app_navigation_regions.dart
Normal file
@ -0,0 +1,87 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:solian/models/channel.dart';
|
||||
import 'package:solian/providers/content/channel.dart';
|
||||
import 'package:solian/router.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class AppNavigationRegions extends StatelessWidget {
|
||||
final Function(Channel item) onSelected;
|
||||
|
||||
const AppNavigationRegions({super.key, required this.onSelected});
|
||||
|
||||
void _gotoChannel(Channel item) {
|
||||
AppRouter.instance.pushReplacementNamed(
|
||||
'channelChat',
|
||||
pathParameters: {'alias': item.alias},
|
||||
queryParameters: {
|
||||
if (item.realmId != null) 'realm': item.realm!.alias,
|
||||
},
|
||||
);
|
||||
|
||||
onSelected(item);
|
||||
}
|
||||
|
||||
Widget _buildEntry(BuildContext context, Channel item) {
|
||||
const padding = EdgeInsets.symmetric(horizontal: 20);
|
||||
|
||||
return ListTile(
|
||||
minTileHeight: 0,
|
||||
leading: const Icon(Icons.tag_outlined),
|
||||
contentPadding: padding,
|
||||
title: Text(item.name),
|
||||
subtitle: Text(
|
||||
item.description,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
onTap: () => _gotoChannel(item),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ChannelProvider channels = Get.find();
|
||||
|
||||
return Obx(() {
|
||||
final List<Channel> noRealmGroupChannels = channels.availableChannels
|
||||
.where((x) => x.type == 0 && x.realmId == null)
|
||||
.toList();
|
||||
final List<Channel> hasRealmGroupChannels = channels.availableChannels
|
||||
.where((x) => x.type == 0 && x.realmId != null)
|
||||
.toList();
|
||||
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
const SliverPadding(padding: EdgeInsets.only(top: 8)),
|
||||
SliverList.builder(
|
||||
itemCount: noRealmGroupChannels.length,
|
||||
itemBuilder: (context, index) {
|
||||
final element = noRealmGroupChannels[index];
|
||||
return _buildEntry(context, element);
|
||||
},
|
||||
),
|
||||
SliverList.list(
|
||||
children: hasRealmGroupChannels
|
||||
.groupListsBy((x) => x.realm)
|
||||
.entries
|
||||
.map((element) {
|
||||
return ExpansionTile(
|
||||
minTileHeight: 0,
|
||||
tilePadding: const EdgeInsets.only(left: 20, right: 24),
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
collapsedBackgroundColor:
|
||||
Theme.of(context).colorScheme.surfaceContainer,
|
||||
title: Text(element.value.first.realm!.name),
|
||||
leading: const Icon(Icons.workspaces, size: 16)
|
||||
.paddingSymmetric(horizontal: 4),
|
||||
children:
|
||||
element.value.map((x) => _buildEntry(context, x)).toList(),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
30
pubspec.lock
30
pubspec.lock
@ -226,7 +226,7 @@ packages:
|
||||
source: hosted
|
||||
version: "4.10.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
@ -405,10 +405,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: "824f5b9f389bfc4dddac3dea76cd70c51092d9dff0b2ece7ef4f53db8547d258"
|
||||
sha256: "825aec673606875c33cd8d3c4083f1a3c3999015a84178b317b7ef396b7384f3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.6"
|
||||
version: "8.0.7"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -740,10 +740,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_webrtc
|
||||
sha256: f46bd76cef6e8d787dc707d0c591f0e89c912a2970c7b5e68a55b9cca1bdde4c
|
||||
sha256: "67faa07cf49392b50b1aa14590a83caa64d2109345fabd29899dcd8da8538348"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.6"
|
||||
version: "0.11.6+hotfix.1"
|
||||
font_awesome_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -788,10 +788,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: go_router
|
||||
sha256: d380de0355788c5c784fe9f81b43fc833b903991c25ecc4e2a416a67faefa722
|
||||
sha256: ddc16d34b0d74cb313986918c0f0885a7ba2fc24d8fb8419de75f0015144ccfe
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.2"
|
||||
version: "14.2.3"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -876,10 +876,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
sha256: "3fe99e3a459b969f657565a853353bdea7e3d3cae34f7dd124836267d426ec87"
|
||||
sha256: c0e72ecd170b00a5590bb71238d57dc8ad22ee14c60c6b0d1a4e05cafbc5db4b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.12+10"
|
||||
version: "0.8.12+11"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1276,10 +1276,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: b29a799ca03be9f999aa6c39f7de5209482d638e6f857f6b93b0875c618b7e54
|
||||
sha256: eaf2a1ec4472775451e88ca6a7b86559ef2f1d1ed903942ed135e38ea0097dca
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.7"
|
||||
version: "12.0.8"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1300,10 +1300,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
|
||||
sha256: fe0ffe274d665be8e34f9c59705441a7d248edebbe5d9e3ec2665f88b79358ea
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
version: "4.2.2"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1897,10 +1897,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: a36e2d7981122fa185006b216eb6b5b97ede3f9a54b7a511bc966971ab98d049
|
||||
sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.3.3"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -68,6 +68,7 @@ dependencies:
|
||||
async: ^2.11.0
|
||||
field_suggestion: ^0.2.5
|
||||
flutter_typeahead: ^5.2.0
|
||||
collection: ^1.18.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Loading…
Reference in New Issue
Block a user