|
|
|
@ -8,6 +8,8 @@ import 'package:intl/intl.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
|
|
|
|
|
|
|
|
import '../connector/meshcore_connector.dart';
|
|
|
|
import '../connector/meshcore_connector.dart';
|
|
|
|
|
|
|
|
import '../models/community.dart';
|
|
|
|
|
|
|
|
import '../storage/community_store.dart';
|
|
|
|
import '../utils/platform_info.dart';
|
|
|
|
import '../utils/platform_info.dart';
|
|
|
|
import '../helpers/chat_scroll_controller.dart';
|
|
|
|
import '../helpers/chat_scroll_controller.dart';
|
|
|
|
import '../connector/meshcore_protocol.dart';
|
|
|
|
import '../connector/meshcore_protocol.dart';
|
|
|
|
@ -56,8 +58,11 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|
|
|
final ChatScrollController _scrollController = ChatScrollController();
|
|
|
|
final ChatScrollController _scrollController = ChatScrollController();
|
|
|
|
final FocusNode _textFieldFocusNode = FocusNode();
|
|
|
|
final FocusNode _textFieldFocusNode = FocusNode();
|
|
|
|
ChannelMessage? _replyingToMessage;
|
|
|
|
ChannelMessage? _replyingToMessage;
|
|
|
|
|
|
|
|
final CommunityStore _communityStore = CommunityStore();
|
|
|
|
|
|
|
|
final CommunityPskIndex _communityIndex = CommunityPskIndex();
|
|
|
|
final Map<String, GlobalKey> _messageKeys = {};
|
|
|
|
final Map<String, GlobalKey> _messageKeys = {};
|
|
|
|
bool _isLoadingOlder = false;
|
|
|
|
bool _isLoadingOlder = false;
|
|
|
|
|
|
|
|
bool _communitiesLoaded = false;
|
|
|
|
|
|
|
|
|
|
|
|
MeshCoreConnector? _connector;
|
|
|
|
MeshCoreConnector? _connector;
|
|
|
|
DateTime? _lastChannelSendAt;
|
|
|
|
DateTime? _lastChannelSendAt;
|
|
|
|
@ -81,6 +86,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|
|
|
final idx = widget.channel.index;
|
|
|
|
final idx = widget.channel.index;
|
|
|
|
final unread = widget.initialUnreadCount;
|
|
|
|
final unread = widget.initialUnreadCount;
|
|
|
|
final messages = connector.getChannelMessages(widget.channel);
|
|
|
|
final messages = connector.getChannelMessages(widget.channel);
|
|
|
|
|
|
|
|
_loadCommunities();
|
|
|
|
ChannelMessage? anchor;
|
|
|
|
ChannelMessage? anchor;
|
|
|
|
if (unread > 0) {
|
|
|
|
if (unread > 0) {
|
|
|
|
anchor = _findOldestUnreadChannelAnchor(messages, unread);
|
|
|
|
anchor = _findOldestUnreadChannelAnchor(messages, unread);
|
|
|
|
@ -107,6 +113,19 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Reload communities when returning from another screen
|
|
|
|
|
|
|
|
Future<void> _loadCommunities() async {
|
|
|
|
|
|
|
|
final connector = context.read<MeshCoreConnector>();
|
|
|
|
|
|
|
|
_communityStore.setPublicKeyHex = connector.selfPublicKeyHex;
|
|
|
|
|
|
|
|
final communities = await _communityStore.loadCommunities();
|
|
|
|
|
|
|
|
if (mounted) {
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
|
|
_communityIndex.initialize(communities);
|
|
|
|
|
|
|
|
_communitiesLoaded = true;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ChannelMessage? _findOldestUnreadChannelAnchor(
|
|
|
|
ChannelMessage? _findOldestUnreadChannelAnchor(
|
|
|
|
List<ChannelMessage> messages,
|
|
|
|
List<ChannelMessage> messages,
|
|
|
|
int unreadCount,
|
|
|
|
int unreadCount,
|
|
|
|
@ -193,16 +212,63 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Widget _channelIcon(Channel channel) {
|
|
|
|
|
|
|
|
// Determine icon based on channel type
|
|
|
|
|
|
|
|
final ChannelType channelType = Channel.getChannelType(
|
|
|
|
|
|
|
|
channel,
|
|
|
|
|
|
|
|
_communityIndex,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
final bool isCommunityChannel = Channel.isCommunityChannel(channelType);
|
|
|
|
|
|
|
|
IconData icon;
|
|
|
|
|
|
|
|
switch (channelType) {
|
|
|
|
|
|
|
|
case ChannelType.communityPublic:
|
|
|
|
|
|
|
|
icon = Icons.groups;
|
|
|
|
|
|
|
|
case ChannelType.communityHashtag:
|
|
|
|
|
|
|
|
icon = Icons.tag;
|
|
|
|
|
|
|
|
case ChannelType.public:
|
|
|
|
|
|
|
|
icon = Icons.public;
|
|
|
|
|
|
|
|
case ChannelType.hashtag:
|
|
|
|
|
|
|
|
icon = Icons.tag;
|
|
|
|
|
|
|
|
case ChannelType.private:
|
|
|
|
|
|
|
|
icon = Icons.lock;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return Stack(
|
|
|
|
|
|
|
|
children: [
|
|
|
|
|
|
|
|
Padding(
|
|
|
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 3),
|
|
|
|
|
|
|
|
child: _communitiesLoaded
|
|
|
|
|
|
|
|
? Icon(icon, size: 20)
|
|
|
|
|
|
|
|
: SizedBox.square(dimension: 20),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
if (isCommunityChannel)
|
|
|
|
|
|
|
|
Positioned(
|
|
|
|
|
|
|
|
right: 0,
|
|
|
|
|
|
|
|
bottom: 0,
|
|
|
|
|
|
|
|
child: Container(
|
|
|
|
|
|
|
|
width: 12,
|
|
|
|
|
|
|
|
height: 12,
|
|
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
|
|
color: Colors.purple,
|
|
|
|
|
|
|
|
shape: BoxShape.circle,
|
|
|
|
|
|
|
|
border: Border.all(
|
|
|
|
|
|
|
|
color: Theme.of(context).cardColor,
|
|
|
|
|
|
|
|
width: 2,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
child: const Icon(Icons.people, size: 8, color: Colors.white),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Scaffold(
|
|
|
|
return Scaffold(
|
|
|
|
appBar: AppBar(
|
|
|
|
appBar: AppBar(
|
|
|
|
title: Row(
|
|
|
|
title: Row(
|
|
|
|
children: [
|
|
|
|
children: [
|
|
|
|
Icon(
|
|
|
|
_channelIcon(widget.channel),
|
|
|
|
widget.channel.isPublicChannel ? Icons.public : Icons.tag,
|
|
|
|
|
|
|
|
size: 20,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
Expanded(
|
|
|
|
Expanded(
|
|
|
|
child: Column(
|
|
|
|
child: Column(
|
|
|
|
|