image previews #267
|
@ -15,7 +15,9 @@ abstract class Cwtch {
|
|||
// ignore: non_constant_identifier_names
|
||||
void LoadProfiles(String pass);
|
||||
// ignore: non_constant_identifier_names
|
||||
void DeleteProfile(String onion, String pass);
|
||||
void DeleteProfile(String profile, String pass);
|
||||
// ignore: non_constant_identifier_names
|
||||
void ChangePassword(String profile, String pass, String newpass, String newpassAgain);
|
||||
dan marked this conversation as resolved
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
void ResetTor();
|
||||
|
|
|
@ -224,7 +224,10 @@ class CwtchNotifier {
|
|||
}
|
||||
break;
|
||||
case "SendMessageToPeerError":
|
||||
// Ignore
|
||||
// Ignore dealt with by IndexedFailure
|
||||
break;
|
||||
case "SendMessageToGroupError":
|
||||
// Ignore dealt with by IndexedFailure
|
||||
break;
|
||||
case "IndexedFailure":
|
||||
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]);
|
||||
|
|
|
@ -115,13 +115,13 @@ class CwtchFfi implements Cwtch {
|
|||
}
|
||||
|
||||
CwtchFfi(CwtchNotifier _cwtchNotifier) {
|
||||
String library_path = getLibraryPath();
|
||||
if (library_path == UNSUPPORTED_OS) {
|
||||
String libraryPath = getLibraryPath();
|
||||
if (libraryPath == UNSUPPORTED_OS) {
|
||||
print("OS ${Platform.operatingSystem} not supported by cwtch/ffi");
|
||||
// emergency, ideally the app stays on splash and just posts the error till user closes
|
||||
exit(0);
|
||||
}
|
||||
library = DynamicLibrary.open(library_path);
|
||||
library = DynamicLibrary.open(libraryPath);
|
||||
cwtchNotifier = _cwtchNotifier;
|
||||
}
|
||||
|
||||
|
@ -716,4 +716,21 @@ class CwtchFfi implements Cwtch {
|
|||
malloc.free(utf8profile);
|
||||
return jsonMessage;
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: non_constant_identifier_names
|
||||
void ChangePassword(String profile, String pass, String newpass, String newpassAgain) {
|
||||
var changePasswordC = library.lookup<NativeFunction<void_from_string_string_string_string_function>>("c_ChangePassword");
|
||||
// ignore: non_constant_identifier_names
|
||||
final ChangePasswordFn = changePasswordC.asFunction<VoidFromStringStringStringStringFn>();
|
||||
final utf8profile = profile.toNativeUtf8();
|
||||
final utf8pass = pass.toNativeUtf8();
|
||||
final utf8newpass = newpass.toNativeUtf8();
|
||||
final utf8newpasssagain = newpassAgain.toNativeUtf8();
|
||||
ChangePasswordFn(utf8profile, utf8profile.length, utf8pass, utf8pass.length, utf8newpass, utf8newpass.length, utf8newpasssagain, utf8newpasssagain.length);
|
||||
malloc.free(utf8profile);
|
||||
malloc.free(utf8pass);
|
||||
malloc.free(utf8newpass);
|
||||
malloc.free(utf8newpasssagain);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,4 +284,9 @@ class CwtchGomobile implements Cwtch {
|
|||
String defaultDownloadPath() {
|
||||
return this.androidHomeDirectoryStr;
|
||||
}
|
||||
|
||||
@override
|
||||
void ChangePassword(String profile, String pass, String newpass, String newpassAgain) {
|
||||
cwtchPlatform.invokeMethod("ChangePassword", {"ProfileOnion": profile, "OldPass": pass, "NewPass": newpass, "NewPassAgain": newpassAgain});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,17 @@ class ErrorHandler extends ChangeNotifier {
|
|||
|
||||
// Add Contact Specific Errors...
|
||||
static const String addContactErrorPrefix = "addcontact";
|
||||
static const String changePasswordErrorPrefix = "changepassword";
|
||||
static const String invalidImportStringErrorType = "invalid_import_string";
|
||||
static const String contactAlreadyExistsErrorType = "contact_already_exists";
|
||||
bool invalidImportStringError = false;
|
||||
bool contactAlreadyExistsError = false;
|
||||
bool explicitAddContactSuccess = false;
|
||||
|
||||
// ChangePassword
|
||||
bool changePasswordError = false;
|
||||
bool explicitChangePasswordSuccess = false;
|
||||
|
||||
// Import Bundle Specific Errors
|
||||
static const String importBundleErrorPrefix = "importBundle";
|
||||
bool importBundleError = false;
|
||||
|
@ -39,6 +44,9 @@ class ErrorHandler extends ChangeNotifier {
|
|||
deletedServerError = false;
|
||||
deletedServerSuccess = false;
|
||||
|
||||
changePasswordError = false;
|
||||
explicitChangePasswordSuccess = false;
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -58,6 +66,9 @@ class ErrorHandler extends ChangeNotifier {
|
|||
case deleteProfileErrorPrefix:
|
||||
handleDeleteProfileError(errorType);
|
||||
break;
|
||||
case changePasswordErrorPrefix:
|
||||
handleChangePasswordError(errorType);
|
||||
break;
|
||||
case deletedServerErrorPrefix:
|
||||
handleDeletedServerError(errorType);
|
||||
}
|
||||
|
@ -115,6 +126,21 @@ class ErrorHandler extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
handleChangePasswordError(String errorType) {
|
||||
// Reset add contact errors
|
||||
changePasswordError = false;
|
||||
explicitChangePasswordSuccess = false;
|
||||
|
||||
switch (errorType) {
|
||||
case successErrorType:
|
||||
explicitChangePasswordSuccess = true;
|
||||
break;
|
||||
default:
|
||||
changePasswordError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleDeletedServerError(String errorType) {
|
||||
// reset
|
||||
deletedServerError = false;
|
||||
|
|
|
@ -150,5 +150,6 @@ class MessageMetadata extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
MessageMetadata(this.profileOnion, this.conversationIdentifier, this.messageID, this.timestamp, this.senderHandle, this.senderImage, this.signature, this._attributes, this._ackd, this._error, this.isAuto);
|
||||
MessageMetadata(
|
||||
this.profileOnion, this.conversationIdentifier, this.messageID, this.timestamp, this.senderHandle, this.senderImage, this.signature, this._attributes, this._ackd, this._error, this.isAuto);
|
||||
}
|
||||
|
|
|
@ -54,14 +54,16 @@ class FileMessage extends Message {
|
|||
if (!validHash(rootHash, nonce)) {
|
||||
return MessageRow(MalformedBubble());
|
||||
}
|
||||
return FileBubble(
|
||||
nameSuggestion,
|
||||
rootHash,
|
||||
nonce,
|
||||
fileSize,
|
||||
isAuto: metadata.isAuto,
|
||||
interactive: false,
|
||||
);
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
child: FileBubble(
|
||||
nameSuggestion,
|
||||
rootHash,
|
||||
nonce,
|
||||
fileSize,
|
||||
isAuto: metadata.isAuto,
|
||||
interactive: false,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cwtch/config.dart';
|
||||
import 'package:cwtch/cwtch/cwtch.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -278,7 +280,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
|||
// TODO Toast
|
||||
}
|
||||
|
||||
void _createPressed() {
|
||||
void _createPressed() async {
|
||||
dan
commented
do we need a Provider.of(context, listen: false).reset(); something like here? do we need a Provider.of<ErrorHandler>(context, listen: false).reset(); something like here?
|
||||
// This will run all the validations in the form including
|
||||
// checking that display name is not empty, and an actual check that the passwords
|
||||
// match (and are provided if the user has requested an encrypted profile).
|
||||
|
@ -301,17 +303,32 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
|||
} else {
|
||||
// At this points passwords have been validated to be the same and not empty
|
||||
// Update both password and name, even if name hasn't been changed...
|
||||
var profile = Provider.of<ProfileInfoState>(context, listen: false).onion;
|
||||
Provider.of<ProfileInfoState>(context, listen: false).nickname = ctrlrNick.value.text;
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.SetProfileAttribute(Provider.of<ProfileInfoState>(context, listen: false).onion, "profile.name", ctrlrNick.value.text);
|
||||
final updatePasswordEvent = {
|
||||
"EventType": "ChangePassword",
|
||||
"Data": {"Password": ctrlrOldPass.text, "NewPassword": ctrlrPass.text}
|
||||
};
|
||||
final updatePasswordEventJson = jsonEncode(updatePasswordEvent);
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.SetProfileAttribute(profile, "profile.name", ctrlrNick.value.text);
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.ChangePassword(profile, ctrlrOldPass.text, ctrlrPass.text, ctrlrPass2.text);
|
||||
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(Provider.of<ProfileInfoState>(context, listen: false).onion, updatePasswordEventJson);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
EnvironmentConfig.debugLog("waiting for change password response");
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
if (globalErrorHandler.changePasswordError) {
|
||||
// TODO: This isn't ideal, but because onChange can be fired during this future check
|
||||
// and because the context can change after being popped we have this kind of double assertion...
|
||||
// There is probably a better pattern to handle this...
|
||||
if (AppLocalizations.of(context) != null) {
|
||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.passwordChangeError));
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
Navigator.pop(context);
|
||||
dan
commented
why do we pop the nav on error? shouldn't we stay on this pane? why do we pop the nav on error? shouldn't we stay on this pane?
|
||||
return;
|
||||
}
|
||||
}
|
||||
}).whenComplete(() {
|
||||
if (globalErrorHandler.explicitChangePasswordSuccess) {
|
||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.newPassword));
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
Navigator.pop(context);
|
||||
return; // otherwise round and round we go...
|
||||
dan
commented
dont understand the comment? dont understand the comment?
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,8 +86,8 @@ class FileBubbleState extends State<FileBubble> {
|
|||
}
|
||||
}
|
||||
return LayoutBuilder(builder: (bcontext, constraints) {
|
||||
var wdgSender = Center(
|
||||
widthFactor: 1,
|
||||
var wdgSender = Visibility(
|
||||
visible: widget.interactive,
|
||||
child: SelectableText(senderDisplayStr + '\u202F',
|
||||
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor())));
|
||||
|
||||
|
@ -102,40 +102,47 @@ class FileBubbleState extends State<FileBubble> {
|
|||
if (!showFileSharing) {
|
||||
wdgDecorations = Text('\u202F');
|
||||
} else if (fromMe) {
|
||||
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, prettyDate: prettyDate);
|
||||
wdgDecorations = Visibility(
|
||||
visible: widget.interactive,
|
||||
child: MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, prettyDate: prettyDate));
|
||||
} else if (downloadComplete) {
|
||||
// in this case, whatever marked download.complete would have also set the path
|
||||
var lpath = path!.toLowerCase();
|
||||
if (showImagePreviews && (lpath.endsWith("jpg") || lpath.endsWith("jpeg") || lpath.endsWith("png") || lpath.endsWith("gif") || lpath.endsWith("webp") || lpath.endsWith("bmp"))) {
|
||||
isPreview = true;
|
||||
wdgDecorations = GestureDetector(
|
||||
child: Image.file(
|
||||
myFile!,
|
||||
cacheWidth: 2048, // limit the amount of space the image can decode too, we keep this high-ish to allow quality previews...
|
||||
filterQuality: FilterQuality.medium,
|
||||
fit: BoxFit.fill,
|
||||
alignment: Alignment.center,
|
||||
width: constraints.maxWidth,
|
||||
isAntiAlias: false,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return MalformedBubble();
|
||||
},
|
||||
),
|
||||
wdgDecorations = Center(
|
||||
child: GestureDetector(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(1.0),
|
||||
child: Image.file(
|
||||
myFile!,
|
||||
cacheWidth: 2048, // limit the amount of space the image can decode too, we keep this high-ish to allow quality previews...
|
||||
filterQuality: FilterQuality.medium,
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: Alignment.center,
|
||||
height: MediaQuery.of(bcontext).size.height * 0.30,
|
||||
isAntiAlias: false,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return MalformedBubble();
|
||||
},
|
||||
)),
|
||||
onTap: () {
|
||||
pop(bcontext, myFile!, wdgMessage);
|
||||
},
|
||||
);
|
||||
));
|
||||
} else {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F');
|
||||
wdgDecorations = Visibility(visible: widget.interactive, child: Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F'));
|
||||
}
|
||||
} else if (downloadActive) {
|
||||
if (!downloadGotManifest) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F');
|
||||
wdgDecorations = Visibility(visible: widget.interactive, child: Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F'));
|
||||
} else {
|
||||
wdgDecorations = LinearProgressIndicator(
|
||||
value: Provider.of<ProfileInfoState>(context).downloadProgress(widget.fileKey()),
|
||||
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||
);
|
||||
wdgDecorations = Visibility(
|
||||
visible: widget.interactive,
|
||||
child: LinearProgressIndicator(
|
||||
value: Provider.of<ProfileInfoState>(context).downloadProgress(widget.fileKey()),
|
||||
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||
));
|
||||
}
|
||||
} else if (flagStarted) {
|
||||
// in this case, the download was done in a previous application launch,
|
||||
|
@ -145,25 +152,24 @@ class FileBubbleState extends State<FileBubble> {
|
|||
AppLocalizations.of(context)!.fileCheckingStatus + '...' +
|
||||
'\u202F');
|
||||
} else {
|
||||
var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(
|
||||
widget.fileKey()) ?? "";
|
||||
wdgDecorations =
|
||||
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(AppLocalizations.of(context)!.fileInterrupted + ': ' +
|
||||
path + '\u202F'),
|
||||
ElevatedButton(onPressed: _btnResume,
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.verfiyResumeButton))
|
||||
]);
|
||||
var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey()) ?? "";
|
||||
wdgDecorations = Visibility(
|
||||
visible: widget.interactive,
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F'),
|
||||
ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton))
|
||||
]));
|
||||
}
|
||||
} else if (!senderIsContact) {
|
||||
wdgDecorations = Text("Add this account to your contacts in order to accept this file.");
|
||||
} else if (!widget.isAuto) {
|
||||
wdgDecorations = Center(
|
||||
widthFactor: 1,
|
||||
child: Wrap(children: [
|
||||
Padding(padding: EdgeInsets.all(5), child: ElevatedButton(child: Text(AppLocalizations.of(context)!.downloadFileButton + '\u202F'), onPressed: _btnAccept)),
|
||||
]));
|
||||
wdgDecorations = Visibility(
|
||||
visible: widget.interactive,
|
||||
child: Center(
|
||||
widthFactor: 1,
|
||||
child: Wrap(children: [
|
||||
Padding(padding: EdgeInsets.all(5), child: ElevatedButton(child: Text(AppLocalizations.of(context)!.downloadFileButton + '\u202F'), onPressed: _btnAccept)),
|
||||
])));
|
||||
} else {
|
||||
wdgDecorations = Container();
|
||||
}
|
||||
|
@ -186,9 +192,7 @@ class FileBubbleState extends State<FileBubble> {
|
|||
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
||||
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: fromMe
|
||||
? [wdgMessage, Visibility(visible: widget.interactive, child: wdgDecorations)]
|
||||
: [wdgSender, isPreview ? Container() : wdgMessage, Visibility(visible: widget.interactive, child: wdgDecorations)]),
|
||||
children: fromMe ? [wdgMessage, Visibility(visible: widget.interactive, child: wdgDecorations)] : [wdgSender, isPreview ? Container() : wdgMessage, wdgDecorations]),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
@ -357,8 +361,9 @@ class FileBubbleState extends State<FileBubble> {
|
|||
meta,
|
||||
Image.file(
|
||||
myFile,
|
||||
cacheHeight: (MediaQuery.of(context).size.height * 0.6).floor(),
|
||||
cacheWidth: (MediaQuery.of(context).size.width * 0.6).floor(),
|
||||
width: (MediaQuery.of(context).size.width * 0.6),
|
||||
height: (MediaQuery.of(context).size.height * 0.6),
|
||||
fit: BoxFit.scaleDown,
|
||||
),
|
||||
Visibility(visible: !Platform.isAndroid, child: Text(myFile.path, textAlign: TextAlign.center)),
|
||||
|
|
Loading…
Reference in New Issue
lol changePassword got rolled into this?