feat: галочки у официальных пользователей, ботов и каналов
This commit is contained in:
@@ -38,6 +38,7 @@ class CachedChat {
|
||||
final bool isOnline;
|
||||
final int seenTime;
|
||||
final Map<int, int> participants;
|
||||
final Set<String> options;
|
||||
|
||||
const CachedChat({
|
||||
required this.id,
|
||||
@@ -57,8 +58,11 @@ class CachedChat {
|
||||
required this.isOnline,
|
||||
required this.seenTime,
|
||||
required this.participants,
|
||||
this.options = const {},
|
||||
});
|
||||
|
||||
bool get isOfficial => options.contains('OFFICIAL');
|
||||
|
||||
factory CachedChat.fromDbRow(Map<String, dynamic> row) => CachedChat(
|
||||
id: row['id'] as int,
|
||||
accountId: row['account_id'] as int,
|
||||
@@ -76,9 +80,15 @@ class CachedChat {
|
||||
dontDisturbUntil: row['dont_disturb_until'] as int,
|
||||
isOnline: (row['is_online'] as int) == 1,
|
||||
seenTime: row['seen_time'] as int,
|
||||
participants: _parseParticipants(row['participants'])
|
||||
participants: _parseParticipants(row['participants']),
|
||||
options: _decodeOptions(row['options']),
|
||||
);
|
||||
|
||||
static Set<String> _decodeOptions(dynamic raw) {
|
||||
if (raw is! String || raw.isEmpty) return const {};
|
||||
return raw.split(',').where((s) => s.isNotEmpty).toSet();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toDbRow() => {
|
||||
'id': id,
|
||||
'account_id': accountId,
|
||||
@@ -96,7 +106,8 @@ class CachedChat {
|
||||
'dont_disturb_until': dontDisturbUntil,
|
||||
'is_online': isOnline ? 1 : 0,
|
||||
'seen_time': seenTime,
|
||||
'participants': jsonEncode(participants.map((k, v) => MapEntry(k.toString(), v)))
|
||||
'participants': jsonEncode(participants.map((k, v) => MapEntry(k.toString(), v))),
|
||||
'options': options.isEmpty ? null : options.join(','),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -210,6 +221,7 @@ class ChatsModule {
|
||||
|
||||
String? title;
|
||||
String? iconUrl;
|
||||
Set<String> options = const {};
|
||||
|
||||
if (type == 'DIALOG') {
|
||||
otherId = _otherParticipantId(chat['participants'], currentUserId);
|
||||
@@ -218,13 +230,25 @@ class ChatsModule {
|
||||
if (contact != null) {
|
||||
title = _nameFromContact(contact);
|
||||
iconUrl = contact['baseUrl'] as String?;
|
||||
final contactOpts = contact['options'];
|
||||
if (contactOpts is List) {
|
||||
options = contactOpts.whereType<String>().toSet();
|
||||
}
|
||||
} else {
|
||||
title = existing[id]?.title;
|
||||
iconUrl = existing[id]?.iconUrl;
|
||||
options = existing[id]?.options ?? const {};
|
||||
}
|
||||
} else {
|
||||
title = chat['title'] as String?;
|
||||
iconUrl = chat['baseIconUrl'] as String?;
|
||||
final chatOpts = chat['options'];
|
||||
if (chatOpts is Map) {
|
||||
options = {
|
||||
for (final entry in chatOpts.entries)
|
||||
if (entry.value == true && entry.key is String) entry.key as String,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
final lastMsg = chat['lastMessage'];
|
||||
@@ -276,7 +300,8 @@ class ChatsModule {
|
||||
dontDisturbUntil: dontDisturbUntil,
|
||||
isOnline: isOnline,
|
||||
seenTime: seenTime,
|
||||
participants: participants
|
||||
participants: participants,
|
||||
options: options,
|
||||
);
|
||||
} catch (e) {
|
||||
logger.e("Ошибка при парсинге чата: $e");
|
||||
|
||||
@@ -11,6 +11,7 @@ class CachedContact {
|
||||
final String? baseUrl;
|
||||
final String? baseRawUrl;
|
||||
final int updateTime;
|
||||
final Set<String> options;
|
||||
|
||||
const CachedContact({
|
||||
required this.id,
|
||||
@@ -22,8 +23,14 @@ class CachedContact {
|
||||
this.baseUrl,
|
||||
this.baseRawUrl,
|
||||
required this.updateTime,
|
||||
this.options = const {},
|
||||
});
|
||||
|
||||
bool get isOfficial => options.contains('OFFICIAL');
|
||||
bool get isBot => options.contains('BOT');
|
||||
bool get isServiceAccount => options.contains('SERVICE_ACCOUNT');
|
||||
bool get isVerified => isOfficial || isBot || isServiceAccount;
|
||||
|
||||
factory CachedContact.fromDbRow(Map<String, dynamic> row) => CachedContact(
|
||||
id: row['id'] as int,
|
||||
accountId: row['account_id'] as int,
|
||||
@@ -34,7 +41,13 @@ class CachedContact {
|
||||
baseUrl: row['base_url'] as String?,
|
||||
baseRawUrl: row['base_raw_url'] as String?,
|
||||
updateTime: row['update_time'] as int,
|
||||
options: _decodeOptions(row['options']),
|
||||
);
|
||||
|
||||
static Set<String> _decodeOptions(dynamic raw) {
|
||||
if (raw is! String || raw.isEmpty) return const {};
|
||||
return raw.split(',').where((s) => s.isNotEmpty).toSet();
|
||||
}
|
||||
}
|
||||
|
||||
class ContactsModule {
|
||||
@@ -126,6 +139,12 @@ class ContactsModule {
|
||||
lastName = name['lastName'] as String?;
|
||||
}
|
||||
|
||||
final optionsRaw = contact['options'];
|
||||
String? optionsStr;
|
||||
if (optionsRaw is List) {
|
||||
optionsStr = optionsRaw.whereType<String>().join(',');
|
||||
}
|
||||
|
||||
return {
|
||||
'id': id,
|
||||
'account_id': accountId,
|
||||
@@ -136,6 +155,7 @@ class ContactsModule {
|
||||
'base_url': contact['baseUrl'] as String?,
|
||||
'base_raw_url': contact['baseRawUrl'] as String?,
|
||||
'update_time': (contact['updateTime'] as int?) ?? 0,
|
||||
'options': optionsStr,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ class AppDatabase {
|
||||
final dbPath = await getDatabasesPath();
|
||||
return openDatabase(
|
||||
join(dbPath, 'komet.db'),
|
||||
version: 8,
|
||||
version: 9,
|
||||
onOpen: (db) => db.execute('PRAGMA foreign_keys = ON'),
|
||||
onCreate: (db, _) => _createTables(db),
|
||||
onUpgrade: (db, oldVersion, newVersion) async {
|
||||
@@ -193,6 +193,14 @@ class AppDatabase {
|
||||
'ALTER TABLE chats_cache ADD COLUMN participants TEXT',
|
||||
);
|
||||
}
|
||||
if (oldVersion < 9) {
|
||||
await db.execute(
|
||||
'ALTER TABLE contacts ADD COLUMN options TEXT',
|
||||
);
|
||||
await db.execute(
|
||||
'ALTER TABLE chats_cache ADD COLUMN options TEXT',
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -230,7 +238,8 @@ class AppDatabase {
|
||||
photo_id INTEGER,
|
||||
base_url TEXT,
|
||||
base_raw_url TEXT,
|
||||
update_time INTEGER NOT NULL DEFAULT 0
|
||||
update_time INTEGER NOT NULL DEFAULT 0,
|
||||
options TEXT
|
||||
)
|
||||
''';
|
||||
|
||||
@@ -262,6 +271,7 @@ class AppDatabase {
|
||||
is_online INTEGER NOT NULL DEFAULT 0,
|
||||
seen_time INTEGER NOT NULL DEFAULT 0,
|
||||
participants TEXT NOT NULL DEFAULT "",
|
||||
options TEXT,
|
||||
PRIMARY KEY (id, account_id)
|
||||
)
|
||||
''';
|
||||
|
||||
@@ -1102,6 +1102,7 @@ class _ChatListScreenState extends State<ChatListScreen>
|
||||
isOnline: chat.isOnline,
|
||||
unreadCount: chat.unreadCount,
|
||||
isMuted: chat.dontDisturbUntil > 0,
|
||||
isVerified: chat.isOfficial,
|
||||
chatType: "DIALOG",
|
||||
);
|
||||
} else {
|
||||
@@ -1134,6 +1135,7 @@ class _ChatListScreenState extends State<ChatListScreen>
|
||||
isOnline: chat.isOnline,
|
||||
unreadCount: chat.unreadCount,
|
||||
isMuted: chat.dontDisturbUntil > 0,
|
||||
isVerified: chat.isOfficial,
|
||||
chatType: chat.type,
|
||||
);
|
||||
}
|
||||
@@ -1749,6 +1751,7 @@ class _ChatListScreenState extends State<ChatListScreen>
|
||||
bool isRead = false,
|
||||
int unreadCount = 0,
|
||||
bool isMuted = false,
|
||||
bool isVerified = false,
|
||||
String chatType = "CHAT",
|
||||
}) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
@@ -1849,16 +1852,33 @@ Navigator.push(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
name,
|
||||
style: TextStyle(
|
||||
color: cs.onSurface,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.1,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
name,
|
||||
style: TextStyle(
|
||||
color: cs.onSurface,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.1,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (isVerified) ...[
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Symbols.verified,
|
||||
color: cs.primary,
|
||||
size: 16,
|
||||
weight: 600,
|
||||
fill: 1,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isMuted) ...[
|
||||
|
||||
@@ -304,14 +304,33 @@ class _ChatScreenState extends State<ChatScreen>
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.name,
|
||||
style: TextStyle(
|
||||
color: cs.onSurface,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'Outfit',
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
widget.name,
|
||||
style: TextStyle(
|
||||
color: cs.onSurface,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'Outfit',
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (chat?.isOfficial ?? false) ...[
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Symbols.verified,
|
||||
color: cs.primary,
|
||||
size: 16,
|
||||
weight: 600,
|
||||
fill: 1,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
Text(
|
||||
status ?? "",
|
||||
|
||||
@@ -100,15 +100,32 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
nameToDisplay,
|
||||
style: TextStyle(
|
||||
color: cs.onSurface,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
nameToDisplay,
|
||||
style: TextStyle(
|
||||
color: cs.onSurface,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (contact.isVerified) ...[
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Symbols.verified,
|
||||
color: cs.primary,
|
||||
size: 16,
|
||||
weight: 600,
|
||||
fill: 1,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
|
||||
Reference in New Issue
Block a user