mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 11:18:41 +00:00
fix flame problem
This commit is contained in:
parent
aa000afaf4
commit
8cdcc74683
13 changed files with 168 additions and 123 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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],
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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: ""),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue