♻️ Chat listening on sidebar

This commit is contained in:
LittleSheep 2024-07-12 21:59:16 +08:00
parent aa43eaa0eb
commit 1a26880719
8 changed files with 318 additions and 322 deletions

View File

@ -95,7 +95,7 @@ class SolianApp extends StatelessWidget {
); );
} }
void _initializeProviders(BuildContext context) { void _initializeProviders(BuildContext context) async {
Get.lazyPut(() => AuthProvider()); Get.lazyPut(() => AuthProvider());
Get.lazyPut(() => FriendProvider()); Get.lazyPut(() => FriendProvider());
Get.lazyPut(() => FeedProvider()); Get.lazyPut(() => FeedProvider());
@ -108,19 +108,19 @@ class SolianApp extends StatelessWidget {
Get.lazyPut(() => ChatCallProvider()); Get.lazyPut(() => ChatCallProvider());
final AuthProvider auth = Get.find(); final AuthProvider auth = Get.find();
auth.isAuthorized.then((value) async { if (await auth.isAuthorized) {
if (value) { Get.find<AccountProvider>().connect();
Get.find<AccountProvider>().connect(); Get.find<ChatProvider>().connect();
Get.find<ChatProvider>().connect();
try { Get.find<ChannelProvider>().refreshAvailableChannel();
Get.find<AccountProvider>().registerPushNotifications();
} catch (err) { try {
context.showSnackbar( Get.find<AccountProvider>().registerPushNotifications();
'pushNotifyRegisterFailed'.trParams({'reason': err.toString()}), } catch (err) {
); context.showSnackbar(
} 'pushNotifyRegisterFailed'.trParams({'reason': err.toString()}),
);
} }
}); }
} }
} }

View File

@ -1,10 +1,32 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:solian/models/channel.dart';
import 'package:solian/providers/auth.dart'; import 'package:solian/providers/auth.dart';
import 'package:solian/widgets/account/friend_select.dart'; import 'package:solian/widgets/account/friend_select.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class ChannelProvider extends GetxController { class ChannelProvider extends GetxController {
RxBool isLoading = false.obs;
RxList<Channel> availableChannels = RxList.empty(growable: true);
List<Channel> get groupChannels =>
availableChannels.where((x) => x.type == 0).toList();
List<Channel> get directChannels =>
availableChannels.where((x) => x.type == 1).toList();
Future<void> refreshAvailableChannel() async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) throw Exception('unauthorized');
isLoading.value = true;
final resp = await listAvailableChannel();
isLoading.value = false;
availableChannels.value =
resp.body.map((x) => Channel.fromJson(x)).toList().cast<Channel>();
availableChannels.refresh();
}
Future<Response> getChannel(String alias, {String realm = 'global'}) async { Future<Response> getChannel(String alias, {String realm = 'global'}) async {
final AuthProvider auth = Get.find(); final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) throw Exception('unauthorized'); if (!await auth.isAuthorized) throw Exception('unauthorized');
@ -19,7 +41,8 @@ class ChannelProvider extends GetxController {
return resp; return resp;
} }
Future<Response> getMyChannelProfile(String alias, {String realm = 'global'}) async { Future<Response> getMyChannelProfile(String alias,
{String realm = 'global'}) async {
final AuthProvider auth = Get.find(); final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) throw Exception('unauthorized'); if (!await auth.isAuthorized) throw Exception('unauthorized');

View File

@ -19,7 +19,7 @@ import 'package:solian/screens/realms/realm_organize.dart';
import 'package:solian/screens/realms/realm_view.dart'; import 'package:solian/screens/realms/realm_view.dart';
import 'package:solian/screens/home.dart'; import 'package:solian/screens/home.dart';
import 'package:solian/screens/posts/post_editor.dart'; import 'package:solian/screens/posts/post_editor.dart';
import 'package:solian/shells/basic_shell.dart'; import 'package:solian/shells/sidebar_shell.dart';
import 'package:solian/shells/root_shell.dart'; import 'package:solian/shells/root_shell.dart';
import 'package:solian/shells/title_shell.dart'; import 'package:solian/shells/title_shell.dart';
import 'package:solian/theme.dart'; import 'package:solian/theme.dart';
@ -115,20 +115,12 @@ abstract class AppRouter {
); );
static final ShellRoute _chatRoute = ShellRoute( static final ShellRoute _chatRoute = ShellRoute(
builder: (context, state, child) => BasicShell( builder: (context, state, child) => child,
state: state,
sidebarFirst: true,
showAppBar: false,
sidebar: const ChatScreen(),
child: child,
),
routes: [ routes: [
GoRoute( GoRoute(
path: '/chat', path: '/chat',
name: 'chat', name: 'chat',
builder: (context, state) => SolianTheme.isExtraLargeScreen(context) builder: (context, state) => const ChatScreen(),
? const EmptyPagePlaceholder()
: const ChatScreen(),
), ),
GoRoute( GoRoute(
path: '/chat/organize', path: '/chat/organize',
@ -170,20 +162,12 @@ abstract class AppRouter {
); );
static final ShellRoute _realmRoute = ShellRoute( static final ShellRoute _realmRoute = ShellRoute(
builder: (context, state, child) => BasicShell( builder: (context, state, child) => child,
state: state,
sidebarFirst: true,
showAppBar: false,
sidebar: const RealmListScreen(),
child: child,
),
routes: [ routes: [
GoRoute( GoRoute(
path: '/realms', path: '/realms',
name: 'realms', name: 'realms',
builder: (context, state) => SolianTheme.isExtraLargeScreen(context) builder: (context, state) => const RealmListScreen(),
? const EmptyPagePlaceholder()
: const RealmListScreen(),
), ),
GoRoute( GoRoute(
path: '/realms/:alias/detail', path: '/realms/:alias/detail',
@ -219,7 +203,7 @@ abstract class AppRouter {
); );
static final ShellRoute _accountRoute = ShellRoute( static final ShellRoute _accountRoute = ShellRoute(
builder: (context, state, child) => BasicShell( builder: (context, state, child) => SidebarShell(
state: state, state: state,
sidebarFirst: true, sidebarFirst: true,
showAppBar: false, showAppBar: false,

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:solian/models/channel.dart'; import 'package:solian/exts.dart';
import 'package:solian/providers/auth.dart'; import 'package:solian/providers/auth.dart';
import 'package:solian/providers/content/channel.dart'; import 'package:solian/providers/content/channel.dart';
import 'package:solian/router.dart'; import 'package:solian/router.dart';
@ -12,6 +12,7 @@ import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/channel/channel_list.dart'; import 'package:solian/widgets/channel/channel_list.dart';
import 'package:solian/widgets/chat/call/chat_call_indicator.dart'; import 'package:solian/widgets/chat/call/chat_call_indicator.dart';
import 'package:solian/widgets/current_state_action.dart'; import 'package:solian/widgets/current_state_action.dart';
import 'package:solian/widgets/sized_container.dart';
class ChatScreen extends StatefulWidget { class ChatScreen extends StatefulWidget {
const ChatScreen({super.key}); const ChatScreen({super.key});
@ -21,44 +22,17 @@ class ChatScreen extends StatefulWidget {
} }
class _ChatScreenState extends State<ChatScreen> { class _ChatScreenState extends State<ChatScreen> {
bool _isBusy = true; late final ChannelProvider _channels;
int? _accountId;
final List<Channel> _channels = List.empty(growable: true);
getProfile() async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) return;
final prof = await auth.getProfile();
_accountId = prof.body['id'];
}
getChannels() async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) return;
setState(() => _isBusy = true);
final ChannelProvider provider = Get.find();
final resp = await provider.listAvailableChannel();
setState(() {
_channels.clear();
_channels.addAll(
resp.body.map((e) => Channel.fromJson(e)).toList().cast<Channel>(),
);
});
setState(() => _isBusy = false);
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
try {
getProfile(); _channels = Get.find();
getChannels(); _channels.refreshAvailableChannel();
} catch (e) {
context.showErrorDialog(e);
}
} }
@override @override
@ -67,144 +41,103 @@ class _ChatScreenState extends State<ChatScreen> {
return Material( return Material(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
child: FutureBuilder( child: Scaffold(
future: auth.isAuthorized, appBar: AppBar(
builder: (context, snapshot) { title: AppBarTitle('chat'.tr),
if (!snapshot.hasData) { centerTitle: false,
return const Center( toolbarHeight: SolianTheme.toolbarHeight(context),
child: CircularProgressIndicator(), actions: [
); const BackgroundStateWidget(),
} else if (snapshot.data == false) { const NotificationButton(),
return SigninRequiredOverlay( PopupMenuButton(
onSignedIn: () { icon: const Icon(Icons.add_circle),
getChannels(); itemBuilder: (BuildContext context) => [
}, PopupMenuItem(
); child: ListTile(
} title: Text('channelOrganizeCommon'.tr),
leading: const Icon(Icons.tag),
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
),
onTap: () {
AppRouter.instance.pushNamed('channelOrganizing').then(
(value) {
if (value != null) {
_channels.refreshAvailableChannel();
}
},
);
},
),
PopupMenuItem(
child: ListTile(
title: Text('channelOrganizeDirect'.tr),
leading: const FaIcon(
FontAwesomeIcons.userGroup,
size: 16,
),
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
),
onTap: () {
final ChannelProvider provider = Get.find();
provider
.createDirectChannel(context, 'global')
.then((resp) {
if (resp != null) {
_channels.refreshAvailableChannel();
}
});
},
),
],
),
SizedBox(
width: SolianTheme.isLargeScreen(context) ? 8 : 16,
),
],
),
body: FutureBuilder(
future: auth.getProfileWithCheck(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.data == null) {
return SigninRequiredOverlay(
onSignedIn: () => _channels.refreshAvailableChannel(),
);
}
return DefaultTabController( final selfId = snapshot.data!.body['id'];
length: 2,
child: NestedScrollView( return Column(
headerSliverBuilder: (context, innerBoxIsScrolled) { children: [
return [ Obx(() {
SliverOverlapAbsorber( if (_channels.isLoading.isFalse) {
handle: NestedScrollView.sliverOverlapAbsorberHandleFor( return const SizedBox();
context), } else {
sliver: SliverAppBar( return const LinearProgressIndicator();
title: AppBarTitle('chat'.tr), }
centerTitle: false, }),
floating: true, const ChatCallCurrentIndicator(),
toolbarHeight: SolianTheme.toolbarHeight(context), Expanded(
actions: [ child: CenteredContainer(
const BackgroundStateWidget(), child: RefreshIndicator(
const NotificationButton(), onRefresh: _channels.refreshAvailableChannel,
PopupMenuButton( child: Obx(
icon: const Icon(Icons.add_circle), () => ChannelListWidget(
itemBuilder: (BuildContext context) => [ noCategory: true,
PopupMenuItem( channels: _channels.directChannels,
child: ListTile( selfId: selfId,
title: Text('channelOrganizeCommon'.tr),
leading: const Icon(Icons.tag),
contentPadding:
const EdgeInsets.symmetric(horizontal: 8),
),
onTap: () {
AppRouter.instance
.pushNamed('channelOrganizing')
.then(
(value) {
if (value != null) getChannels();
},
);
},
),
PopupMenuItem(
child: ListTile(
title: Text('channelOrganizeDirect'.tr),
leading: const FaIcon(
FontAwesomeIcons.userGroup,
size: 16,
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 8),
),
onTap: () {
final ChannelProvider provider = Get.find();
provider
.createDirectChannel(context, 'global')
.then((resp) {
if (resp != null) {
getChannels();
}
});
},
),
],
), ),
SizedBox(
width: SolianTheme.isLargeScreen(context) ? 8 : 16,
),
],
bottom: TabBar(
tabs: [
Tab(
icon: const Icon(Icons.tag),
text: 'channels'.tr,
),
Tab(
icon: const Icon(Icons.chat),
text: 'channelCategoryDirect'.tr,
),
],
), ),
), ),
), ),
]; ),
}, ],
body: Builder(builder: (context) { );
if (_isBusy) { },
return const Center(child: CircularProgressIndicator()); ),
}
return TabBarView(
children: [
Column(
children: [
const ChatCallCurrentIndicator(),
Expanded(
child: RefreshIndicator(
onRefresh: () => getChannels(),
child: ChannelListWidget(
channels:
_channels.where((x) => x.type == 0).toList(),
selfId: _accountId ?? 0,
),
),
),
],
),
Column(
children: [
const ChatCallCurrentIndicator(),
Expanded(
child: RefreshIndicator(
onRefresh: () => getChannels(),
child: ChannelListWidget(
channels:
_channels.where((x) => x.type == 1).toList(),
selfId: _accountId ?? 0,
noCategory: true,
),
),
),
],
),
],
);
}),
),
);
},
), ),
); );
} }

View File

@ -11,6 +11,7 @@ import 'package:solian/theme.dart';
import 'package:solian/widgets/account/signin_required_overlay.dart'; import 'package:solian/widgets/account/signin_required_overlay.dart';
import 'package:solian/widgets/app_bar_title.dart'; import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/current_state_action.dart'; import 'package:solian/widgets/current_state_action.dart';
import 'package:solian/widgets/sized_container.dart';
class RealmListScreen extends StatefulWidget { class RealmListScreen extends StatefulWidget {
const RealmListScreen({super.key}); const RealmListScreen({super.key});
@ -56,63 +57,65 @@ class _RealmListScreenState extends State<RealmListScreen> {
return Material( return Material(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
child: FutureBuilder( child: Scaffold(
future: auth.isAuthorized, appBar: AppBar(
builder: (context, snapshot) { title: AppBarTitle('realm'.tr),
if (!snapshot.hasData) { centerTitle: false,
return const Center( toolbarHeight: SolianTheme.toolbarHeight(context),
child: CircularProgressIndicator(), actions: [
); const BackgroundStateWidget(),
} else if (snapshot.data == false) { const NotificationButton(),
return SigninRequiredOverlay( IconButton(
onSignedIn: () { icon: const Icon(Icons.add_circle),
getRealms(); onPressed: () {
}, AppRouter.instance.pushNamed('realmOrganizing').then(
); (value) {
} if (value != null) getRealms();
return RefreshIndicator(
onRefresh: () => getRealms(),
child: CustomScrollView(
slivers: [
SliverAppBar(
title: AppBarTitle('realm'.tr),
centerTitle: false,
floating: true,
toolbarHeight: SolianTheme.toolbarHeight(context),
actions: [
const BackgroundStateWidget(),
const NotificationButton(),
IconButton(
icon: const Icon(Icons.add_circle),
onPressed: () {
AppRouter.instance.pushNamed('realmOrganizing').then(
(value) {
if (value != null) getRealms();
},
);
},
),
SizedBox(
width: SolianTheme.isLargeScreen(context) ? 8 : 16,
),
],
),
if (_isBusy)
SliverToBoxAdapter(
child: const LinearProgressIndicator().animate().scaleX(),
),
SliverList.builder(
itemCount: _realms.length,
itemBuilder: (context, index) {
final element = _realms[index];
return buildRealm(element);
}, },
) );
], },
), ),
); SizedBox(
}, width: SolianTheme.isLargeScreen(context) ? 8 : 16,
),
],
),
body: FutureBuilder(
future: auth.isAuthorized,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.data == false) {
return SigninRequiredOverlay(
onSignedIn: () {
getRealms();
},
);
}
return Column(
children: [
if (_isBusy) const LinearProgressIndicator().animate().scaleX(),
Expanded(
child: CenteredContainer(
child: RefreshIndicator(
onRefresh: () => getRealms(),
child: ListView.builder(
itemCount: _realms.length,
itemBuilder: (context, index) {
final element = _realms[index];
return buildRealm(element);
},
),
),
),
),
],
);
},
),
), ),
); );
} }

View File

@ -5,7 +5,7 @@ import 'package:solian/theme.dart';
import 'package:solian/widgets/app_bar_title.dart'; import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/sidebar/sidebar_placeholder.dart'; import 'package:solian/widgets/sidebar/sidebar_placeholder.dart';
class BasicShell extends StatelessWidget { class SidebarShell extends StatelessWidget {
final bool showAppBar; final bool showAppBar;
final GoRouterState state; final GoRouterState state;
final Widget child; final Widget child;
@ -13,7 +13,7 @@ class BasicShell extends StatelessWidget {
final bool sidebarFirst; final bool sidebarFirst;
final Widget? sidebar; final Widget? sidebar;
const BasicShell({ const SidebarShell({
super.key, super.key,
required this.child, required this.child,
required this.state, required this.state,

View File

@ -8,13 +8,17 @@ import 'package:solian/widgets/account/account_avatar.dart';
class ChannelListWidget extends StatefulWidget { class ChannelListWidget extends StatefulWidget {
final List<Channel> channels; final List<Channel> channels;
final int selfId; final int selfId;
final bool isDense;
final bool noCategory; final bool noCategory;
final bool useReplace;
const ChannelListWidget({ const ChannelListWidget({
super.key, super.key,
required this.channels, required this.channels,
required this.selfId, required this.selfId,
this.isDense = false,
this.noCategory = false, this.noCategory = false,
this.useReplace = false,
}); });
@override @override
@ -48,68 +52,87 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
@override @override
void didUpdateWidget(covariant ChannelListWidget oldWidget) { void didUpdateWidget(covariant ChannelListWidget oldWidget) {
setState(() => mapChannels());
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
setState(() => mapChannels());
} }
@override @override
void initState() { void initState() {
mapChannels();
super.initState(); super.initState();
mapChannels();
} }
Widget buildItem(Channel element) { void gotoChannel(Channel item) {
if (element.type == 1) { if (widget.useReplace) {
final otherside = element.members! AppRouter.instance.pushReplacementNamed(
'channelChat',
pathParameters: {'alias': item.alias},
queryParameters: {
if (item.realmId != null) 'realm': item.realm!.alias,
},
);
} else {
AppRouter.instance.pushNamed(
'channelChat',
pathParameters: {'alias': item.alias},
queryParameters: {
if (item.realmId != null) 'realm': item.realm!.alias,
},
);
}
}
Widget buildItem(Channel item) {
final padding = widget.isDense
? const EdgeInsets.symmetric(horizontal: 20)
: const EdgeInsets.symmetric(horizontal: 24);
if (item.type == 1) {
final otherside = item.members!
.where((e) => e.account.externalId != widget.selfId) .where((e) => e.account.externalId != widget.selfId)
.first; .first;
return ListTile( return ListTile(
leading: AccountAvatar( leading: AccountAvatar(
content: otherside.account.avatar, content: otherside.account.avatar,
radius: widget.isDense ? 12 : 24,
bgColor: Colors.indigo, bgColor: Colors.indigo,
feColor: Colors.white, feColor: Colors.white,
), ),
contentPadding: const EdgeInsets.symmetric(horizontal: 24), contentPadding: padding,
title: Text(otherside.account.nick), title: Text(otherside.account.nick),
subtitle: Text( subtitle: !widget.isDense
'channelDirectDescription' ? Text(
.trParams({'username': '@${otherside.account.name}'}), 'channelDirectDescription'.trParams(
{'username': '@${otherside.account.name}'},
),
)
: null,
onTap: () => gotoChannel(item),
);
} else {
return ListTile(
minTileHeight: item.realmId == null
? 48
: widget.isDense
? 24
: null,
leading: CircleAvatar(
backgroundColor:
item.realmId == null ? Colors.indigo : Colors.transparent,
radius: widget.isDense ? 12 : 24,
child: FaIcon(
FontAwesomeIcons.hashtag,
color: item.realmId == null ? Colors.white : Colors.indigo,
size: widget.isDense ? 12 : 16,
),
), ),
onTap: () { contentPadding: padding,
AppRouter.instance.pushNamed( title: Text(item.name),
'channelChat', subtitle: !widget.isDense ? Text(item.description) : null,
pathParameters: {'alias': element.alias}, onTap: () => gotoChannel(item),
queryParameters: {
if (element.realmId != null) 'realm': element.realm!.alias,
},
);
},
); );
} }
return ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.indigo,
child: FaIcon(
FontAwesomeIcons.hashtag,
color: Colors.white,
size: 16,
),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
title: Text(element.name),
subtitle: Text(element.description),
onTap: () {
AppRouter.instance.pushNamed(
'channelChat',
pathParameters: {'alias': element.alias},
queryParameters: {
if (element.realmId != null) 'realm': element.realm!.alias,
},
);
},
);
} }
@override @override
@ -137,25 +160,16 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
return buildItem(element); return buildItem(element);
}, },
), ),
..._inRealms.entries.map((element) { SliverList.list(
return SliverList.builder( children: _inRealms.entries.map((element) {
itemCount: element.value.length + 1, return ExpansionTile(
itemBuilder: (context, index) { tilePadding: const EdgeInsets.symmetric(horizontal: 24),
if (index == 0) { minTileHeight: 48,
return ListTile( title: Text(element.value.first.realm!.name),
contentPadding: const EdgeInsets.symmetric(horizontal: 28), children: element.value.map((x) => buildItem(x)).toList(),
leading: const Icon(Icons.workspaces, size: 20) );
.paddingOnly(left: 6, right: 10), }).toList(),
tileColor: Theme.of(context).colorScheme.surfaceContainerHigh, ),
title: Text(element.value.first.realm!.name),
);
}
final item = element.value[index - 1];
return buildItem(item);
},
);
}),
], ],
); );
} }

View File

@ -3,10 +3,12 @@ import 'package:get/get.dart';
import 'package:solian/models/account_status.dart'; import 'package:solian/models/account_status.dart';
import 'package:solian/providers/account_status.dart'; import 'package:solian/providers/account_status.dart';
import 'package:solian/providers/auth.dart'; import 'package:solian/providers/auth.dart';
import 'package:solian/providers/content/channel.dart';
import 'package:solian/router.dart'; import 'package:solian/router.dart';
import 'package:solian/shells/root_shell.dart'; import 'package:solian/shells/root_shell.dart';
import 'package:solian/widgets/account/account_avatar.dart'; import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/account/account_status_action.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:solian/widgets/navigation/app_navigation.dart';
import 'package:badges/badges.dart' as badges; import 'package:badges/badges.dart' as badges;
@ -21,8 +23,12 @@ class AppNavigationDrawer extends StatefulWidget {
class _AppNavigationDrawerState extends State<AppNavigationDrawer> { class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
int? _selectedIndex = 0; int? _selectedIndex = 0;
AccountStatus? _accountStatus; AccountStatus? _accountStatus;
late final AuthProvider _auth;
late final ChannelProvider _channels;
void getStatus() async { void getStatus() async {
final StatusProvider provider = Get.find(); final StatusProvider provider = Get.find();
@ -50,6 +56,8 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_auth = Get.find();
_channels = Get.find();
detectSelectedIndex(); detectSelectedIndex();
getStatus(); getStatus();
} }
@ -145,7 +153,38 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
), ),
const Divider(thickness: 0.3, height: 1).paddingOnly( const Divider(thickness: 0.3, height: 1).paddingOnly(
top: 12, top: 12,
bottom: 8, ),
FutureBuilder(
future: _auth.getProfileWithCheck(),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == null) {
return const SizedBox();
}
final selfId = snapshot.data!.body['id'];
return Column(
children: [
ExpansionTile(
title: Text('chat'.tr),
tilePadding: const EdgeInsets.symmetric(horizontal: 24),
children: [
Obx(
() => SizedBox(
height: 360,
child: ChannelListWidget(
channels: _channels.groupChannels,
selfId: selfId,
isDense: true,
useReplace: true,
),
),
),
],
),
],
);
},
), ),
], ],
); );