починил галочки, закрепленные чаты

This commit is contained in:
Jganenok
2026-05-15 17:02:22 +07:00
parent 74917dd3ec
commit f093e7c802
4 changed files with 66 additions and 18 deletions
+2
View File
@@ -132,6 +132,7 @@ class ChatsModule {
final chatsConfig = configMap['chats'] is Map final chatsConfig = configMap['chats'] is Map
? configMap['chats'] as Map ? configMap['chats'] as Map
: {}; : {};
// Presence for online statuses // Presence for online statuses
final presenceMap = data['presence'] is Map ? data['presence'] as Map : {}; final presenceMap = data['presence'] is Map ? data['presence'] as Map : {};
final cachedAt = DateTime.now().millisecondsSinceEpoch; final cachedAt = DateTime.now().millisecondsSinceEpoch;
@@ -272,6 +273,7 @@ class ChatsModule {
dontDisturbUntil = (config['dontDisturbUntil'] as int?) ?? 0; dontDisturbUntil = (config['dontDisturbUntil'] as int?) ?? 0;
} }
int seenTime = 0; int seenTime = 0;
bool isOnline = false; bool isOnline = false;
if (type == 'DIALOG' && otherId != null) { if (type == 'DIALOG' && otherId != null) {
+1 -1
View File
@@ -29,7 +29,7 @@ class CachedContact {
bool get isOfficial => options.contains('OFFICIAL'); bool get isOfficial => options.contains('OFFICIAL');
bool get isBot => options.contains('BOT'); bool get isBot => options.contains('BOT');
bool get isServiceAccount => options.contains('SERVICE_ACCOUNT'); bool get isServiceAccount => options.contains('SERVICE_ACCOUNT');
bool get isVerified => isOfficial || isBot || isServiceAccount; bool get isVerified => isOfficial;
factory CachedContact.fromDbRow(Map<String, dynamic> row) => CachedContact( factory CachedContact.fromDbRow(Map<String, dynamic> row) => CachedContact(
id: row['id'] as int, id: row['id'] as int,
+11 -6
View File
@@ -8,19 +8,19 @@ import '../../models/attachment.dart';
class ContactCache { class ContactCache {
static final Map<int, String> _nameCache = {}; static final Map<int, String> _nameCache = {};
static final Map<int, String> _avatarCache = {}; static final Map<int, String> _avatarCache = {};
static final Map<int, Set<String>> _optionsCache = {};
static void put(int id, String name) { static void put(int id, String name) => _nameCache[id] = name;
_nameCache[id] = name;
}
static void putAvatar(int id, String? baseUrl) { static void putAvatar(int id, String? baseUrl) {
if (baseUrl != null) { if (baseUrl != null) _avatarCache[id] = baseUrl;
_avatarCache[id] = baseUrl;
}
} }
static void putOptions(int id, Set<String> opts) => _optionsCache[id] = opts;
static String? get(int id) => _nameCache[id]; static String? get(int id) => _nameCache[id];
static String? getAvatar(int id) => _avatarCache[id]; static String? getAvatar(int id) => _avatarCache[id];
static bool isOfficial(int id) => _optionsCache[id]?.contains('OFFICIAL') ?? false;
} }
class TranscriptionResult { class TranscriptionResult {
@@ -545,6 +545,11 @@ class MessagesModule {
final baseUrl = contact['baseUrl'] as String?; final baseUrl = contact['baseUrl'] as String?;
ContactCache.putAvatar(contactId, baseUrl); ContactCache.putAvatar(contactId, baseUrl);
final rawOpts = contact['options'];
if (rawOpts is List) {
ContactCache.putOptions(contactId, rawOpts.whereType<String>().toSet());
}
return fullName; return fullName;
} }
} }
@@ -376,13 +376,21 @@ class _ChatListScreenState extends State<ChatListScreen>
} }
List<CachedChat> _chatsForPageIndex(int pageIndex) { List<CachedChat> _chatsForPageIndex(int pageIndex) {
if (_folders.isEmpty) return _chats; List<CachedChat> base;
if (pageIndex < 0 || pageIndex >= _folders.length) return _chats; if (_folders.isEmpty) {
final folder = _folders[pageIndex]; base = _chats;
if (FoldersModule.isAllChatsFolder(folder)) return _chats; } else if (pageIndex < 0 || pageIndex >= _folders.length) {
return _chats base = _chats;
.where((c) => FoldersModule.chatMatchesFolder(c, folder)) } else {
.toList(); final folder = _folders[pageIndex];
base = FoldersModule.isAllChatsFolder(folder)
? _chats
: _chats.where((c) => FoldersModule.chatMatchesFolder(c, folder)).toList();
}
final pinned = base.where((c) => (c.favIndex ?? 0) > 0).toList()
..sort((a, b) => a.favIndex!.compareTo(b.favIndex!));
final regular = base.where((c) => (c.favIndex ?? 0) <= 0).toList();
return [...pinned, ...regular];
} }
void _syncFolderChatScrollControllers() { void _syncFolderChatScrollControllers() {
@@ -1088,7 +1096,25 @@ class _ChatListScreenState extends State<ChatListScreen>
if (_isInitialLoading) { if (_isInitialLoading) {
return _buildChatShimmer(); return _buildChatShimmer();
} }
final chat = chats[index];
final pinnedCount = chats.where((c) => (c.favIndex ?? 0) > 0).length;
final hasSeparator = pinnedCount > 0 && pinnedCount < chats.length;
// Insert separator row between pinned and regular sections
if (hasSeparator && index == pinnedCount) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Divider(
height: 1,
thickness: 0.5,
color: cs.outlineVariant.withValues(alpha: 0.5),
),
);
}
final chatIndex = hasSeparator && index > pinnedCount ? index - 1 : index;
final chat = chats[chatIndex];
final isPinned = (chat.favIndex ?? 0) > 0;
if (chat.type.isNotEmpty && chat.type == "DIALOG" && chat.id != 0) { if (chat.type.isNotEmpty && chat.type == "DIALOG" && chat.id != 0) {
final secondId = chat.participants.entries final secondId = chat.participants.entries
@@ -1097,6 +1123,9 @@ class _ChatListScreenState extends State<ChatListScreen>
.key; .key;
final name = ContactCache.get(secondId); final name = ContactCache.get(secondId);
final avatar = ContactCache.getAvatar(secondId); final avatar = ContactCache.getAvatar(secondId);
// ContactCache.isOfficial covers contacts loaded via opcode 32;
// chat.isOfficial covers contacts from the login payload.
final isVerified = ContactCache.isOfficial(secondId) || chat.isOfficial;
return _buildChatItem( return _buildChatItem(
chat.id.toString(), chat.id.toString(),
@@ -1107,7 +1136,8 @@ class _ChatListScreenState extends State<ChatListScreen>
isOnline: chat.isOnline, isOnline: chat.isOnline,
unreadCount: chat.unreadCount, unreadCount: chat.unreadCount,
isMuted: chat.dontDisturbUntil > 0, isMuted: chat.dontDisturbUntil > 0,
isVerified: chat.isOfficial, isVerified: isVerified,
isPinned: isPinned,
chatType: "DIALOG", chatType: "DIALOG",
); );
} else { } else {
@@ -1124,7 +1154,7 @@ class _ChatListScreenState extends State<ChatListScreen>
if (name?.isNotEmpty == true && chat.id != 0) { if (name?.isNotEmpty == true && chat.id != 0) {
fullMsg += "$name: "; fullMsg += "$name: ";
} }
if (chat.lastMsgText?.isNotEmpty == true) { if (chat.lastMsgText?.isNotEmpty == true) {
fullMsg += chat.lastMsgText ?? ""; fullMsg += chat.lastMsgText ?? "";
} }
@@ -1141,10 +1171,11 @@ class _ChatListScreenState extends State<ChatListScreen>
unreadCount: chat.unreadCount, unreadCount: chat.unreadCount,
isMuted: chat.dontDisturbUntil > 0, isMuted: chat.dontDisturbUntil > 0,
isVerified: chat.isOfficial, isVerified: chat.isOfficial,
isPinned: isPinned,
chatType: chat.type, chatType: chat.type,
); );
} }
}, childCount: _isInitialLoading ? 10 : chats.length), }, childCount: _isInitialLoading ? 10 : chats.length + (chats.any((c) => (c.favIndex ?? 0) > 0) && chats.any((c) => (c.favIndex ?? 0) <= 0) ? 1 : 0)),
), ),
SliverPadding( SliverPadding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
@@ -1760,6 +1791,7 @@ class _ChatListScreenState extends State<ChatListScreen>
int unreadCount = 0, int unreadCount = 0,
bool isMuted = false, bool isMuted = false,
bool isVerified = false, bool isVerified = false,
bool isPinned = false,
String chatType = "CHAT", String chatType = "CHAT",
}) { }) {
final cs = Theme.of(context).colorScheme; final cs = Theme.of(context).colorScheme;
@@ -1898,6 +1930,15 @@ Navigator.push(
weight: 400, weight: 400,
), ),
], ],
if (isPinned) ...[
const SizedBox(width: 4),
Icon(
Symbols.keep,
color: cs.outlineVariant,
size: 14,
weight: 400,
),
],
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
time, time,