fix: фикс отступов, фикс перемещения чатов при запуске

This commit is contained in:
Jganenokk
2026-07-26 13:19:23 +07:00
parent a6cb13dcf9
commit ba1f63ad05
5 changed files with 174 additions and 6 deletions
+1
View File
@@ -598,6 +598,7 @@ class AccountModule {
profile.id,
config.cast<dynamic, dynamic>(),
);
await chats.applyFavorites(profile.id);
final userConfig = config['user'];
if (userConfig is Map) {
await AppDatabase.savePrivacyConfig(profile.id, jsonEncode(userConfig));
+3 -2
View File
@@ -163,13 +163,14 @@ _resolveLastMessage(dynamic lastMsg) {
Map<int, CachedChat> existing,
) {
final config = chatsConfig[id.toString()] ?? chatsConfig[id];
final ex = existing[id];
if (config is Map) {
final configFav = config['favIndex'] as int?;
return (
favIndex: config['favIndex'] as int?,
favIndex: (configFav != null && configFav > 0) ? configFav : ex?.favIndex,
dontDisturbUntil: (config['dontDisturbUntil'] as int?) ?? 0,
);
}
final ex = existing[id];
if (ex != null) {
return (favIndex: ex.favIndex, dontDisturbUntil: ex.dontDisturbUntil);
}
+1 -4
View File
@@ -745,10 +745,7 @@ class MessageBubble extends StatelessWidget {
if (showAvatar)
_buildLeadingAvatar(cs)
else if (showAvatarSlot && chatType == "CHAT")
const CircleAvatar(
radius: 15,
backgroundColor: Color(0x00000000),
),
const SizedBox(width: 30),
Column(
crossAxisAlignment: isMe
? CrossAxisAlignment.end
+89
View File
@@ -0,0 +1,89 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:komet/backend/modules/chat_parsing.dart';
import 'package:komet/backend/modules/chats.dart';
const int _me = 4242;
const int _chatId = -1000000000001;
CachedChat _cached({int? favIndex}) => CachedChat(
id: _chatId,
accountId: _me,
type: 'CHAT',
title: 'pinned chat',
unreadCount: 0,
lastEventTime: 1700000000000,
cachedAt: 0,
favIndex: favIndex,
dontDisturbUntil: 0,
isOnline: false,
seenTime: 0,
participants: {_me: 1700000000000},
);
CachedChat _parse({
Map<dynamic, dynamic> chatsConfig = const {},
CachedChat? existing,
}) => parseChatRow(
{
'id': _chatId,
'type': 'CHAT',
'title': 'pinned chat',
'lastEventTime': 1700000000000,
'participants': {'$_me': 1700000000000},
},
_me,
_me,
const {},
chatsConfig,
const {},
existing == null ? const {} : {_chatId: existing},
0,
)!;
void main() {
group('login sync keeps pins', () {
test('a zero favIndex in the config does not unpin a cached chat', () {
final parsed = _parse(
chatsConfig: {
'$_chatId': {'favIndex': 0, 'dontDisturbUntil': 0},
},
existing: _cached(favIndex: 3),
);
expect(parsed.favIndex, 3);
});
test('a real favIndex from the config wins', () {
final parsed = _parse(
chatsConfig: {
'$_chatId': {'favIndex': 2, 'dontDisturbUntil': 0},
},
existing: _cached(favIndex: 3),
);
expect(parsed.favIndex, 2);
});
test('mute settings still come from the config', () {
final parsed = _parse(
chatsConfig: {
'$_chatId': {'favIndex': 0, 'dontDisturbUntil': -1},
},
existing: _cached(favIndex: 3),
);
expect(parsed.dontDisturbUntil, -1);
expect(parsed.isMuted, isTrue);
});
test('a chat with no cached pin stays unpinned', () {
final parsed = _parse(
chatsConfig: {
'$_chatId': {'favIndex': 0, 'dontDisturbUntil': 0},
},
);
expect(parsed.favIndex, isNull);
});
test('without a config entry the cached pin survives', () {
expect(_parse(existing: _cached(favIndex: 5)).favIndex, 5);
});
});
}
+80
View File
@@ -35,6 +35,56 @@ CachedMessage _message({
: null,
);
Future<void> _pumpColumn(
WidgetTester tester,
List<CachedMessage> messages, {
required String chatType,
double textScale = 1,
}) async {
tester.view.physicalSize = const Size(1080, 2400);
tester.view.devicePixelRatio = 2.5;
addTearDown(tester.view.reset);
await tester.pumpWidget(
MaterialApp(
locale: const Locale('ru'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold(
body: MediaQuery(
data: MediaQueryData(textScaler: TextScaler.linear(textScale)),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (var i = 0; i < messages.length; i++)
MessageBubble(
key: ValueKey('bubble$i'),
message: messages[i],
prevMessage: i > 0 ? messages[i - 1] : null,
nextMessage: i < messages.length - 1 ? messages[i + 1] : null,
isMe: false,
myId: _me,
chatType: chatType,
),
],
),
),
),
),
);
await tester.pump();
}
Rect _bubbleRect(WidgetTester tester, int index, String text) {
final label = find.descendant(
of: find.byKey(ValueKey('bubble$index')),
matching: find.text(text),
);
final box = find.ancestor(of: label, matching: find.byType(Container)).first;
return tester.getTopLeft(box) & tester.getSize(box);
}
Future<void> _pumpBubble(WidgetTester tester, CachedMessage message) async {
tester.view.physicalSize = const Size(1080, 2400);
tester.view.devicePixelRatio = 2.5;
@@ -103,6 +153,36 @@ void main() {
expect(clock.right, closeTo(header.right, 1));
});
testWidgets('grouped bubbles keep the same gap with and without avatars', (
tester,
) async {
final stream = [
for (var i = 0; i < 4; i++)
CachedMessage(
id: '$i',
accountId: _me,
chatId: 2,
senderId: 404,
text: 'm$i',
time: DateTime(2026, 1, 1, 12, 54).millisecondsSinceEpoch + i * 1000,
status: 'sent',
),
];
double gapAt(WidgetTester tester, int index) =>
_bubbleRect(tester, index + 1, 'm${index + 1}').top -
_bubbleRect(tester, index, 'm$index').bottom;
await _pumpColumn(tester, stream, chatType: 'DIALOG', textScale: 0.35);
final dialogGaps = [for (var i = 0; i < 3; i++) gapAt(tester, i)];
await _pumpColumn(tester, stream, chatType: 'CHAT', textScale: 0.35);
final groupGaps = [for (var i = 0; i < 3; i++) gapAt(tester, i)];
expect(dialogGaps, everyElement(2.0));
expect(groupGaps, dialogGaps);
});
testWidgets('a bubble without a header or reply still hugs its text', (
tester,
) async {