закрепление и мьют чатов

This commit is contained in:
Jganenok
2026-05-17 19:49:29 +07:00
parent 3aa6cdb52c
commit 4987ae30ab
9 changed files with 601 additions and 148 deletions
+37
View File
@@ -0,0 +1,37 @@
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
enum BubbleBehavior { mutable, immutable }
class AppBubbleBehavior {
static const prefKey = 'app_bubble_behavior';
static final ValueNotifier<BubbleBehavior> current = ValueNotifier(
BubbleBehavior.mutable,
);
static Future<BubbleBehavior> load() async {
final prefs = await SharedPreferences.getInstance();
final val = prefs.getString(prefKey);
return _parse(val);
}
static Future<void> save(BubbleBehavior behavior) async {
current.value = behavior;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(prefKey, behavior.name);
}
static BubbleBehavior _parse(String? val) {
if (val == BubbleBehavior.immutable.name) return BubbleBehavior.immutable;
return BubbleBehavior.mutable;
}
static String label(BubbleBehavior behavior) {
switch (behavior) {
case BubbleBehavior.mutable:
return 'Изменяемая';
case BubbleBehavior.immutable:
return 'Неизменяемая';
}
}
}
+17 -6
View File
@@ -471,15 +471,26 @@ class AppDatabase {
);
}
static Future<List<Map<String, dynamic>>> findDialogChatsByParticipant(
int accountId,
int contactId,
) async {
static Future<List<Map<String, dynamic>>> loadDialogChats(int accountId) async {
final db = await _instance;
return db.query(
'chats_cache',
where: "account_id = ? AND type = 'DIALOG' AND participants LIKE ?",
whereArgs: [accountId, '%"$contactId":%'],
where: "account_id = ? AND type = 'DIALOG'",
whereArgs: [accountId],
);
}
static Future<List<Map<String, dynamic>>> loadChatsByIds(
int accountId,
List<int> ids,
) async {
if (ids.isEmpty) return const [];
final db = await _instance;
final placeholders = List.filled(ids.length, '?').join(',');
return db.query(
'chats_cache',
where: 'account_id = ? AND id IN ($placeholders)',
whereArgs: [accountId, ...ids],
);
}
+81
View File
@@ -0,0 +1,81 @@
import 'package:flutter/widgets.dart';
import '../config/app_bubble_behavior.dart';
import '../config/app_bubble_shape.dart';
const double kBubbleBigRadius = 20;
const double kBubbleSmallRadius = 4;
const Radius _big = Radius.circular(kBubbleBigRadius);
const Radius _small = Radius.circular(kBubbleSmallRadius);
BorderRadius computeBubbleRadius({
required bool isMe,
required bool isTop,
required bool isBottom,
required BubbleStyle style,
required BubbleBehavior behavior,
bool hasPhotoWithCaption = false,
bool hasMultiplePhotosNoCaption = false,
}) {
final isSingle = isTop && isBottom;
if (hasPhotoWithCaption && (isTop || isBottom)) {
return BorderRadius.only(
topLeft: _big,
topRight: _big,
bottomLeft: isMe ? _big : _small,
bottomRight: _small,
);
}
if (hasMultiplePhotosNoCaption && isBottom) {
return BorderRadius.only(
topLeft: isMe ? _big : _small,
topRight: _small,
bottomLeft: isMe ? _big : _small,
bottomRight: isMe ? _small : _big,
);
}
final base = style == BubbleStyle.desktop ? _small : _big;
Radius tl = base, tr = base, bl = base, br = base;
if (behavior == BubbleBehavior.immutable || isSingle) {
return BorderRadius.only(
topLeft: tl,
topRight: tr,
bottomLeft: bl,
bottomRight: br,
);
}
if (isTop) {
if (isMe) {
br = _small;
} else {
bl = _small;
}
} else if (isBottom) {
if (isMe) {
tr = _small;
} else {
tl = _small;
}
} else {
if (isMe) {
tr = _small;
br = _small;
} else {
tl = _small;
bl = _small;
}
}
return BorderRadius.only(
topLeft: tl,
topRight: tr,
bottomLeft: bl,
bottomRight: br,
);
}