moar fileshare plz

This commit is contained in:
erinn 2021-09-29 13:31:01 -07:00
parent cb3c161277
commit 8fe577afd4
10 changed files with 107 additions and 53 deletions

View File

@ -194,6 +194,11 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
Log.i("FlwtchWorker::DownloadFile", "DownloadFile("+filepath+", "+manifestpath+")") Log.i("FlwtchWorker::DownloadFile", "DownloadFile("+filepath+", "+manifestpath+")")
Cwtch.downloadFile(profile, handle, filepath, manifestpath, filekey) Cwtch.downloadFile(profile, handle, filepath, manifestpath, filekey)
} }
"CheckDownloadStatus" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val fileKey = (a.get("fileKey") as? String) ?: ""
Cwtch.checkDownloadStatus(profile, fileKey)
}
"SendProfileEvent" -> { "SendProfileEvent" -> {
val onion = (a.get("onion") as? String) ?: "" val onion = (a.get("onion") as? String) ?: ""
val jsonEvent = (a.get("jsonEvent") as? String) ?: "" val jsonEvent = (a.get("jsonEvent") as? String) ?: ""

View File

@ -46,6 +46,8 @@ abstract class Cwtch {
void DownloadFile(String profile, String handle, String filepath, String manifestpath, String filekey); void DownloadFile(String profile, String handle, String filepath, String manifestpath, String filekey);
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void CreateDownloadableFile(String profile, String handle, String filenameSuggestion, String filekey); void CreateDownloadableFile(String profile, String handle, String filenameSuggestion, String filekey);
// ignore: non_constant_identifier_names
void CheckDownloadStatus(String profile, String fileKey);
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void ArchiveConversation(String profile, String handle); void ArchiveConversation(String profile, String handle);

View File

@ -330,7 +330,7 @@ class CwtchNotifier {
profileCN.getProfile(data["ProfileOnion"])?.downloadUpdate(data["FileKey"], int.parse(data["Progress"])); profileCN.getProfile(data["ProfileOnion"])?.downloadUpdate(data["FileKey"], int.parse(data["Progress"]));
break; break;
case "FileDownloaded": case "FileDownloaded":
profileCN.getProfile(data["ProfileOnion"])?.downloadMarkFinished(data["FileKey"]); profileCN.getProfile(data["ProfileOnion"])?.downloadMarkFinished(data["FileKey"], data["FilePath"]);
break; break;
default: default:
EnvironmentConfig.debugLog("unhandled event: $type"); EnvironmentConfig.debugLog("unhandled event: $type");

View File

@ -397,6 +397,19 @@ class CwtchFfi implements Cwtch {
// android only - do nothing // android only - do nothing
} }
@override
// ignore: non_constant_identifier_names
void CheckDownloadStatus(String profileOnion, String fileKey) {
var checkDownloadStatus = library.lookup<NativeFunction<string_string_to_void_function>>("c_CheckDownloadStatus");
// ignore: non_constant_identifier_names
final CheckDownloadStatus = checkDownloadStatus.asFunction<VoidFromStringStringFn>();
final u1 = profileOnion.toNativeUtf8();
final u2 = fileKey.toNativeUtf8();
CheckDownloadStatus(u1, u1.length, u2, u2.length);
malloc.free(u1);
malloc.free(u2);
}
@override @override
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void ResetTor() { void ResetTor() {

View File

@ -148,6 +148,12 @@ class CwtchGomobile implements Cwtch {
cwtchPlatform.invokeMethod("CreateDownloadableFile", {"ProfileOnion": profileOnion, "handle": contactHandle, "filename": filenameSuggestion, "filekey": filekey}); cwtchPlatform.invokeMethod("CreateDownloadableFile", {"ProfileOnion": profileOnion, "handle": contactHandle, "filename": filenameSuggestion, "filekey": filekey});
} }
@override
// ignore: non_constant_identifier_names
void CheckDownloadStatus(String profileOnion, String fileKey) {
cwtchPlatform.invokeMethod("CheckDownloadStatus", {"ProfileOnion": profileOnion, "fileKey": fileKey});
}
@override @override
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void ResetTor() { void ResetTor() {

View File

@ -381,14 +381,17 @@ class ProfileInfoState extends ChangeNotifier {
} }
} }
void downloadMarkFinished(String fileKey) { void downloadMarkFinished(String fileKey, String finalPath) {
if (!downloadActive(fileKey)) { if (!downloadActive(fileKey)) {
print("error: received download completion notice for unknown download "+fileKey); // happens as a result of a CheckDownloadStatus call,
} else { // invoked from a historical (timeline) download message
this._downloads[fileKey]!.timeEnd = DateTime.now(); // so setting numChunks correctly shouldn't matter
this._downloads[fileKey]!.complete = true; this.downloadInit(fileKey, 1);
notifyListeners();
} }
this._downloads[fileKey]!.timeEnd = DateTime.now();
this._downloads[fileKey]!.downloadedTo = finalPath;
this._downloads[fileKey]!.complete = true;
notifyListeners();
} }
bool downloadActive(String fileKey) { bool downloadActive(String fileKey) {
@ -407,8 +410,12 @@ class ProfileInfoState extends ChangeNotifier {
return this._downloads.containsKey(fileKey) ? this._downloads[fileKey]!.progress() : 0.0; return this._downloads.containsKey(fileKey) ? this._downloads[fileKey]!.progress() : 0.0;
} }
String? downloadFinalPath(String fileKey) {
return this._downloads.containsKey(fileKey) ? this._downloads[fileKey]!.downloadedTo : null;
}
String downloadSpeed(String fileKey) { String downloadSpeed(String fileKey) {
if (!downloadActive(fileKey)) { if (!downloadActive(fileKey) || this._downloads[fileKey]!.chunksDownloaded == 0) {
return "0 B/s"; return "0 B/s";
} }
var bytes = this._downloads[fileKey]!.chunksDownloaded * 4096; var bytes = this._downloads[fileKey]!.chunksDownloaded * 4096;
@ -425,6 +432,7 @@ class FileDownloadProgress {
int chunksTotal = 1; int chunksTotal = 1;
bool complete = false; bool complete = false;
bool gotManifest = false; bool gotManifest = false;
String? downloadedTo;
DateTime? timeStart; DateTime? timeStart;
DateTime? timeEnd; DateTime? timeEnd;

View File

@ -35,38 +35,39 @@ Future<Message> messageHandler(BuildContext context, String profileOnion, String
try { try {
var rawMessageEnvelopeFuture = Provider.of<FlwtchState>(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, index); var rawMessageEnvelopeFuture = Provider.of<FlwtchState>(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, index);
return rawMessageEnvelopeFuture.then((dynamic rawMessageEnvelope) { return rawMessageEnvelopeFuture.then((dynamic rawMessageEnvelope) {
dynamic messageWrapper = jsonDecode(rawMessageEnvelope); var metadata = MessageMetadata(profileOnion, contactHandle, index, DateTime.now(), "", "", null, 0, false, true);
// There are 2 conditions in which this error condition can be met:
// 1. The application == nil, in which case this instance of the UI is already
// broken beyond repair, and will either be replaced by a new version, or requires a complete
// restart.
// 2. This index was incremented and we happened to fetch the timeline prior to the messages inclusion.
// This should be rare as Timeline addition/fetching is mutex protected and Dart itself will pipeline the
// calls to libCwtch-go - however because we use goroutines on the backend there is always a chance that one
// will find itself delayed.
// The second case is recoverable by tail-recursing this future.
if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') {
return Future.delayed(Duration(seconds: 2), () {
print("Tail recursive call to messageHandler called. This should be a rare event. If you see multiples of this log over a short period of time please log it as a bug.");
return messageHandler(context, profileOnion, contactHandle, index).then((value) => value);
});
}
// Construct the initial metadata
var timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
var senderHandle = messageWrapper['PeerID'];
var senderImage = messageWrapper['ContactImage'];
var flags = int.parse(messageWrapper['Flags'].toString(), radix: 2);
var ackd = messageWrapper['Acknowledged'];
var error = messageWrapper['Error'] != null;
String? signature;
// If this is a group, store the signature
if (contactHandle.length == GroupConversationHandleLength) {
signature = messageWrapper['Signature'];
}
var metadata = MessageMetadata(profileOnion, contactHandle, index, timestamp, senderHandle, senderImage, signature, flags, ackd, error);
try { try {
dynamic messageWrapper = jsonDecode(rawMessageEnvelope);
// There are 2 conditions in which this error condition can be met:
// 1. The application == nil, in which case this instance of the UI is already
// broken beyond repair, and will either be replaced by a new version, or requires a complete
// restart.
// 2. This index was incremented and we happened to fetch the timeline prior to the messages inclusion.
// This should be rare as Timeline addition/fetching is mutex protected and Dart itself will pipeline the
// calls to libCwtch-go - however because we use goroutines on the backend there is always a chance that one
// will find itself delayed.
// The second case is recoverable by tail-recursing this future.
if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') {
return Future.delayed(Duration(seconds: 2), () {
print("Tail recursive call to messageHandler called. This should be a rare event. If you see multiples of this log over a short period of time please log it as a bug.");
return messageHandler(context, profileOnion, contactHandle, index).then((value) => value);
});
}
// Construct the initial metadata
var timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
var senderHandle = messageWrapper['PeerID'];
var senderImage = messageWrapper['ContactImage'];
var flags = int.parse(messageWrapper['Flags'].toString());
var ackd = messageWrapper['Acknowledged'];
var error = messageWrapper['Error'] != null;
String? signature;
// If this is a group, store the signature
if (contactHandle.length == GroupConversationHandleLength) {
signature = messageWrapper['Signature'];
}
metadata = MessageMetadata(profileOnion, contactHandle, index, timestamp, senderHandle, senderImage, signature, flags, ackd, error);
dynamic message = jsonDecode(messageWrapper['Message']); dynamic message = jsonDecode(messageWrapper['Message']);
var content = message['d'] as dynamic; var content = message['d'] as dynamic;
var overlay = int.parse(message['o'].toString()); var overlay = int.parse(message['o'].toString());
@ -86,6 +87,7 @@ Future<Message> messageHandler(BuildContext context, String profileOnion, String
return MalformedMessage(metadata); return MalformedMessage(metadata);
} }
} catch (e) { } catch (e) {
print("an error! " + e.toString());
return MalformedMessage(metadata); return MalformedMessage(metadata);
} }
}); });

View File

@ -39,7 +39,7 @@ class FileBubbleState extends State<FileBubble> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion; var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
//isAccepted = Provider.of<ProfileInfoState>(context).contactList.getContact(widget.inviteTarget) != null; var flagStarted = Provider.of<MessageMetadata>(context).flags & 0x02 > 0;
var borderRadiousEh = 15.0; var borderRadiousEh = 15.0;
var showFileSharing = Provider.of<Settings>(context).isExperimentEnabled(FileSharingExperiment); var showFileSharing = Provider.of<Settings>(context).isExperimentEnabled(FileSharingExperiment);
var prettyDate = DateFormat.yMd(Platform.localeName).add_jm().format(Provider.of<MessageMetadata>(context).timestamp); var prettyDate = DateFormat.yMd(Platform.localeName).add_jm().format(Provider.of<MessageMetadata>(context).timestamp);
@ -66,27 +66,40 @@ class FileBubbleState extends State<FileBubble> {
? senderFileChrome( ? senderFileChrome(
AppLocalizations.of(context)!.messageFileSent, widget.nameSuggestion, widget.rootHash, widget.fileSize) AppLocalizations.of(context)!.messageFileSent, widget.nameSuggestion, widget.rootHash, widget.fileSize)
: (fileChrome(AppLocalizations.of(context)!.messageFileOffered + ":", widget.nameSuggestion, widget.rootHash, widget.fileSize, Provider.of<ProfileInfoState>(context).downloadSpeed(widget.fileKey()))); : (fileChrome(AppLocalizations.of(context)!.messageFileOffered + ":", widget.nameSuggestion, widget.rootHash, widget.fileSize, Provider.of<ProfileInfoState>(context).downloadSpeed(widget.fileKey())));
Widget wdgDecorations; Widget wdgDecorations;
if (!showFileSharing) { if (!showFileSharing) {
wdgDecorations = Text('\u202F'); wdgDecorations = Text('\u202F');
} else if (fromMe) { } else if (fromMe) {
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, prettyDate: prettyDate); wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, prettyDate: prettyDate);
} else if (Provider.of<ProfileInfoState>(context).downloadComplete(widget.fileKey())) { } else if (Provider.of<ProfileInfoState>(context).downloadComplete(widget.fileKey())) {
wdgDecorations = Center( // in this case, whatever marked download.complete would have also set the path
widthFactor: 1, var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey())!;
child: Wrap(children: [ wdgDecorations = Text('Saved to: ' + path + '\u202F');
Padding(padding: EdgeInsets.all(5), child: ElevatedButton(child: Text(AppLocalizations.of(context)!.openFolderButton + '\u202F'), onPressed: _btnAccept)),
]));
} else if (Provider.of<ProfileInfoState>(context).downloadActive(widget.fileKey())) { } else if (Provider.of<ProfileInfoState>(context).downloadActive(widget.fileKey())) {
if (!Provider.of<ProfileInfoState>(context).downloadGotManifest(widget.fileKey())) { if (!Provider.of<ProfileInfoState>(context).downloadGotManifest(
wdgDecorations = Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F'); widget.fileKey())) {
wdgDecorations = Text(
AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F');
} else { } else {
wdgDecorations = LinearProgressIndicator( wdgDecorations = LinearProgressIndicator(
value: Provider.of<ProfileInfoState>(context).downloadProgress(widget.fileKey()), value: Provider.of<ProfileInfoState>(context).downloadProgress(
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(), widget.fileKey()),
color: Provider
.of<Settings>(context)
.theme
.defaultButtonActiveColor(),
); );
} }
} else if (flagStarted) {
// in this case, the download was done in a previous application launch,
// so we probably have to request an info lookup
var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey());
if (path == null) {
wdgDecorations = Text('Checking download status...' + '\u202F');
Provider.of<FlwtchState>(context, listen: false).cwtch.CheckDownloadStatus(Provider.of<ProfileInfoState>(context, listen: false).onion, widget.fileKey());
} else {
wdgDecorations = Text('Saved to: ' + (path??"null") + '\u202F');
}
} else { } else {
wdgDecorations = Center( wdgDecorations = Center(
widthFactor: 1, widthFactor: 1,
@ -135,10 +148,13 @@ class FileBubbleState extends State<FileBubble> {
File? file; File? file;
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion; var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
var handle = Provider.of<MessageMetadata>(context, listen: false).senderHandle; var handle = Provider.of<MessageMetadata>(context, listen: false).senderHandle;
var contact = Provider.of<ContactInfoState>(context, listen: false).onion;
var idx = Provider.of<MessageMetadata>(context, listen: false).messageIndex;
if (Platform.isAndroid) { if (Platform.isAndroid) {
//todo: would be better to only call downloadInit if CreateDownloadableFile results in a user-pick (they might cancel)
Provider.of<ProfileInfoState>(context, listen: false).downloadInit(widget.fileKey(), (widget.fileSize / 4096).ceil()); Provider.of<ProfileInfoState>(context, listen: false).downloadInit(widget.fileKey(), (widget.fileSize / 4096).ceil());
Provider.of<FlwtchState>(context, listen: false).cwtch.UpdateMessageFlags(profileOnion, contact, idx, Provider.of<MessageMetadata>(context, listen: false).flags | 0x02);
Provider.of<MessageMetadata>(context, listen: false).flags |= 0x02;
Provider.of<FlwtchState>(context, listen: false).cwtch.CreateDownloadableFile(profileOnion, handle, widget.nameSuggestion, widget.fileKey()); Provider.of<FlwtchState>(context, listen: false).cwtch.CreateDownloadableFile(profileOnion, handle, widget.nameSuggestion, widget.fileKey());
} else { } else {
try { try {
@ -148,6 +164,8 @@ class FileBubbleState extends State<FileBubble> {
print("saving to " + file.path); print("saving to " + file.path);
var manifestPath = file.path + ".manifest"; var manifestPath = file.path + ".manifest";
Provider.of<ProfileInfoState>(context, listen: false).downloadInit(widget.fileKey(), (widget.fileSize / 4096).ceil()); Provider.of<ProfileInfoState>(context, listen: false).downloadInit(widget.fileKey(), (widget.fileSize / 4096).ceil());
Provider.of<FlwtchState>(context, listen: false).cwtch.UpdateMessageFlags(profileOnion, contact, idx, Provider.of<MessageMetadata>(context, listen: false).flags | 0x02);
Provider.of<MessageMetadata>(context, listen: false).flags |= 0x02;
Provider.of<FlwtchState>(context, listen: false).cwtch.DownloadFile(profileOnion, handle, file.path, manifestPath, widget.fileKey()); Provider.of<FlwtchState>(context, listen: false).cwtch.DownloadFile(profileOnion, handle, file.path, manifestPath, widget.fileKey());
} }
} catch (e) { } catch (e) {

View File

@ -131,7 +131,7 @@ class InvitationBubbleState extends State<InvitationBubble> {
var contact = Provider.of<ContactInfoState>(context, listen: false).onion; var contact = Provider.of<ContactInfoState>(context, listen: false).onion;
var idx = Provider.of<MessageMetadata>(context, listen: false).messageIndex; var idx = Provider.of<MessageMetadata>(context, listen: false).messageIndex;
Provider.of<FlwtchState>(context, listen: false).cwtch.UpdateMessageFlags(profileOnion, contact, idx, Provider.of<MessageMetadata>(context, listen: false).flags | 0x01); Provider.of<FlwtchState>(context, listen: false).cwtch.UpdateMessageFlags(profileOnion, contact, idx, Provider.of<MessageMetadata>(context, listen: false).flags | 0x01);
Provider.of<MessageMetadata>(context).flags |= 0x01; Provider.of<MessageMetadata>(context, listen: false).flags |= 0x01;
}); });
} }

View File

@ -87,7 +87,7 @@ class _MessageListState extends State<MessageList> {
// Already includes MessageRow,, // Already includes MessageRow,,
return message.getWidget(context); return message.getWidget(context);
} else { } else {
return MessageLoadingBubble(); return Text('');//MessageLoadingBubble();
} }
}, },
); );