mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 13:08:42 +00:00
retransmission works
This commit is contained in:
parent
444edf0230
commit
10dadcee7c
8 changed files with 255 additions and 90 deletions
|
|
@ -477,13 +477,14 @@
|
||||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = CN332ZUGRP;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.connect;
|
PRODUCT_BUNDLE_IDENTIFIER = eu.twonly;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|
@ -664,13 +665,14 @@
|
||||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = CN332ZUGRP;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.connect;
|
PRODUCT_BUNDLE_IDENTIFIER = eu.twonly;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
|
@ -687,13 +689,14 @@
|
||||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = CN332ZUGRP;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.connect;
|
PRODUCT_BUNDLE_IDENTIFIER = eu.twonly;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
|
<true/>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
|
|
@ -24,6 +26,16 @@
|
||||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>To create photos that can be shared.</string>
|
||||||
|
<key>NSFaceIDUsageDescription</key>
|
||||||
|
<string>To protect others twonlies!</string>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>To create videos that can be securely shared.</string>
|
||||||
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
|
<string>Store photos in the gallery.</string>
|
||||||
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
|
<true/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UIMainStoryboardFile</key>
|
||||||
|
|
@ -41,18 +53,5 @@
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
|
||||||
<true/>
|
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>NSCameraUsageDescription</key>
|
|
||||||
<string>To create photos that can be shared.</string>
|
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
|
||||||
<string>To create videos that can be securely shared.</string>
|
|
||||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
|
||||||
<string>Store photos in the gallery.</string>
|
|
||||||
<key>NSFaceIDUsageDescription</key>
|
|
||||||
<string>To protect others twonlies!</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,19 @@ String beautifulZoomScale(double scale) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CameraZoomButtonsState extends State<CameraZoomButtons> {
|
class _CameraZoomButtonsState extends State<CameraZoomButtons> {
|
||||||
|
bool showWideAngleZoom = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
initAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future initAsync() async {
|
||||||
|
showWideAngleZoom = (await widget.controller.getMinZoomLevel()) < 1;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var zoomButtonStyle = TextButton.styleFrom(
|
var zoomButtonStyle = TextButton.styleFrom(
|
||||||
|
|
@ -47,34 +60,37 @@ class _CameraZoomButtonsState extends State<CameraZoomButtons> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
if (showWideAngleZoom)
|
||||||
style: zoomButtonStyle.copyWith(
|
TextButton(
|
||||||
foregroundColor: WidgetStateProperty.all(
|
style: zoomButtonStyle.copyWith(
|
||||||
(widget.scaleFactor < 1) ? Colors.yellow : Colors.white,
|
foregroundColor: WidgetStateProperty.all(
|
||||||
|
(widget.scaleFactor < 1) ? Colors.yellow : Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
var level = await widget.controller.getMinZoomLevel();
|
||||||
|
widget.updateScaleFactor(level);
|
||||||
|
},
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: widget.controller.getMinZoomLevel(),
|
||||||
|
builder: (context, snap) {
|
||||||
|
if (snap.hasData) {
|
||||||
|
var minLevel =
|
||||||
|
beautifulZoomScale(snap.data!.toDouble());
|
||||||
|
var currentLevel =
|
||||||
|
beautifulZoomScale(widget.scaleFactor);
|
||||||
|
return Text(
|
||||||
|
widget.scaleFactor < 1
|
||||||
|
? "${currentLevel}x"
|
||||||
|
: "${minLevel}x",
|
||||||
|
style: zoomTextStyle,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Text("");
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
|
||||||
var level = await widget.controller.getMinZoomLevel();
|
|
||||||
widget.updateScaleFactor(level);
|
|
||||||
},
|
|
||||||
child: FutureBuilder(
|
|
||||||
future: widget.controller.getMinZoomLevel(),
|
|
||||||
builder: (context, snap) {
|
|
||||||
if (snap.hasData) {
|
|
||||||
var minLevel = beautifulZoomScale(snap.data!.toDouble());
|
|
||||||
var currentLevel = beautifulZoomScale(widget.scaleFactor);
|
|
||||||
return Text(
|
|
||||||
widget.scaleFactor < 1
|
|
||||||
? "${currentLevel}x"
|
|
||||||
: "${minLevel}x",
|
|
||||||
style: zoomTextStyle,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Text("");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
TextButton(
|
||||||
style: zoomButtonStyle.copyWith(
|
style: zoomButtonStyle.copyWith(
|
||||||
foregroundColor: WidgetStateProperty.all(
|
foregroundColor: WidgetStateProperty.all(
|
||||||
|
|
|
||||||
|
|
@ -15,42 +15,32 @@ import 'package:twonly/src/utils/signal.dart' as SignalHelper;
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
|
|
||||||
Future tryTransmitMessages() async {
|
Future tryTransmitMessages() async {
|
||||||
// List<Message> retransmit =
|
List<Message> retransmit =
|
||||||
// await twonlyDatabase.getAllMessagesForRetransmitting();
|
await twonlyDatabase.messagesDao.getAllMessagesForRetransmitting();
|
||||||
|
|
||||||
// if (retransmit.isEmpty) return;
|
if (retransmit.isEmpty) return;
|
||||||
|
|
||||||
// Logger("api.dart").info("try sending messages: ${retransmit.length}");
|
Logger("api.dart").info("try sending messages: ${retransmit.length}");
|
||||||
|
|
||||||
// Box box = await getMediaStorage();
|
Box box = await getMediaStorage();
|
||||||
// for (int i = 0; i < retransmit.length; i++) {
|
for (int i = 0; i < retransmit.length; i++) {
|
||||||
// int msgId = retransmit[i].messageId;
|
int msgId = retransmit[i].messageId;
|
||||||
|
|
||||||
// Uint8List? bytes = box.get("retransmit-$msgId-textmessage");
|
Uint8List? bytes = box.get("retransmit-$msgId-textmessage");
|
||||||
// if (bytes != null) {
|
if (bytes != null) {
|
||||||
// Result resp = await apiProvider.sendTextMessage(
|
Result resp = await apiProvider.sendTextMessage(
|
||||||
// retransmit[i].contactId,
|
retransmit[i].contactId,
|
||||||
// bytes,
|
bytes,
|
||||||
// );
|
);
|
||||||
|
if (resp.isSuccess) {
|
||||||
// if (resp.isSuccess) {
|
await twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||||
// await twonlyDatabase.updateMessageByMessageId(
|
msgId, MessagesCompanion(acknowledgeByServer: Value(true)));
|
||||||
// msgId, MessagesCompanion(acknowledgeByServer: Value(true)));
|
box.delete("retransmit-$msgId-textmessage");
|
||||||
|
} else {
|
||||||
// box.delete("retransmit-$msgId-textmessage");
|
// in case of error do nothing. As the message is not removed the app will try again when relaunched
|
||||||
// } else {
|
}
|
||||||
// // in case of error do nothing. As the message is not removed the app will try again when relaunched
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// Uint8List? encryptedMedia = await box.get("retransmit-$msgId-media");
|
|
||||||
// if (encryptedMedia != null) {
|
|
||||||
// MediaMessageContent content =
|
|
||||||
// MediaMessageContent.fromJson(jsonDecode(retransmit[i].contentJson!));
|
|
||||||
// uploadMediaFile(msgId, retransmit[i].contactId, encryptedMedia,
|
|
||||||
// content.isRealTwonly, content.maxShowTime, retransmit[i].sendAt);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this functions ensures that the message is received by the server and in case of errors will try again later
|
// this functions ensures that the message is received by the server and in case of errors will try again later
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:collection';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
import 'package:cryptography_plus/cryptography_plus.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/app.dart';
|
import 'package:twonly/src/app.dart';
|
||||||
|
|
@ -37,11 +38,40 @@ Future tryDownloadAllMediaFiles() async {
|
||||||
|
|
||||||
class Metadata {
|
class Metadata {
|
||||||
late List<int> userIds;
|
late List<int> userIds;
|
||||||
late HashMap<int, int> messageIds;
|
late Map<int, int> messageIds;
|
||||||
late Uint8List imageBytes;
|
|
||||||
late bool isRealTwonly;
|
late bool isRealTwonly;
|
||||||
late int maxShowTime;
|
late int maxShowTime;
|
||||||
late DateTime messageSendAt;
|
late DateTime messageSendAt;
|
||||||
|
|
||||||
|
Metadata();
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
// Convert Map<int, int> to Map<String, int> for JSON encoding
|
||||||
|
Map<String, int> stringKeyMessageIds =
|
||||||
|
messageIds.map((key, value) => MapEntry(key.toString(), value));
|
||||||
|
|
||||||
|
return {
|
||||||
|
'userIds': userIds,
|
||||||
|
'messageIds': stringKeyMessageIds,
|
||||||
|
'isRealTwonly': isRealTwonly,
|
||||||
|
'maxShowTime': maxShowTime,
|
||||||
|
'messageSendAt': messageSendAt.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Metadata.fromJson(Map<String, dynamic> json) {
|
||||||
|
Metadata state = Metadata();
|
||||||
|
state.userIds = List<int>.from(json['userIds']);
|
||||||
|
|
||||||
|
// Convert Map<String, dynamic> to Map<int, int>
|
||||||
|
state.messageIds = (json['messageIds'] as Map<String, dynamic>)
|
||||||
|
.map((key, value) => MapEntry(int.parse(key), value as int));
|
||||||
|
|
||||||
|
state.isRealTwonly = json['isRealTwonly'];
|
||||||
|
state.maxShowTime = json['maxShowTime'];
|
||||||
|
state.messageSendAt = DateTime.parse(json['messageSendAt']);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrepareState {
|
class PrepareState {
|
||||||
|
|
@ -50,11 +80,75 @@ class PrepareState {
|
||||||
late List<int> encryptionMac;
|
late List<int> encryptionMac;
|
||||||
late List<int> encryptedBytes;
|
late List<int> encryptedBytes;
|
||||||
late List<int> encryptionNonce;
|
late List<int> encryptionNonce;
|
||||||
|
|
||||||
|
PrepareState();
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'sha2Hash': sha2Hash,
|
||||||
|
'encryptionKey': encryptionKey,
|
||||||
|
'encryptionMac': encryptionMac,
|
||||||
|
'encryptedBytes': encryptedBytes,
|
||||||
|
'encryptionNonce': encryptionNonce,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory PrepareState.fromJson(Map<String, dynamic> json) {
|
||||||
|
PrepareState state = PrepareState();
|
||||||
|
state.sha2Hash = List<int>.from(json['sha2Hash']);
|
||||||
|
state.encryptionKey = List<int>.from(json['encryptionKey']);
|
||||||
|
state.encryptionMac = List<int>.from(json['encryptionMac']);
|
||||||
|
state.encryptedBytes = List<int>.from(json['encryptedBytes']);
|
||||||
|
state.encryptionNonce = List<int>.from(json['encryptionNonce']);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UploadState {
|
class UploadState {
|
||||||
late List<int> uploadToken;
|
late List<int> uploadToken;
|
||||||
late List<List<int>> downloadTokens;
|
late List<List<int>> downloadTokens;
|
||||||
|
|
||||||
|
UploadState();
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'uploadToken': uploadToken,
|
||||||
|
'downloadTokens': downloadTokens,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory UploadState.fromJson(Map<String, dynamic> json) {
|
||||||
|
UploadState state = UploadState();
|
||||||
|
state.uploadToken = List<int>.from(json['uploadToken']);
|
||||||
|
state.downloadTokens = List<List<int>>.from(
|
||||||
|
json['downloadTokens'].map((token) => List<int>.from(token)),
|
||||||
|
);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class States {
|
||||||
|
late Metadata metadata;
|
||||||
|
late PrepareState prepareState;
|
||||||
|
|
||||||
|
States({
|
||||||
|
required this.metadata,
|
||||||
|
required this.prepareState,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'metadata': metadata.toJson(),
|
||||||
|
'prepareState': prepareState.toJson(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory States.fromJson(Map<String, dynamic> json) {
|
||||||
|
return States(
|
||||||
|
metadata: Metadata.fromJson(json['metadata']),
|
||||||
|
prepareState: PrepareState.fromJson(json['prepareState']),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageUploader {
|
class ImageUploader {
|
||||||
|
|
@ -208,10 +302,11 @@ Future sendImage(
|
||||||
metadata.userIds = userIds;
|
metadata.userIds = userIds;
|
||||||
metadata.isRealTwonly = isRealTwonly;
|
metadata.isRealTwonly = isRealTwonly;
|
||||||
metadata.maxShowTime = maxShowTime;
|
metadata.maxShowTime = maxShowTime;
|
||||||
metadata.messageIds = HashMap();
|
metadata.messageIds = {};
|
||||||
metadata.messageSendAt = DateTime.now();
|
metadata.messageSendAt = DateTime.now();
|
||||||
|
|
||||||
// store prepareState and metadata...
|
String stateId = prepareState.sha2Hash.toString();
|
||||||
|
States states = States(metadata: metadata, prepareState: prepareState);
|
||||||
|
|
||||||
// at this point it is safe inform the user about the process of sending the image..
|
// at this point it is safe inform the user about the process of sending the image..
|
||||||
for (final userId in metadata.userIds) {
|
for (final userId in metadata.userIds) {
|
||||||
|
|
@ -240,13 +335,66 @@ Future sendImage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Box storage = await getMediaStorage();
|
||||||
|
|
||||||
|
String? mediaFilesJson = storage.get("mediaUploads");
|
||||||
|
Map<String, dynamic> allMediaFiles = {};
|
||||||
|
|
||||||
|
if (mediaFilesJson != null) {
|
||||||
|
// allMediaFiles = jsonDecode(mediaFilesJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
allMediaFiles[stateId] = jsonEncode(states.toJson());
|
||||||
|
storage.put("mediaUploads", jsonEncode(allMediaFiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadMediaState(stateId, prepareState, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future retransmitMediaFiles() async {
|
||||||
|
Box storage = await getMediaStorage();
|
||||||
|
String? mediaFilesJson = storage.get("mediaUploads");
|
||||||
|
|
||||||
|
if (mediaFilesJson == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> allMediaFiles = jsonDecode(mediaFilesJson);
|
||||||
|
|
||||||
|
for (final entry in allMediaFiles.entries) {
|
||||||
|
try {
|
||||||
|
String stateId = entry.key;
|
||||||
|
States states = States.fromJson(jsonDecode(entry.value));
|
||||||
|
// upload one by one
|
||||||
|
await uploadMediaState(stateId, states.prepareState, states.metadata);
|
||||||
|
} catch (e) {
|
||||||
|
Logger("media.dart").shout(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the upload failes this function is called again from the retransmitMediaFiles function which is
|
||||||
|
// called when the WebSocket is reconnected again.
|
||||||
|
Future uploadMediaState(
|
||||||
|
String stateId, PrepareState prepareState, Metadata metadata) async {
|
||||||
final uploadState =
|
final uploadState =
|
||||||
await ImageUploader.uploadState(prepareState, metadata.userIds.length);
|
await ImageUploader.uploadState(prepareState, metadata.userIds.length);
|
||||||
if (uploadState == null) {
|
if (uploadState == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete prepareState and store uploadState...
|
{
|
||||||
|
Box storage = await getMediaStorage();
|
||||||
|
|
||||||
|
String? mediaFilesJson = storage.get("mediaUploads");
|
||||||
|
|
||||||
|
if (mediaFilesJson != null) {
|
||||||
|
Map<String, dynamic> allMediaFiles = jsonDecode(mediaFilesJson);
|
||||||
|
allMediaFiles.remove(stateId);
|
||||||
|
storage.put("mediaUploads", jsonEncode(allMediaFiles));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final notifyState =
|
final notifyState =
|
||||||
await ImageUploader.notifyState(prepareState, uploadState, metadata);
|
await ImageUploader.notifyState(prepareState, uploadState, metadata);
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ class ApiProvider {
|
||||||
|
|
||||||
if (!globalIsAppInBackground) {
|
if (!globalIsAppInBackground) {
|
||||||
tryTransmitMessages();
|
tryTransmitMessages();
|
||||||
|
retransmitMediaFiles();
|
||||||
tryDownloadAllMediaFiles();
|
tryDownloadAllMediaFiles();
|
||||||
notifyContactsAboutProfileChange();
|
notifyContactsAboutProfileChange();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ Future<Box> getMediaStorage() async {
|
||||||
var encryptionKey =
|
var encryptionKey =
|
||||||
base64Url.decode((await storage.read(key: 'hive_encryption_key'))!);
|
base64Url.decode((await storage.read(key: 'hive_encryption_key'))!);
|
||||||
|
|
||||||
return await Hive.openBox('media_storage',
|
return await Hive.openBox(
|
||||||
encryptionCipher: HiveAesCipher(encryptionKey));
|
'media_storage',
|
||||||
|
encryptionCipher: HiveAesCipher(encryptionKey),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,16 +119,22 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
.reduce((a, b) => a > b ? a : b);
|
.reduce((a, b) => a > b ? a : b);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListView.builder(
|
return RefreshIndicator(
|
||||||
restorationId: 'chat_list_view',
|
onRefresh: () async {
|
||||||
itemCount: contacts.length,
|
await apiProvider.connect();
|
||||||
itemBuilder: (BuildContext context, int index) {
|
await Future.delayed(Duration(seconds: 1));
|
||||||
final user = contacts[index];
|
|
||||||
return UserListItem(
|
|
||||||
user: user,
|
|
||||||
maxTotalMediaCounter: maxTotalMediaCounter,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
child: ListView.builder(
|
||||||
|
restorationId: 'chat_list_view',
|
||||||
|
itemCount: contacts.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
final user = contacts[index];
|
||||||
|
return UserListItem(
|
||||||
|
user: user,
|
||||||
|
maxTotalMediaCounter: maxTotalMediaCounter,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue