sending an receiving works

This commit is contained in:
otsmr 2025-01-30 22:25:37 +01:00
parent e0d420b78d
commit 59146674b6
16 changed files with 217 additions and 83 deletions

View file

@ -7,6 +7,7 @@ import 'package:twonly/src/providers/api_provider.dart';
import 'package:twonly/src/providers/db_provider.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:twonly/src/providers/download_change_provider.dart';
import 'package:twonly/src/providers/messages_change_provider.dart';
import 'package:twonly/src/providers/contacts_change_provider.dart';
import 'package:twonly/src/utils/misc.dart';
@ -61,6 +62,7 @@ void main() async {
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => MessagesChangeProvider()),
ChangeNotifierProvider(create: (_) => DownloadChangeProvider()),
ChangeNotifierProvider(create: (_) => ContactChangeProvider()),
],
child: MyApp(settingsController: settingsController),

View file

@ -1,6 +1,7 @@
import 'package:provider/provider.dart';
import 'package:twonly/main.dart';
import 'package:twonly/src/providers/contacts_change_provider.dart';
import 'package:twonly/src/providers/download_change_provider.dart';
import 'package:twonly/src/providers/messages_change_provider.dart';
import 'package:twonly/src/utils/storage.dart';
import 'package:twonly/src/views/onboarding_view.dart';
@ -22,6 +23,7 @@ Function(bool) globalCallbackConnectionState = (a) {};
// these two callbacks are called on updated to the corresponding database
Function globalCallBackOnContactChange = () {};
Function(int) globalCallBackOnMessageChange = (a) {};
Function(List<int>, bool) globalCallBackOnDownloadChange = (a, b) {};
/// The Widget that configures your application.
class MyApp extends StatefulWidget {
@ -61,6 +63,10 @@ class _MyAppState extends State<MyApp> {
context.read<ContactChangeProvider>().update();
};
globalCallBackOnDownloadChange = (token, add) {
context.read<DownloadChangeProvider>().update(token, add);
};
globalCallBackOnMessageChange = (userId) {
context.read<MessagesChangeProvider>().updateLastMessageFor(userId);
};
@ -73,6 +79,7 @@ class _MyAppState extends State<MyApp> {
void dispose() {
// disable globalCallbacks to the flutter tree
globalCallbackConnectionState = (a) {};
globalCallBackOnDownloadChange = (a, b) {};
globalCallBackOnContactChange = () {};
globalCallBackOnMessageChange = (a) {};
super.dispose();
@ -143,7 +150,8 @@ class _MyAppState extends State<MyApp> {
if (snapshot.hasData) {
return snapshot.data!
? HomeView(
settingsController: widget.settingsController)
settingsController: widget.settingsController,
)
: _showOnboarding
? OnboardingView(
callbackOnSuccess: () {
@ -152,10 +160,12 @@ class _MyAppState extends State<MyApp> {
});
},
)
: RegisterView(callbackOnSuccess: () {
_isUserCreated = isUserCreated();
setState(() {});
});
: RegisterView(
callbackOnSuccess: () {
_isUserCreated = isUserCreated();
setState(() {});
},
);
} else {
return Container();
}

View file

@ -1,6 +1,9 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/model/messages_model.dart';
import 'package:twonly/src/providers/download_change_provider.dart';
enum MessageSendState {
received,
@ -12,21 +15,32 @@ enum MessageSendState {
}
class MessageSendStateIcon extends StatelessWidget {
final MessageSendState state;
final MessageKind kind;
final bool isDownloaded;
final DbMessage message;
final MainAxisAlignment mainAxisAlignment;
const MessageSendStateIcon(this.state, this.isDownloaded, this.kind,
{super.key});
const MessageSendStateIcon(this.message,
{super.key, this.mainAxisAlignment = MainAxisAlignment.end});
@override
Widget build(BuildContext context) {
Widget icon = Placeholder();
String text = "";
Color color = kind.getColor(Theme.of(context).colorScheme.primary);
Color color =
message.messageKind.getColor(Theme.of(context).colorScheme.primary);
switch (state) {
Widget loaderIcon = Row(
children: [
SizedBox(
width: 10,
height: 10,
child: CircularProgressIndicator(strokeWidth: 1, color: color),
),
SizedBox(width: 2),
],
);
switch (message.getSendState()) {
case MessageSendState.receivedOpened:
icon = Icon(Icons.crop_square, size: 14, color: color);
text = "Received";
@ -45,29 +59,38 @@ class MessageSendStateIcon extends StatelessWidget {
break;
case MessageSendState.sending:
case MessageSendState.receiving:
icon = Row(
children: [
SizedBox(
width: 10,
height: 10,
child: CircularProgressIndicator(strokeWidth: 1, color: color),
),
SizedBox(width: 2),
],
);
icon = loaderIcon;
text = "Sending";
break;
}
if (!isDownloaded) {
if (!message.isDownloaded) {
text = "Tap do load";
}
bool isDownloading = false;
if (message.messageContent != null &&
message.messageContent!.downloadToken != null) {
isDownloading = context
.watch<DownloadChangeProvider>()
.currentlyDownloading
.contains(message.messageContent!.downloadToken!);
}
if (isDownloading) {
text = "Downloading";
icon = loaderIcon;
}
return Row(
mainAxisAlignment: mainAxisAlignment,
children: [
icon,
const SizedBox(width: 3),
Text(text, style: TextStyle(fontSize: 12)),
Text(
text,
style: TextStyle(fontSize: 12),
),
const SizedBox(width: 5),
],
);

View file

@ -238,6 +238,17 @@ class DbMessages extends CvModelBase {
}
}
static Future _updateByOtherMessageId(
int fromUserId, int messageId, Map<String, dynamic> data) async {
await dbProvider.db!.update(
tableName,
data,
where: "$columnMessageOtherId = ?",
whereArgs: [messageId],
);
globalCallBackOnMessageChange(fromUserId);
}
// this ensures that the message id can be spoofed by another person
static Future _updateByMessageIdOther(
int fromUserId, int messageId, Map<String, dynamic> data) async {
@ -250,14 +261,15 @@ class DbMessages extends CvModelBase {
globalCallBackOnMessageChange(fromUserId);
}
static Future userOpenedMessage(int messageId) async {
static Future userOpenedOtherMessage(
int otherMessageId, int fromUserId) async {
Map<String, dynamic> data = {
columnMessageOpenedAt: DateTime.now().toIso8601String(),
};
await _updateByMessageId(messageId, data);
await _updateByOtherMessageId(fromUserId, otherMessageId, data);
}
static Future userOpenedMessageOtherUser(
static Future otherUserOpenedMyMessage(
int fromUserId, int messageId, DateTime openedAt) async {
Map<String, dynamic> data = {
columnMessageOpenedAt: openedAt.toIso8601String(),

View file

@ -854,11 +854,15 @@ class ApplicationData_UploadData extends $pb.GeneratedMessage {
class ApplicationData_DownloadData extends $pb.GeneratedMessage {
factory ApplicationData_DownloadData({
$core.List<$core.int>? uploadToken,
$core.int? offset,
}) {
final $result = create();
if (uploadToken != null) {
$result.uploadToken = uploadToken;
}
if (offset != null) {
$result.offset = offset;
}
return $result;
}
ApplicationData_DownloadData._() : super();
@ -867,6 +871,7 @@ class ApplicationData_DownloadData extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ApplicationData.DownloadData', package: const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), createEmptyInstance: create)
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'uploadToken', $pb.PbFieldType.OY)
..a<$core.int>(2, _omitFieldNames ? '' : 'offset', $pb.PbFieldType.OU3)
..hasRequiredFields = false
;
@ -899,6 +904,15 @@ class ApplicationData_DownloadData extends $pb.GeneratedMessage {
$core.bool hasUploadToken() => $_has(0);
@$pb.TagNumber(1)
void clearUploadToken() => clearField(1);
@$pb.TagNumber(2)
$core.int get offset => $_getIZ(1);
@$pb.TagNumber(2)
set offset($core.int v) { $_setUnsignedInt32(1, v); }
@$pb.TagNumber(2)
$core.bool hasOffset() => $_has(1);
@$pb.TagNumber(2)
void clearOffset() => clearField(2);
}
enum ApplicationData_ApplicationData {

View file

@ -182,6 +182,7 @@ const ApplicationData_DownloadData$json = {
'1': 'DownloadData',
'2': [
{'1': 'upload_token', '3': 1, '4': 1, '5': 12, '10': 'uploadToken'},
{'1': 'offset', '3': 2, '4': 1, '5': 13, '10': 'offset'},
],
};
@ -204,8 +205,8 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
'cl9pZBgBIAEoA1IGdXNlcklkGi0KEkdldFByZWtleXNCeVVzZXJJZBIXCgd1c2VyX2lkGAEgAS'
'gDUgZ1c2VySWQaEAoOR2V0VXBsb2FkVG9rZW4aWwoKVXBsb2FkRGF0YRIhCgx1cGxvYWRfdG9r'
'ZW4YASABKAxSC3VwbG9hZFRva2VuEhYKBm9mZnNldBgCIAEoDVIGb2Zmc2V0EhIKBGRhdGEYAy'
'ABKAxSBGRhdGEaMQoMRG93bmxvYWREYXRhEiEKDHVwbG9hZF90b2tlbhgBIAEoDFILdXBsb2Fk'
'VG9rZW5CEQoPQXBwbGljYXRpb25EYXRh');
'ABKAxSBGRhdGEaSQoMRG93bmxvYWREYXRhEiEKDHVwbG9hZF90b2tlbhgBIAEoDFILdXBsb2Fk'
'VG9rZW4SFgoGb2Zmc2V0GAIgASgNUgZvZmZzZXRCEQoPQXBwbGljYXRpb25EYXRh');
@$core.Deprecated('Use responseDescriptor instead')
const Response$json = {

View file

@ -29,6 +29,7 @@ class ErrorCode extends $pb.ProtobufEnum {
static const ErrorCode OnlyOneSessionAllowed = ErrorCode._(1010, _omitEnumNames ? '' : 'OnlyOneSessionAllowed');
static const ErrorCode UploadLimitReached = ErrorCode._(1011, _omitEnumNames ? '' : 'UploadLimitReached');
static const ErrorCode InvalidUpdateToken = ErrorCode._(1012, _omitEnumNames ? '' : 'InvalidUpdateToken');
static const ErrorCode InvalidOffset = ErrorCode._(1013, _omitEnumNames ? '' : 'InvalidOffset');
static const $core.List<ErrorCode> values = <ErrorCode> [
Unknown,
@ -46,6 +47,7 @@ class ErrorCode extends $pb.ProtobufEnum {
OnlyOneSessionAllowed,
UploadLimitReached,
InvalidUpdateToken,
InvalidOffset,
];
static final $core.Map<$core.int, ErrorCode> _byValue = $pb.ProtobufEnum.initByValue(values);

View file

@ -32,6 +32,7 @@ const ErrorCode$json = {
{'1': 'OnlyOneSessionAllowed', '2': 1010},
{'1': 'UploadLimitReached', '2': 1011},
{'1': 'InvalidUpdateToken', '2': 1012},
{'1': 'InvalidOffset', '2': 1013},
],
};
@ -43,5 +44,6 @@ final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode(
'VzZXJuYW1lTm90Rm91bmQQ7QcSFQoQVXNlcm5hbWVOb3RWYWxpZBDuBxIVChBJbnZhbGlkUHVi'
'bGljS2V5EO8HEiAKG1Nlc3Npb25BbHJlYWR5QXV0aGVudGljYXRlZBDwBxIcChdTZXNzaW9uTm'
'90QXV0aGVudGljYXRlZBDxBxIaChVPbmx5T25lU2Vzc2lvbkFsbG93ZWQQ8gcSFwoSVXBsb2Fk'
'TGltaXRSZWFjaGVkEPMHEhcKEkludmFsaWRVcGRhdGVUb2tlbhD0Bw==');
'TGltaXRSZWFjaGVkEPMHEhcKEkludmFsaWRVcGRhdGVUb2tlbhD0BxISCg1JbnZhbGlkT2Zmc2'
'V0EPUH');

View file

@ -7,6 +7,7 @@ import 'package:hive/hive.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:twonly/main.dart';
import 'package:twonly/src/app.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/model/messages_model.dart';
import 'package:twonly/src/proto/api/error.pb.dart';
@ -56,7 +57,7 @@ Future sendTextMessage(Int64 target, String message) async {
timestamp: DateTime.now(),
);
await encryptAndSendMessage(target, msg);
encryptAndSendMessage(target, msg);
}
Future sendImageToSingleTarget(Int64 target, Uint8List imageBytes) async {
@ -122,7 +123,7 @@ Future sendImage(List<Int64> userIds, String imagePath) async {
}
}
Future tryDownloadMedia(List<int> imageToken, {bool force = false}) async {
Future tryDownloadMedia(List<int> mediaToken, {bool force = false}) async {
if (!force) {
// TODO: create option to enable download via mobile data
final List<ConnectivityResult> connectivityResult =
@ -132,32 +133,39 @@ Future tryDownloadMedia(List<int> imageToken, {bool force = false}) async {
return;
}
}
Logger("tryDownloadMedia").info("Downloading: $imageToken");
apiProvider.triggerDownload(imageToken);
Logger("tryDownloadMedia").info("Downloading: $mediaToken");
int offset = 0;
final box = await getMediaStorage();
Uint8List? media = box.get("$mediaToken");
if (media != null && media.isNotEmpty) {
offset = media.length;
}
globalCallBackOnDownloadChange(mediaToken, true);
apiProvider.triggerDownload(mediaToken, offset);
}
Future userOpenedMessage(int fromUserId, int messageId) async {
await DbMessages.userOpenedMessage(messageId);
Future userOpenedOtherMessage(int fromUserId, int messageOtherId) async {
await DbMessages.userOpenedOtherMessage(messageOtherId, fromUserId);
encryptAndSendMessage(
Int64(fromUserId),
Message(
kind: MessageKind.opened,
messageId: messageId,
messageId: messageOtherId,
timestamp: DateTime.now(),
),
);
}
Future<Uint8List?> getDownloadedMedia(
List<int> mediaToken, int messageId) async {
List<int> mediaToken, int messageOtherId) async {
final box = await getMediaStorage();
Uint8List? media = box.get("${mediaToken}_downloaded");
int fromUserId = box.get("${mediaToken}_fromUserId");
await userOpenedMessage(fromUserId, messageId);
box.delete(mediaToken.toString());
box.put("${mediaToken}_downloaded", "deleted");
box.delete("${mediaToken}_fromUserId");
await userOpenedOtherMessage(fromUserId, messageOtherId);
// box.delete(mediaToken.toString());
// box.put("${mediaToken}_downloaded", "deleted");
// box.delete("${mediaToken}_fromUserId");
return media;
}

View file

@ -57,7 +57,8 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
Uint8List downloadedBytes;
if (buffered != null) {
if (data.offset != buffered.length) {
return client.Response()..error = ErrorCode.BadRequest;
// Logger("handleDownloadData").error(object)
return client.Response()..error = ErrorCode.InvalidOffset;
}
var b = BytesBuilder();
b.add(buffered);
@ -70,16 +71,20 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
if (data.fin) {
SignalHelper.getSignalStore();
int fromUserId = box.get("${data.uploadToken}_fromUserId")!;
Uint8List? rawBytes =
await SignalHelper.decryptBytes(downloadedBytes, Int64(fromUserId));
int? fromUserId = box.get("${data.uploadToken}_fromUserId");
if (fromUserId != null) {
print(fromUserId);
Uint8List? rawBytes =
await SignalHelper.decryptBytes(downloadedBytes, Int64(fromUserId));
if (rawBytes != null) {
box.put("${data.uploadToken}_downloaded", rawBytes);
if (rawBytes != null) {
box.put("${data.uploadToken}_downloaded", rawBytes);
}
box.delete(boxId);
globalCallBackOnMessageChange(fromUserId);
globalCallBackOnDownloadChange(data.uploadToken, false);
}
box.delete(boxId);
globalCallBackOnMessageChange(fromUserId);
} else {
box.put(boxId, downloadedBytes);
}
@ -102,7 +107,7 @@ Future<client.Response> handleNewMessage(
}
break;
case MessageKind.opened:
await DbMessages.userOpenedMessageOtherUser(
await DbMessages.otherUserOpenedMyMessage(
fromUserId.toInt(),
message.messageId!,
message.timestamp,

View file

@ -255,8 +255,11 @@ class ApiProvider {
return await _sendRequestV0(req);
}
Future<Result> triggerDownload(List<int> token) async {
var get = ApplicationData_DownloadData()..uploadToken = token;
Future<Result> triggerDownload(List<int> token, int offset) async {
log.info("Offset: ${offset}");
var get = ApplicationData_DownloadData()
..uploadToken = token
..offset = offset;
var appData = ApplicationData()..downloaddata = get;
var req = createClientToServerFromApplicationData(appData);
return await _sendRequestV0(req);

View file

@ -0,0 +1,16 @@
import 'dart:collection';
import 'package:flutter/foundation.dart';
class DownloadChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
final HashSet<List<int>> _currentlyDownloading = HashSet<List<int>>();
HashSet<List<int>> get currentlyDownloading => _currentlyDownloading;
void update(List<int> token, bool add) {
if (add) {
_currentlyDownloading.add(token);
} else {
_currentlyDownloading.remove(token);
}
}
}

View file

@ -6,7 +6,10 @@ import 'package:twonly/src/model/messages_model.dart';
/// for every contact.
class MessagesChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
final Map<int, DbMessage> _lastMessage = <int, DbMessage>{};
final Map<int, int> _changeCounter = <int, int>{};
Map<int, DbMessage> get lastMessage => _lastMessage;
Map<int, int> get changeCounter => _changeCounter;
void updateLastMessageFor(int targetUserId) async {
DbMessage? last =
@ -14,6 +17,10 @@ class MessagesChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
if (last != null) {
_lastMessage[last.otherUserId] = last;
}
if (!changeCounter.containsKey(targetUserId)) {
changeCounter[targetUserId] = 0;
}
changeCounter[targetUserId] = changeCounter[targetUserId]! + 1;
}
void init() async {

View file

@ -1,4 +1,5 @@
import 'package:cv/cv.dart';
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
@ -17,10 +18,10 @@ class ChatListEntry extends StatelessWidget {
final DbMessage message;
final Contact user;
final bool lastMessageFromSameUser;
@override
Widget build(BuildContext context) {
bool right = message.messageOtherId == null;
MessageSendState state = message.getSendState();
Widget child = Container();
@ -29,15 +30,15 @@ class ChatListEntry extends StatelessWidget {
case MessageKind.textMessage:
child = Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width *
0.8, // Maximum 80% of the screen width
maxWidth: MediaQuery.of(context).size.width * 0.8,
),
padding: EdgeInsets.symmetric(
vertical: 4, horizontal: 10), // Add some padding around the text
decoration: BoxDecoration(
color: right
? Colors.deepPurpleAccent
: Colors.blueAccent, // Set the background color
? const Color.fromARGB(107, 124, 77, 255)
: const Color.fromARGB(
83, 68, 137, 255), // Set the background color
borderRadius: BorderRadius.circular(12.0), // Set border radius
),
child: Text(
@ -71,7 +72,7 @@ class ChatListEntry extends StatelessWidget {
},
child: Container(
padding: EdgeInsets.all(10),
width: 200,
width: 150,
decoration: BoxDecoration(
border: Border.all(
color: color, // Set the background color
@ -79,10 +80,13 @@ class ChatListEntry extends StatelessWidget {
),
borderRadius: BorderRadius.circular(12.0), // Set border radius
),
child: MessageSendStateIcon(
state,
message.isDownloaded,
message.messageKind,
child: Align(
alignment: Alignment.centerRight,
child: MessageSendStateIcon(
message,
mainAxisAlignment:
right ? MainAxisAlignment.center : MainAxisAlignment.center,
),
),
),
);
@ -94,7 +98,7 @@ class ChatListEntry extends StatelessWidget {
child: Padding(
padding: lastMessageFromSameUser
? EdgeInsets.only(top: 5, bottom: 0, right: 10, left: 10)
: EdgeInsets.all(10),
: EdgeInsets.only(top: 5, bottom: 20, right: 10, left: 10),
child: child),
);
}
@ -112,34 +116,50 @@ class ChatItemDetailsView extends StatefulWidget {
class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
List<DbMessage> _messages = [];
int lastChangeCounter = 0;
final TextEditingController newMessageController = TextEditingController();
HashSet<int> alreadyReportedOpened = HashSet<int>();
@override
void initState() {
super.initState();
_loadAsync();
_loadAsync(updateOpenStatus: true);
}
Future _loadAsync() async {
Future _loadAsync({bool updateOpenStatus = false}) async {
_messages =
await DbMessages.getAllMessagesForUser(widget.user.userId.toInt());
setState(() {});
if (updateOpenStatus) {
_messages.where((x) => x.messageOpenedAt == null).forEach((message) {
if (message.messageOtherId != null &&
message.messageKind == MessageKind.textMessage) {
if (!alreadyReportedOpened.contains(message.messageOtherId!)) {
userOpenedOtherMessage(
message.otherUserId, message.messageOtherId!);
alreadyReportedOpened.add(message.messageOtherId!);
}
}
});
}
}
Future _sendMessage() async {
String text = newMessageController.text;
if (text == "") return;
sendTextMessage(widget.user.userId, newMessageController.text);
await sendTextMessage(widget.user.userId, newMessageController.text);
_loadAsync();
newMessageController.clear();
}
@override
Widget build(BuildContext context) {
final messages = context.watch<MessagesChangeProvider>().lastMessage;
if (messages.containsKey(widget.user.userId.toInt()) &&
_messages.isNotEmpty) {
final lastMessage = messages[widget.user.userId.toInt()];
if (lastMessage!.messageId != _messages[0].messageId) {
_loadAsync();
final changeCounter = context.watch<MessagesChangeProvider>().changeCounter;
if (changeCounter.containsKey(widget.user.userId.toInt())) {
if (changeCounter[widget.user.userId.toInt()] != lastChangeCounter) {
_loadAsync(updateOpenStatus: true);
lastChangeCounter = changeCounter[widget.user.userId.toInt()]!;
}
}
// messages = messages.reversed.toList();
@ -163,7 +183,10 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
_messages[i].messageOtherId != null);
}
return ChatListEntry(
_messages[i], widget.user, lastMessageFromSameUser);
_messages[i],
widget.user,
lastMessageFromSameUser,
);
},
),
),

View file

@ -156,8 +156,7 @@ class _UserListItem extends State<UserListItem> {
title: Text(widget.user.displayName),
subtitle: Row(
children: [
MessageSendStateIcon(state, widget.lastMessage.isDownloaded,
widget.lastMessage.messageKind),
MessageSendStateIcon(widget.lastMessage),
Text(""),
const SizedBox(width: 5),
Text(
@ -190,13 +189,19 @@ class _UserListItem extends State<UserListItem> {
tryDownloadMedia(token, force: true);
return;
}
if (state == MessageSendState.received &&
widget.lastMessage.containsOtherMedia()) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return MediaViewerView(widget.user, widget.lastMessage);
}),
);
return;
}
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
if (state == MessageSendState.received &&
widget.lastMessage.containsOtherMedia()) {
return MediaViewerView(widget.user, widget.lastMessage);
}
return ChatItemDetailsView(user: widget.user);
}),
);

View file

@ -27,7 +27,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
Future _initAsync() async {
List<int> token = widget.message.messageContent!.downloadToken!;
_imageByte = await getDownloadedMedia(token, widget.message.messageId);
_imageByte =
await getDownloadedMedia(token, widget.message.messageOtherId!);
setState(() {});
}