Better chat screen

This commit is contained in:
LittleSheep 2024-06-23 02:25:45 +08:00
parent bb67edd227
commit e221016c8d
3 changed files with 130 additions and 81 deletions

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.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/models/channel.dart';
@ -80,82 +79,135 @@ class _ChatScreenState extends State<ChatScreen> {
); );
} }
return RefreshIndicator( final prefixSlivers = [
onRefresh: () => getChannels(), Obx(() {
child: CustomScrollView( if (call.current.value != null) {
slivers: [ return const SliverToBoxAdapter(
SliverAppBar( child: ChatCallCurrentIndicator(),
title: AppBarTitle('chat'.tr), );
centerTitle: false, } else {
floating: true, return const SliverToBoxAdapter();
titleSpacing: SolianTheme.titleSpacing(context), }
toolbarHeight: SolianTheme.toolbarHeight(context), }),
actions: [ ];
const BackgroundStateWidget(),
const NotificationButton(), return DefaultTabController(
PopupMenuButton( length: 2,
icon: const Icon(Icons.add_circle), child: NestedScrollView(
itemBuilder: (BuildContext context) => [ headerSliverBuilder: (context, innerBoxIsScrolled) {
PopupMenuItem( return [
child: ListTile( SliverOverlapAbsorber(
title: Text('channelOrganizeCommon'.tr), handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
leading: const Icon(Icons.tag), context),
contentPadding: sliver: SliverAppBar(
const EdgeInsets.symmetric(horizontal: 8), title: AppBarTitle('chat'.tr),
), centerTitle: true,
onTap: () { floating: true,
AppRouter.instance titleSpacing: SolianTheme.titleSpacing(context),
.pushNamed('channelOrganizing') toolbarHeight: SolianTheme.toolbarHeight(context),
.then( actions: [
(value) { const BackgroundStateWidget(),
if (value != null) getChannels(); const NotificationButton(),
PopupMenuButton(
icon: const Icon(Icons.add_circle),
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) getChannels();
},
);
}, },
);
},
),
PopupMenuItem(
child: ListTile(
title: Text('channelOrganizeDirect'.tr),
leading: const FaIcon(
FontAwesomeIcons.userGroup,
size: 16,
), ),
contentPadding: PopupMenuItem(
const EdgeInsets.symmetric(horizontal: 8), child: ListTile(
), title: Text('channelOrganizeDirect'.tr),
onTap: () { leading: const FaIcon(
final ChannelProvider provider = Get.find(); FontAwesomeIcons.userGroup,
provider size: 16,
.createDirectChannel(context, 'global') ),
.then((resp) { contentPadding:
if (resp != null) { const EdgeInsets.symmetric(horizontal: 8),
getChannels(); ),
} 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,
),
],
),
), ),
SizedBox( ),
width: SolianTheme.isLargeScreen(context) ? 8 : 16, ];
},
body: Builder(builder: (context) {
if (_isBusy) {
return const Center(child: CircularProgressIndicator());
}
return TabBarView(
children: [
RefreshIndicator(
onRefresh: () => getChannels(),
child: CustomScrollView(
slivers: [
...prefixSlivers,
ChannelListWidget(
channels:
_channels.where((x) => x.type == 0).toList(),
selfId: _accountId ?? 0,
),
],
),
),
RefreshIndicator(
onRefresh: () => getChannels(),
child: CustomScrollView(
slivers: [
...prefixSlivers,
ChannelListWidget(
channels:
_channels.where((x) => x.type == 1).toList(),
selfId: _accountId ?? 0,
noCategory: true,
),
],
),
), ),
], ],
), );
Obx(() { }),
if (call.current.value != null) {
return const SliverToBoxAdapter(
child: ChatCallCurrentIndicator(),
);
} else {
return const SliverToBoxAdapter();
}
}),
if (_isBusy)
SliverToBoxAdapter(
child: const LinearProgressIndicator().animate().scaleX(),
),
ChannelListWidget(channels: _channels, selfId: _accountId ?? 0),
],
), ),
); );
}, },

View File

@ -127,6 +127,7 @@ class SolianMessages extends Translations {
'realmDeletionConfirm': 'Confirm realm deletion', 'realmDeletionConfirm': 'Confirm realm deletion',
'realmDeletionConfirmCaption': 'realmDeletionConfirmCaption':
'Are you sure to delete realm @realm? This action cannot be undone!', 'Are you sure to delete realm @realm? This action cannot be undone!',
'channels': 'Channels',
'channelNew': 'Create a new channel', 'channelNew': 'Create a new channel',
'channelNewInRealmHint': 'Create channel in realm @realm', 'channelNewInRealmHint': 'Create channel in realm @realm',
'channelOrganizing': 'Organize a channel', 'channelOrganizing': 'Organize a channel',
@ -328,6 +329,7 @@ class SolianMessages extends Translations {
'realmLeaveConfirmCaption': '你确认要离开领域 @realm 吗?你在该领域发表的内容不会被删除。', 'realmLeaveConfirmCaption': '你确认要离开领域 @realm 吗?你在该领域发表的内容不会被删除。',
'realmDeletionConfirm': '确认删除领域', 'realmDeletionConfirm': '确认删除领域',
'realmDeletionConfirmCaption': '你确定要删除领域 @realm 嘛?该操作不可撤销。', 'realmDeletionConfirmCaption': '你确定要删除领域 @realm 嘛?该操作不可撤销。',
'channels': '频道',
'channelNew': '创建新频道', 'channelNew': '创建新频道',
'channelNewInRealmHint': '在领域 @realm 里创建新频道', 'channelNewInRealmHint': '在领域 @realm 里创建新频道',
'channelOrganizing': '组织频道', 'channelOrganizing': '组织频道',

View File

@ -23,13 +23,11 @@ class ChannelListWidget extends StatefulWidget {
class _ChannelListWidgetState extends State<ChannelListWidget> { class _ChannelListWidgetState extends State<ChannelListWidget> {
final List<Channel> _globalChannels = List.empty(growable: true); final List<Channel> _globalChannels = List.empty(growable: true);
final List<Channel> _directMessages = List.empty(growable: true);
final Map<String, List<Channel>> _inRealms = {}; final Map<String, List<Channel>> _inRealms = {};
void mapChannels() { void mapChannels() {
_inRealms.clear(); _inRealms.clear();
_globalChannels.clear(); _globalChannels.clear();
_directMessages.clear();
if (widget.noCategory) { if (widget.noCategory) {
_globalChannels.addAll(widget.channels); _globalChannels.addAll(widget.channels);
@ -42,8 +40,6 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
_inRealms[channel.realm!.alias] = List.empty(growable: true); _inRealms[channel.realm!.alias] = List.empty(growable: true);
} }
_inRealms[channel.realm!.alias]!.add(channel); _inRealms[channel.realm!.alias]!.add(channel);
} else if (channel.type == 1) {
_directMessages.add(channel);
} else { } else {
_globalChannels.add(channel); _globalChannels.add(channel);
} }
@ -56,6 +52,12 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
} }
@override
void initState() {
mapChannels();
super.initState();
}
Widget buildItem(Channel element) { Widget buildItem(Channel element) {
if (element.type == 1) { if (element.type == 1) {
final otherside = element.members! final otherside = element.members!
@ -125,13 +127,6 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
return SliverList.list( return SliverList.list(
children: [ children: [
..._globalChannels.map((e) => buildItem(e)), ..._globalChannels.map((e) => buildItem(e)),
if (_directMessages.isNotEmpty)
ExpansionTile(
tilePadding: const EdgeInsets.symmetric(horizontal: 24),
title: Text('channelCategoryDirect'.tr),
subtitle: Text('channelCategoryDirectHint'.tr),
children: _directMessages.map((e) => buildItem(e)).toList(),
),
..._inRealms.entries.map((element) { ..._inRealms.entries.map((element) {
return ExpansionTile( return ExpansionTile(
tilePadding: const EdgeInsets.symmetric(horizontal: 24), tilePadding: const EdgeInsets.symmetric(horizontal: 24),