fix flame problem

This commit is contained in:
otsmr 2025-02-07 19:06:17 +01:00
parent aa000afaf4
commit 8cdcc74683
13 changed files with 168 additions and 123 deletions

View file

@ -4,7 +4,6 @@ Don't be lonely, get twonly! Send pictures to a friend in real time and be sure
## TODOS bevor first beta ## TODOS bevor first beta
- Flammen Problem fixen
- Add no_screenshot plugin: https://pub.dev/packages/no_screenshot - Add no_screenshot plugin: https://pub.dev/packages/no_screenshot
- Bei mehreren neu Empfangen Nachrichten fixen - Bei mehreren neu Empfangen Nachrichten fixen
- Settings - Settings

View file

@ -1,6 +1,8 @@
import 'dart:collection'; import 'dart:collection';
import 'package:fixnum/fixnum.dart'; import 'package:fixnum/fixnum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:twonly/src/providers/messages_change_provider.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/components/flame.dart'; import 'package:twonly/src/components/flame.dart';
import 'package:twonly/src/components/headline.dart'; import 'package:twonly/src/components/headline.dart';
@ -87,6 +89,11 @@ class UserCheckbox extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
int? flameCounter = context
.watch<MessagesChangeProvider>()
.flamesCounter[user.userId.toInt()];
flameCounter ??= 0;
return Container( return Container(
padding: padding:
EdgeInsets.symmetric(horizontal: 3), // Padding inside the container EdgeInsets.symmetric(horizontal: 3), // Padding inside the container
@ -116,8 +123,8 @@ class UserCheckbox extends StatelessWidget {
: user.displayName, : user.displayName,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
if (user.flameCounter > 0) if (flameCounter > 0)
FlameCounterWidget(user, maxTotalMediaCounter), FlameCounterWidget(user, flameCounter, maxTotalMediaCounter),
Expanded(child: Container()), Expanded(child: Container()),
Checkbox( Checkbox(
value: isChecked, value: isChecked,

View file

@ -4,9 +4,11 @@ import 'package:twonly/src/model/contacts_model.dart';
class FlameCounterWidget extends StatelessWidget { class FlameCounterWidget extends StatelessWidget {
final Contact user; final Contact user;
final int maxTotalMediaCounter; final int maxTotalMediaCounter;
final int flameCounter;
const FlameCounterWidget( const FlameCounterWidget(
this.user, this.user,
this.flameCounter,
this.maxTotalMediaCounter, { this.maxTotalMediaCounter, {
super.key, super.key,
}); });
@ -19,7 +21,7 @@ class FlameCounterWidget extends StatelessWidget {
Text(""), Text(""),
const SizedBox(width: 5), const SizedBox(width: 5),
Text( Text(
user.flameCounter.toString(), flameCounter.toString(),
style: const TextStyle(fontSize: 13), style: const TextStyle(fontSize: 13),
), ),
Text( Text(

View file

@ -10,17 +10,13 @@ class Contact {
{required this.userId, {required this.userId,
required this.displayName, required this.displayName,
required this.accepted, required this.accepted,
required this.flameCounter,
required this.totalMediaCounter, required this.totalMediaCounter,
required this.lastUpdateOfFlameCounter,
required this.requested}); required this.requested});
final Int64 userId; final Int64 userId;
final String displayName; final String displayName;
final bool accepted; final bool accepted;
final bool requested; final bool requested;
final int flameCounter;
final int totalMediaCounter; final int totalMediaCounter;
final DateTime lastUpdateOfFlameCounter;
} }
class DbContacts extends CvModelBase { class DbContacts extends CvModelBase {
@ -41,16 +37,9 @@ class DbContacts extends CvModelBase {
static const columnBlocked = "blocked"; static const columnBlocked = "blocked";
final blocked = CvField<int>(columnBlocked); final blocked = CvField<int>(columnBlocked);
static const columnFlameCounter = "flame_counter";
final flameCounter = CvField<int>(columnFlameCounter);
static const columnTotalMediaCounter = "total_media_counter"; static const columnTotalMediaCounter = "total_media_counter";
final totalMediaCounter = CvField<int>(columnTotalMediaCounter); final totalMediaCounter = CvField<int>(columnTotalMediaCounter);
static const columnLastUpdateOfFlameCounter = "last_update_flame_counter";
final lastUpdateOfFlameCounter =
CvField<DateTime>(columnLastUpdateOfFlameCounter);
static const columnCreatedAt = "created_at"; static const columnCreatedAt = "created_at";
final createdAt = CvField<DateTime>(columnCreatedAt); final createdAt = CvField<DateTime>(columnCreatedAt);
@ -65,8 +54,6 @@ class DbContacts extends CvModelBase {
$columnRequested INT NOT NULL DEFAULT 0, $columnRequested INT NOT NULL DEFAULT 0,
$columnBlocked INT NOT NULL DEFAULT 0, $columnBlocked INT NOT NULL DEFAULT 0,
$columnTotalMediaCounter INT NOT NULL DEFAULT 0, $columnTotalMediaCounter INT NOT NULL DEFAULT 0,
$columnFlameCounter INT NOT NULL DEFAULT 0,
$columnLastUpdateOfFlameCounter DATETIME DEFAULT CURRENT_TIMESTAMP,
$columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP $columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
) )
"""; """;
@ -85,46 +72,22 @@ class DbContacts extends CvModelBase {
List<Map<String, dynamic>> result = await dbProvider.db!.query( List<Map<String, dynamic>> result = await dbProvider.db!.query(
tableName, tableName,
columns: [ columns: [columnTotalMediaCounter],
columnLastUpdateOfFlameCounter,
columnFlameCounter,
columnTotalMediaCounter
],
where: '$columnUserId = ?', where: '$columnUserId = ?',
whereArgs: [userId], whereArgs: [userId],
); );
if (result.isNotEmpty) { if (result.isNotEmpty) {
String lastUpdateString = result.first[columnLastUpdateOfFlameCounter];
DateTime lastUpdate = DateTime.tryParse(lastUpdateString)!;
if (timestamp.isAfter(lastUpdate)) {
int currentCount = result.first.cast()[columnFlameCounter];
int totalMediaCounter = result.first.cast()[columnTotalMediaCounter]; int totalMediaCounter = result.first.cast()[columnTotalMediaCounter];
if (lastUpdate.isAfter(DateTime.now() _updateFlameCounter(userId, totalMediaCounter + 1);
.subtract(Duration(seconds: nextFlameCounterInSeconds)))) {
_updateFlameCounter(userId, currentCount,
totalMediaCounter: totalMediaCounter + 1,
timestamp: timestamp); // just update the time
} else {
_updateFlameCounter(userId, (currentCount + 1),
totalMediaCounter: totalMediaCounter + 1, timestamp: timestamp);
}
}
}
globalCallBackOnContactChange(); globalCallBackOnContactChange();
} }
static Future _updateFlameCounter(int userId, int newCount,
{DateTime? timestamp, int? totalMediaCounter}) async {
timestamp ??= DateTime.now();
Map<String, dynamic> valuesToUpdate = {
columnFlameCounter: newCount,
columnLastUpdateOfFlameCounter: timestamp.toIso8601String()
};
if (totalMediaCounter != null) {
valuesToUpdate[columnTotalMediaCounter] = totalMediaCounter;
} }
static Future _updateFlameCounter(int userId, int totalMediaCounter) async {
Map<String, dynamic> valuesToUpdate = {
columnTotalMediaCounter: totalMediaCounter
};
await dbProvider.db!.update( await dbProvider.db!.update(
tableName, tableName,
valuesToUpdate, valuesToUpdate,
@ -141,9 +104,7 @@ class DbContacts extends CvModelBase {
columnDisplayName, columnDisplayName,
columnAccepted, columnAccepted,
columnRequested, columnRequested,
columnFlameCounter,
columnTotalMediaCounter, columnTotalMediaCounter,
columnLastUpdateOfFlameCounter,
columnCreatedAt columnCreatedAt
], ],
where: "$columnBlocked = 0"); where: "$columnBlocked = 0");
@ -151,27 +112,13 @@ class DbContacts extends CvModelBase {
List<Contact> parsedUsers = []; List<Contact> parsedUsers = [];
for (int i = 0; i < users.length; i++) { for (int i = 0; i < users.length; i++) {
DateTime lastUpdate =
DateTime.tryParse(users.cast()[i][columnLastUpdateOfFlameCounter])!;
int userId = users.cast()[i][columnUserId]; int userId = users.cast()[i][columnUserId];
int flameCounter = users.cast()[i][columnFlameCounter];
// if (lastUpdate.isBefore(DateTime.now()
// .subtract(Duration(seconds: nextFlameCounterInSeconds * 2)))) {
// _updateFlameCounter(userId, 0);
// flameCounter = 0;
// }
parsedUsers.add( parsedUsers.add(
Contact( Contact(
userId: Int64(userId), userId: Int64(userId),
totalMediaCounter: users.cast()[i][columnTotalMediaCounter], totalMediaCounter: users.cast()[i][columnTotalMediaCounter],
displayName: users.cast()[i][columnDisplayName], displayName: users.cast()[i][columnDisplayName],
accepted: users[i][columnAccepted] == 1, accepted: users[i][columnAccepted] == 1,
flameCounter: flameCounter,
lastUpdateOfFlameCounter: lastUpdate,
requested: users[i][columnRequested] == 1, requested: users[i][columnRequested] == 1,
), ),
); );

View file

@ -19,7 +19,7 @@ class DbMessage {
required this.messageAcknowledgeByUser, required this.messageAcknowledgeByUser,
required this.isDownloaded, required this.isDownloaded,
required this.messageAcknowledgeByServer, required this.messageAcknowledgeByServer,
required this.sendOrReceivedAt, required this.sendAt,
}); });
int messageId; int messageId;
@ -32,7 +32,7 @@ class DbMessage {
bool messageAcknowledgeByUser; bool messageAcknowledgeByUser;
bool isDownloaded; bool isDownloaded;
bool messageAcknowledgeByServer; bool messageAcknowledgeByServer;
DateTime sendOrReceivedAt; DateTime sendAt;
bool containsOtherMedia() { bool containsOtherMedia() {
if (messageOtherId == null) return false; if (messageOtherId == null) return false;
@ -99,8 +99,8 @@ class DbMessages extends CvModelBase {
final messageAcknowledgeByServer = final messageAcknowledgeByServer =
CvField<int>(columnMessageAcknowledgeByServer); CvField<int>(columnMessageAcknowledgeByServer);
static const columnSendOrReceivedAt = "message_send_or_received_at"; static const columnSendAt = "message_send_or_received_at";
final sendOrReceivedAt = CvField<DateTime>(columnSendOrReceivedAt); final sendAt = CvField<DateTime>(columnSendAt);
static const columnUpdatedAt = "updated_at"; static const columnUpdatedAt = "updated_at";
final updatedAt = CvField<DateTime>(columnUpdatedAt); final updatedAt = CvField<DateTime>(columnUpdatedAt);
@ -116,12 +116,33 @@ class DbMessages extends CvModelBase {
$columnMessageAcknowledgeByServer INTEGER NOT NULL DEFAULT 0, $columnMessageAcknowledgeByServer INTEGER NOT NULL DEFAULT 0,
$columnMessageContentJson TEXT NOT NULL, $columnMessageContentJson TEXT NOT NULL,
$columnMessageOpenedAt DATETIME DEFAULT NULL, $columnMessageOpenedAt DATETIME DEFAULT NULL,
$columnSendOrReceivedAt DATETIME DEFAULT CURRENT_TIMESTAMP, $columnSendAt DATETIME DEFAULT CURRENT_TIMESTAMP,
$columnUpdatedAt DATETIME DEFAULT CURRENT_TIMESTAMP $columnUpdatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
) )
"""; """;
} }
static Future<List<(DateTime, int?)>> getMessageDates(int otherUserId) async {
final List<Map<String, dynamic>> maps = await dbProvider.db!.rawQuery('''
SELECT $columnSendAt, $columnMessageOtherId
FROM $tableName
WHERE $columnOtherUserId = ?
ORDER BY $columnSendAt DESC;
''', [otherUserId]);
try {
return List.generate(maps.length, (i) {
return (
DateTime.tryParse(maps[i][columnSendAt])!,
maps[i][columnMessageOtherId]
);
});
} catch (e) {
Logger("error parsing datetime: $e");
return [];
}
}
static Future deleteMessageById(int messageId) async { static Future deleteMessageById(int messageId) async {
await dbProvider.db!.delete( await dbProvider.db!.delete(
tableName, tableName,
@ -147,14 +168,14 @@ class DbMessages extends CvModelBase {
return null; return null;
} }
static Future<int?> insertMyMessage( static Future<int?> insertMyMessage(int userIdFrom, MessageKind kind,
int userIdFrom, MessageKind kind, MessageContent content) async { MessageContent content, DateTime messageSendAt) async {
try { try {
int messageId = await dbProvider.db!.insert(tableName, { int messageId = await dbProvider.db!.insert(tableName, {
columnMessageKind: kind.index, columnMessageKind: kind.index,
columnMessageContentJson: jsonEncode(content.toJson()), columnMessageContentJson: jsonEncode(content.toJson()),
columnOtherUserId: userIdFrom, columnOtherUserId: userIdFrom,
columnSendOrReceivedAt: DateTime.now().toIso8601String() columnSendAt: messageSendAt.toIso8601String()
}); });
globalCallBackOnMessageChange(userIdFrom); globalCallBackOnMessageChange(userIdFrom);
return messageId; return messageId;
@ -165,7 +186,7 @@ class DbMessages extends CvModelBase {
} }
static Future<int?> insertOtherMessage(int userIdFrom, MessageKind kind, static Future<int?> insertOtherMessage(int userIdFrom, MessageKind kind,
int messageOtherId, String jsonContent) async { int messageOtherId, String jsonContent, DateTime messageSendAt) async {
try { try {
int messageId = await dbProvider.db!.insert(tableName, { int messageId = await dbProvider.db!.insert(tableName, {
columnMessageOtherId: messageOtherId, columnMessageOtherId: messageOtherId,
@ -175,7 +196,7 @@ class DbMessages extends CvModelBase {
columnMessageAcknowledgeByUser: columnMessageAcknowledgeByUser:
0, // ack in case of sending corresponds to the opened flag 0, // ack in case of sending corresponds to the opened flag
columnOtherUserId: userIdFrom, columnOtherUserId: userIdFrom,
columnSendOrReceivedAt: DateTime.now().toIso8601String() columnSendAt: messageSendAt.toIso8601String()
}); });
globalCallBackOnMessageChange(userIdFrom); globalCallBackOnMessageChange(userIdFrom);
return messageId; return messageId;
@ -330,13 +351,8 @@ class DbMessages extends CvModelBase {
} }
@override @override
List<CvField> get fields => [ List<CvField> get fields =>
messageId, [messageId, messageKind, messageContentJson, messageOpenedAt, sendAt];
messageKind,
messageContentJson,
messageOpenedAt,
sendOrReceivedAt
];
static Future<List<DbMessage>> convertToDbMessage( static Future<List<DbMessage>> convertToDbMessage(
List<dynamic> fromDb) async { List<dynamic> fromDb) async {
@ -366,8 +382,7 @@ class DbMessages extends CvModelBase {
} }
parsedUsers.add( parsedUsers.add(
DbMessage( DbMessage(
sendOrReceivedAt: sendAt: DateTime.tryParse(fromDb[i][columnSendAt])!,
DateTime.tryParse(fromDb[i][columnSendOrReceivedAt])!,
messageId: fromDb[i][columnMessageId], messageId: fromDb[i][columnMessageId],
messageOtherId: messageOtherId, messageOtherId: messageOtherId,
otherUserId: fromDb[i][columnOtherUserId], otherUserId: fromDb[i][columnOtherUserId],

View file

@ -46,13 +46,8 @@ Future tryTransmitMessages() async {
if (encryptedMedia != null) { if (encryptedMedia != null) {
final content = retransmit[i].messageContent; final content = retransmit[i].messageContent;
if (content is MediaMessageContent) { if (content is MediaMessageContent) {
uploadMediaFile( uploadMediaFile(msgId, Int64(retransmit[i].otherUserId), encryptedMedia,
msgId, content.isRealTwonly, content.maxShowTime, retransmit[i].sendAt);
Int64(retransmit[i].otherUserId),
encryptedMedia,
content.isRealTwonly,
content.maxShowTime,
);
} }
} }
} }
@ -88,18 +83,18 @@ Future<Result> encryptAndSendMessage(Int64 userId, Message msg) async {
Future sendTextMessage(Int64 target, String message) async { Future sendTextMessage(Int64 target, String message) async {
MessageContent content = TextMessageContent(text: message); MessageContent content = TextMessageContent(text: message);
DateTime messageSendAt =
DateTime.now().subtract(Duration(days: 1, minutes: 120));
int? messageId = await DbMessages.insertMyMessage( int? messageId = await DbMessages.insertMyMessage(
target.toInt(), target.toInt(), MessageKind.textMessage, content, messageSendAt);
MessageKind.textMessage,
content,
);
if (messageId == null) return; if (messageId == null) return;
Message msg = Message( Message msg = Message(
kind: MessageKind.textMessage, kind: MessageKind.textMessage,
messageId: messageId, messageId: messageId,
content: content, content: content,
timestamp: DateTime.now(), timestamp: messageSendAt,
); );
encryptAndSendMessage(target, msg); encryptAndSendMessage(target, msg);
@ -112,6 +107,7 @@ Future uploadMediaFile(
Uint8List encryptedMedia, Uint8List encryptedMedia,
bool isRealTwonly, bool isRealTwonly,
int maxShowTime, int maxShowTime,
DateTime messageSendAt,
) async { ) async {
Box box = await getMediaStorage(); Box box = await getMediaStorage();
@ -156,7 +152,7 @@ Future uploadMediaFile(
maxShowTime: maxShowTime, maxShowTime: maxShowTime,
isRealTwonly: isRealTwonly, isRealTwonly: isRealTwonly,
isVideo: false), isVideo: false),
timestamp: DateTime.now(), timestamp: messageSendAt,
), ),
); );
} }
@ -167,6 +163,7 @@ Future encryptAndUploadMediaFile(
bool isRealTwonly, bool isRealTwonly,
int maxShowTime, int maxShowTime,
) async { ) async {
DateTime messageSendAt = DateTime.now();
int? messageId = await DbMessages.insertMyMessage( int? messageId = await DbMessages.insertMyMessage(
target.toInt(), target.toInt(),
MessageKind.image, MessageKind.image,
@ -174,7 +171,9 @@ Future encryptAndUploadMediaFile(
downloadToken: [], downloadToken: [],
maxShowTime: maxShowTime, maxShowTime: maxShowTime,
isRealTwonly: isRealTwonly, isRealTwonly: isRealTwonly,
isVideo: false)); isVideo: false,
),
messageSendAt);
// isRealTwonly, // isRealTwonly,
if (messageId == null) return; if (messageId == null) return;
@ -185,8 +184,8 @@ Future encryptAndUploadMediaFile(
return; return;
} }
await uploadMediaFile( await uploadMediaFile(messageId, target, encryptBytes, isRealTwonly,
messageId, target, encryptBytes, isRealTwonly, maxShowTime); maxShowTime, messageSendAt);
} }
Future sendImage( Future sendImage(
@ -253,11 +252,11 @@ Future<Uint8List?> getDownloadedMedia(
final box = await getMediaStorage(); final box = await getMediaStorage();
Uint8List? media = box.get("${mediaToken}_downloaded"); Uint8List? media = box.get("${mediaToken}_downloaded");
// int fromUserId = box.get("${mediaToken}_fromUserId"); int fromUserId = box.get("${mediaToken}_fromUserId");
// await userOpenedOtherMessage(fromUserId, messageOtherId); await userOpenedOtherMessage(fromUserId, messageOtherId);
// box.delete(mediaToken.toString()); box.delete(mediaToken.toString());
// box.put("${mediaToken}_downloaded", "deleted"); box.put("${mediaToken}_downloaded", "deleted");
// box.delete("${mediaToken}_fromUserId"); box.delete("${mediaToken}_fromUserId");
return media; return media;
} }

View file

@ -131,7 +131,11 @@ Future<client.Response> handleNewMessage(
} else { } else {
String content = jsonEncode(message.content.toJson()); String content = jsonEncode(message.content.toJson());
int? messageId = await DbMessages.insertOtherMessage( int? messageId = await DbMessages.insertOtherMessage(
fromUserId.toInt(), message.kind, message.messageId!, content); fromUserId.toInt(),
message.kind,
message.messageId!,
content,
message.timestamp);
if (messageId == null) { if (messageId == null) {
return client.Response()..error = ErrorCode.InternalError; return client.Response()..error = ErrorCode.InternalError;

View file

@ -1,15 +1,18 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/contacts_model.dart';
import 'package:twonly/src/model/messages_model.dart'; import 'package:twonly/src/model/messages_model.dart';
import 'package:twonly/src/utils/misc.dart';
/// This provider does always contains the latest messages send or received /// This provider does always contains the latest messages send or received
/// for every contact. /// for every contact.
class MessagesChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { class MessagesChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
final Map<int, DbMessage> _lastMessage = <int, DbMessage>{}; final Map<int, DbMessage> _lastMessage = <int, DbMessage>{};
final Map<int, int> _changeCounter = <int, int>{}; final Map<int, int> _changeCounter = <int, int>{};
final Map<int, int> _flamesCounter = <int, int>{};
Map<int, DbMessage> get lastMessage => _lastMessage; Map<int, DbMessage> get lastMessage => _lastMessage;
Map<int, int> get changeCounter => _changeCounter; Map<int, int> get changeCounter => _changeCounter;
Map<int, int> get flamesCounter => _flamesCounter;
void updateLastMessageFor(int targetUserId) async { void updateLastMessageFor(int targetUserId) async {
DbMessage? last = DbMessage? last =
@ -21,6 +24,7 @@ class MessagesChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
changeCounter[targetUserId] = 0; changeCounter[targetUserId] = 0;
} }
changeCounter[targetUserId] = changeCounter[targetUserId]! + 1; changeCounter[targetUserId] = changeCounter[targetUserId]! + 1;
flamesCounter[targetUserId] = await getFlamesForOtherUser(targetUserId);
notifyListeners(); notifyListeners();
} }
@ -33,6 +37,8 @@ class MessagesChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
if (last != null) { if (last != null) {
_lastMessage[last.otherUserId] = last; _lastMessage[last.otherUserId] = last;
} }
flamesCounter[contact.userId.toInt()] =
await getFlamesForOtherUser(contact.userId.toInt());
} }
notifyListeners(); notifyListeners();
} }

View file

@ -7,6 +7,7 @@ import 'package:gal/gal.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:twonly/src/model/messages_model.dart';
import 'package:twonly/src/proto/api/error.pb.dart'; import 'package:twonly/src/proto/api/error.pb.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -131,3 +132,48 @@ Future<Uint8List?> getCompressedImage(Uint8List imageBytes) async {
); );
return result; return result;
} }
int getFlameCounter(List<DateTime> dates) {
if (dates.isEmpty) return 0;
int flamesCount = 0;
DateTime lastFlameCount = DateTime.now();
if (dates[0].difference(lastFlameCount).inDays == 0) {
flamesCount = 1;
lastFlameCount = dates[0];
}
// print(dates[0]);
for (int i = 1; i < dates.length; i++) {
// print(
// "${dates[i]} ${dates[i].difference(dates[i - 1]).inDays} ${dates[i].difference(lastFlameCount).inDays}");
if (dates[i].difference(dates[i - 1]).inDays == 0) {
if (lastFlameCount.difference(dates[i]).inDays == 1) {
flamesCount++;
lastFlameCount = dates[i];
}
} else {
break; // Stop counting if there's a break in the sequence
}
}
return flamesCount;
}
Future<int> getFlamesForOtherUser(int otherUserId) async {
List<(DateTime, int?)> dates = await DbMessages.getMessageDates(otherUserId);
// print("Dates ${dates.length}");
if (dates.isEmpty) return 0;
List<DateTime> received =
dates.where((x) => x.$2 != null).map((x) => x.$1).toList();
List<DateTime> send =
dates.where((x) => x.$2 == null).map((x) => x.$1).toList();
// print("Received ${received.length} and send ${send.length}");
int a = getFlameCounter(received);
int b = getFlameCounter(send);
// print("Received $a and send $b");
return min(a, b);
}

View file

@ -3,12 +3,14 @@ import 'dart:typed_data';
import 'package:fixnum/fixnum.dart'; import 'package:fixnum/fixnum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:twonly/src/components/best_friends_selector.dart'; import 'package:twonly/src/components/best_friends_selector.dart';
import 'package:twonly/src/components/flame.dart'; import 'package:twonly/src/components/flame.dart';
import 'package:twonly/src/components/headline.dart'; import 'package:twonly/src/components/headline.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/contacts_model.dart';
import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/api/api.dart';
import 'package:twonly/src/providers/messages_change_provider.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/home_view.dart'; import 'package:twonly/src/views/home_view.dart';
@ -51,10 +53,14 @@ class _ShareImageView extends State<ShareImageView> {
} }
Future _updateUsers(List<Contact> users) async { Future _updateUsers(List<Contact> users) async {
Map<int, int> flameCounters =
context.read<MessagesChangeProvider>().flamesCounter;
// Sort contacts by flameCounter and then by totalMediaCounter // Sort contacts by flameCounter and then by totalMediaCounter
users.sort((a, b) { users.sort((a, b) {
// First, compare by flameCounter // First, compare by flameCounter
int flameComparison = b.flameCounter.compareTo(a.flameCounter); int flameComparison = flameCounters[b.userId.toInt()]!
.compareTo(flameCounters[a.userId.toInt()]!);
if (flameComparison != 0) { if (flameComparison != 0) {
return flameComparison; // Sort by flameCounter in descending order return flameComparison; // Sort by flameCounter in descending order
} }
@ -74,7 +80,8 @@ class _ShareImageView extends State<ShareImageView> {
List<Contact> otherUsers = []; List<Contact> otherUsers = [];
for (var contact in users) { for (var contact in users) {
if (contact.flameCounter > 0 && bestFriends.length < 6) { if (flameCounters[contact.userId.toInt()]! > 0 &&
bestFriends.length < 6) {
bestFriends.add(contact); bestFriends.add(contact);
} else { } else {
otherUsers.add(contact); otherUsers.add(contact);
@ -179,7 +186,7 @@ class _ShareImageView extends State<ShareImageView> {
// TODO: pop back to the HomeView page popUntil did not work. check later how to improve in case of pushing more then 2 // TODO: pop back to the HomeView page popUntil did not work. check later how to improve in case of pushing more then 2
Navigator.pop(context); Navigator.pop(context);
Navigator.pop(context); Navigator.pop(context);
globalUpdateOfHomeViewPageIndex(0); globalUpdateOfHomeViewPageIndex(1);
}, },
style: ButtonStyle( style: ButtonStyle(
padding: WidgetStateProperty.all<EdgeInsets>( padding: WidgetStateProperty.all<EdgeInsets>(
@ -216,16 +223,21 @@ class UserList extends StatelessWidget {
// Step 1: Sort the users alphabetically // Step 1: Sort the users alphabetically
users.sort((a, b) => a.displayName.compareTo(b.displayName)); users.sort((a, b) => a.displayName.compareTo(b.displayName));
Map<int, int> flameCounters =
context.watch<MessagesChangeProvider>().flamesCounter;
return ListView.builder( return ListView.builder(
restorationId: 'new_message_users_list', restorationId: 'new_message_users_list',
itemCount: users.length, itemCount: users.length,
itemBuilder: (BuildContext context, int i) { itemBuilder: (BuildContext context, int i) {
Contact user = users[i]; Contact user = users[i];
int? flameCounter = flameCounters[user.userId.toInt()];
flameCounter ??= 0;
return ListTile( return ListTile(
title: Row(children: [ title: Row(children: [
Text(user.displayName), Text(user.displayName),
if (user.flameCounter > 0) if (flameCounter > 0)
FlameCounterWidget(user, maxTotalMediaCounter), FlameCounterWidget(user, flameCounter, maxTotalMediaCounter),
]), ]),
leading: InitialsAvatar( leading: InitialsAvatar(
displayName: user.displayName, displayName: user.displayName,

View file

@ -45,8 +45,8 @@ class _ChatListViewState extends State<ChatListView> {
.toList(); .toList();
activeUsers.sort((b, a) { activeUsers.sort((b, a) {
return lastMessages[a.userId.toInt()]! return lastMessages[a.userId.toInt()]!
.sendOrReceivedAt .sendAt
.compareTo(lastMessages[b.userId.toInt()]!.sendOrReceivedAt); .compareTo(lastMessages[b.userId.toInt()]!.sendAt);
}); });
int maxTotalMediaCounter = 0; int maxTotalMediaCounter = 0;
@ -165,9 +165,8 @@ class _UserListItem extends State<UserListItem> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
int lastMessageInSeconds = DateTime.now() int lastMessageInSeconds =
.difference(widget.lastMessage.sendOrReceivedAt) DateTime.now().difference(widget.lastMessage.sendAt).inSeconds;
.inSeconds;
MessageSendState state = widget.lastMessage.getSendState(); MessageSendState state = widget.lastMessage.getSendState();
bool isDownloading = false; bool isDownloading = false;
@ -183,6 +182,11 @@ class _UserListItem extends State<UserListItem> {
.contains(token.toString()); .contains(token.toString());
} }
int? flameCounter = context
.watch<MessagesChangeProvider>()
.flamesCounter[widget.user.userId.toInt()];
flameCounter ??= 0;
return UserContextMenu( return UserContextMenu(
user: widget.user, user: widget.user,
child: ListTile( child: ListTile(
@ -196,8 +200,9 @@ class _UserListItem extends State<UserListItem> {
formatDuration(lastMessageInSeconds), formatDuration(lastMessageInSeconds),
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
), ),
if (widget.user.flameCounter > 0) if (flameCounter > 0)
FlameCounterWidget(widget.user, widget.maxTotalMediaCounter), FlameCounterWidget(
widget.user, flameCounter, widget.maxTotalMediaCounter),
], ],
), ),
leading: InitialsAvatar(displayName: widget.user.displayName), leading: InitialsAvatar(displayName: widget.user.displayName),

View file

@ -24,6 +24,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
DateTime? canBeSeenUntil; DateTime? canBeSeenUntil;
int maxShowTime = 999999; int maxShowTime = 999999;
bool isRealTwonly = false; bool isRealTwonly = false;
Timer? _timer;
// DateTime opened; // DateTime opened;
@override @override
@ -72,7 +73,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
} }
startTimer() { startTimer() {
Future.delayed(canBeSeenUntil!.difference(DateTime.now()), () { _timer = Timer(canBeSeenUntil!.difference(DateTime.now()), () {
if (context.mounted) { if (context.mounted) {
Navigator.pop(context); Navigator.pop(context);
} }
@ -96,6 +97,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
_timer?.cancel();
} }
@override @override

View file

@ -66,8 +66,8 @@ class HomeViewState extends State<HomeView> {
activePageIdx = index; activePageIdx = index;
}, },
children: [ children: [
ChatListView(),
CameraPreviewViewPermission(), CameraPreviewViewPermission(),
ChatListView(),
], ],
), ),
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: BottomNavigationBar(
@ -76,12 +76,12 @@ class HomeViewState extends State<HomeView> {
selectedIconTheme: selectedIconTheme:
IconThemeData(color: const Color.fromARGB(255, 255, 255, 255)), IconThemeData(color: const Color.fromARGB(255, 255, 255, 255)),
items: [ items: [
BottomNavigationBarItem(
icon: FaIcon(FontAwesomeIcons.solidComments), label: ""),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: FaIcon(FontAwesomeIcons.camera), icon: FaIcon(FontAwesomeIcons.camera),
label: "", label: "",
), ),
BottomNavigationBarItem(
icon: FaIcon(FontAwesomeIcons.solidComments), label: ""),
// BottomNavigationBarItem( // BottomNavigationBarItem(
// icon: FaIcon(FontAwesomeIcons.userShield), label: ""), // icon: FaIcon(FontAwesomeIcons.userShield), label: ""),
], ],