✨ Better chat screen
This commit is contained in:
parent
bb67edd227
commit
e221016c8d
@ -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),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -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': '组织频道',
|
||||||
|
@ -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),
|
||||||
|
Loading…
Reference in New Issue
Block a user