♻️ Chat listening on sidebar
This commit is contained in:
parent
aa43eaa0eb
commit
1a26880719
@ -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()}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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');
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
@ -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);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user