починил галочки, закрепленные чаты
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user