Account status on sidebar

This commit is contained in:
LittleSheep 2024-07-12 13:15:46 +08:00
parent 3b1b6ec8d6
commit 48b76ed574
8 changed files with 148 additions and 53 deletions

View File

@ -19,7 +19,6 @@ import 'package:solian/providers/content/realm.dart';
import 'package:solian/providers/friend.dart';
import 'package:solian/providers/account_status.dart';
import 'package:solian/router.dart';
import 'package:solian/shells/root_shell.dart';
import 'package:solian/shells/system_shell.dart';
import 'package:solian/theme.dart';
import 'package:solian/translations.dart';
@ -88,10 +87,8 @@ class SolianApp extends StatelessWidget {
onInit: () => _initializeProviders(context),
builder: (context, child) {
return SystemShell(
child: RootShell(
child: ScaffoldMessenger(
child: child ?? const SizedBox(),
),
child: ScaffoldMessenger(
child: child ?? const SizedBox(),
),
);
},

View File

@ -94,9 +94,10 @@ class StatusProvider extends GetConnect {
return resp;
}
static (Widget, String) determineStatus(AccountStatus status,
static (Widget, Color, String) determineStatus(AccountStatus status,
{double size = 14}) {
Widget icon;
Color color;
String? text;
if (!presetStatuses.keys.contains(status.status?.type)) {
@ -104,15 +105,18 @@ class StatusProvider extends GetConnect {
}
if (status.isDisturbable && status.isOnline) {
icon = Icon(Icons.circle, color: Colors.green, size: size);
color = Colors.green;
icon = Icon(Icons.circle, color: color, size: size);
text ??= 'accountStatusOnline'.tr;
} else if (!status.isDisturbable && status.isOnline) {
icon = Icon(Icons.do_not_disturb_on, color: Colors.red, size: size);
color = Colors.red;
icon = Icon(Icons.do_not_disturb_on, color: color, size: size);
text ??= 'accountStatusSilent'.tr;
} else {
icon = Icon(Icons.circle, color: Colors.grey, size: size);
color = Colors.grey;
icon = Icon(Icons.circle, color: color, size: size);
text ??= 'accountStatusOffline'.tr;
}
return (icon, text);
return (icon, color, text);
}
}

View File

@ -218,4 +218,10 @@ class AuthProvider extends GetConnect {
return resp;
}
Future<Response?> getProfileWithCheck({noCache = false}) async {
if (!await isAuthorized) return null;
return await getProfile(noCache: noCache);
}
}

View File

@ -20,6 +20,7 @@ import 'package:solian/screens/realms/realm_view.dart';
import 'package:solian/screens/feed.dart';
import 'package:solian/screens/posts/post_editor.dart';
import 'package:solian/shells/basic_shell.dart';
import 'package:solian/shells/root_shell.dart';
import 'package:solian/shells/title_shell.dart';
import 'package:solian/theme.dart';
import 'package:solian/widgets/sidebar/empty_placeholder.dart';
@ -27,10 +28,18 @@ import 'package:solian/widgets/sidebar/empty_placeholder.dart';
abstract class AppRouter {
static GoRouter instance = GoRouter(
routes: [
_feedRoute,
_chatRoute,
_realmRoute,
_accountRoute,
ShellRoute(
builder: (context, state, child) => RootShell(
state: state,
child: child,
),
routes: [
_feedRoute,
_chatRoute,
_realmRoute,
_accountRoute,
],
),
],
);

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:solian/theme.dart';
import 'package:solian/widgets/navigation/app_navigation_drawer.dart';
import 'package:solian/widgets/navigation/app_navigation_rail.dart';
@ -9,10 +10,12 @@ class RootShell extends StatelessWidget {
final bool showSidebar;
final bool showNavigation;
final bool? showBottomNavigation;
final GoRouterState state;
final Widget child;
const RootShell({
super.key,
required this.state,
required this.child,
this.showSidebar = true,
this.showNavigation = true,
@ -25,9 +28,14 @@ class RootShell extends StatelessWidget {
@override
Widget build(BuildContext context) {
final routeName = state.topRoute?.name;
return Scaffold(
key: rootScaffoldKey,
drawer: const AppNavigationDrawer(),
drawer: AppNavigationDrawer(
key: const ValueKey('navigation-drawer'),
routeName: routeName,
),
body: SolianTheme.isLargeScreen(context)
? Row(
children: [

View File

@ -124,7 +124,7 @@ class AccountHeadingWidget extends StatelessWidget {
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(info.$2),
Text(info.$3),
if (!status.isOnline && status.lastSeenAt != null)
Opacity(
opacity: 0.75,

View File

@ -1,36 +0,0 @@
import 'package:flutter/material.dart';
import 'package:solian/router.dart';
import 'package:solian/widgets/navigation/app_navigation.dart';
class AppNavigationBottomBar extends StatefulWidget {
const AppNavigationBottomBar({super.key});
@override
State<AppNavigationBottomBar> createState() => _AppNavigationBottomBarState();
}
class _AppNavigationBottomBarState extends State<AppNavigationBottomBar> {
int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
items: AppNavigation.destinations
.map(
(e) => BottomNavigationBarItem(
icon: e.icon,
label: e.label,
),
)
.toList(),
type: BottomNavigationBarType.fixed,
landscapeLayout: BottomNavigationBarLandscapeLayout.centered,
currentIndex: _selectedIndex,
showUnselectedLabels: false,
onTap: (idx) {
setState(() => _selectedIndex = idx);
AppRouter.instance.goNamed(AppNavigation.destinations[idx].page);
},
);
}
}

View File

@ -1,10 +1,19 @@
import 'package:flutter/material.dart';
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/router.dart';
import 'package:solian/shells/root_shell.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/account/account_status_action.dart';
import 'package:solian/widgets/navigation/app_navigation.dart';
import 'package:badges/badges.dart' as badges;
class AppNavigationDrawer extends StatefulWidget {
const AppNavigationDrawer({super.key});
final String? routeName;
const AppNavigationDrawer({super.key, this.routeName});
@override
State<AppNavigationDrawer> createState() => _AppNavigationDrawerState();
@ -12,9 +21,45 @@ class AppNavigationDrawer extends StatefulWidget {
class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
int _selectedIndex = 0;
AccountStatus? _accountStatus;
void getStatus() async {
final StatusProvider provider = Get.find();
final resp = await provider.getCurrentStatus();
final status = AccountStatus.fromJson(resp.body);
setState(() {
_accountStatus = status;
});
}
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 : 0;
}
@override
void initState() {
super.initState();
detectSelectedIndex();
getStatus();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
detectSelectedIndex();
}
@override
Widget build(BuildContext context) {
final AuthProvider auth = Get.find();
return NavigationDrawer(
selectedIndex: _selectedIndex,
onDestinationSelected: (idx) {
@ -23,6 +68,68 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
rootScaffoldKey.currentState!.closeDrawer();
},
children: [
FutureBuilder(
future: auth.getProfileWithCheck(),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == null) {
return const SizedBox();
}
return Column(
children: [
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
title: Text(snapshot.data!.body['nick']),
subtitle: Builder(
builder: (context) {
if (_accountStatus == null) {
return Text('loading'.tr);
}
final info = StatusProvider.determineStatus(
_accountStatus!,
);
return Text(info.$3);
},
),
leading: Builder(builder: (context) {
final badgeColor = _accountStatus != null
? StatusProvider.determineStatus(
_accountStatus!,
).$2
: Colors.grey;
return badges.Badge(
showBadge: _accountStatus != null,
badgeStyle: badges.BadgeStyle(badgeColor: badgeColor),
position: badges.BadgePosition.bottomEnd(
bottom: 0,
end: -2,
),
child: AccountAvatar(
content: snapshot.data!.body['avatar'],
),
);
}),
onTap: () {
showModalBottomSheet(
useRootNavigator: true,
context: context,
builder: (context) => AccountStatusAction(
currentStatus: _accountStatus!.status,
),
).then((val) {
if (val == true) getStatus();
});
},
),
const Divider(thickness: 0.3, height: 1).paddingOnly(
bottom: 16,
top: 8,
),
],
);
},
),
...AppNavigation.destinations.map(
(e) => NavigationDrawerDestination(
icon: e.icon,