forked from cwtch.im/cwtch-ui
Merge pull request 'Format, Context Binding and Check if File Exists in File Bubble' (#447) from file-fixes into trunk
Reviewed-on: cwtch.im/cwtch-ui#447 Reviewed-by: Dan Ballard <dan@openprivacy.ca>
This commit is contained in:
commit
9d8f73ac00
|
@ -215,8 +215,9 @@ class CwtchFfi implements Cwtch {
|
|||
// ignore: non_constant_identifier_names
|
||||
final StartCwtch = startCwtchC.asFunction<StartCwtchFn>();
|
||||
|
||||
final ut8CwtchDir = cwtchDir.toNativeUtf8();
|
||||
StartCwtch(ut8CwtchDir, ut8CwtchDir.length, bundledTor.toNativeUtf8(), bundledTor.length);
|
||||
final utf8CwtchDir = cwtchDir.toNativeUtf8();
|
||||
StartCwtch(utf8CwtchDir, utf8CwtchDir.length, bundledTor.toNativeUtf8(), bundledTor.length);
|
||||
malloc.free(utf8CwtchDir);
|
||||
|
||||
// Spawn an isolate to listen to events from libcwtch-go and then dispatch them when received on main thread to cwtchNotifier
|
||||
cwtchIsolate = await Isolate.spawn(_checkAppbusEvents, _receivePort.sendPort);
|
||||
|
@ -326,6 +327,7 @@ class CwtchFfi implements Cwtch {
|
|||
String jsonMessage = jsonMessageBytes.toDartString();
|
||||
_UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes);
|
||||
malloc.free(utf8profile);
|
||||
|
||||
return jsonMessage;
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ class MessageCache extends ChangeNotifier {
|
|||
void addFrontIndexGap(int count, int lastSeenId) {
|
||||
// scan across indexed message the unread count amount (that's the last time UI/BE acked a message)
|
||||
// if we find the last seen ID, the diff of unread count is what's unsynced
|
||||
for(var i = 0; i < (count+1) && i < cacheByIndex.length; i++) {
|
||||
for (var i = 0; i < (count + 1) && i < cacheByIndex.length; i++) {
|
||||
if (this.cacheByIndex[i].messageId == lastSeenId) {
|
||||
// we have found the matching lastSeenId so we can calculate the unsynced as the unread messages before it
|
||||
this._indexUnsynced = count - i;
|
||||
|
@ -114,7 +114,7 @@ class MessageCache extends ChangeNotifier {
|
|||
// we did not find a matching index, diff to the back end is too great, reset index cache
|
||||
resetIndexCache();
|
||||
}
|
||||
|
||||
|
||||
int get indexUnsynced => _indexUnsynced;
|
||||
|
||||
void resetIndexCache() {
|
||||
|
|
|
@ -350,4 +350,9 @@ class ProfileInfoState extends ChangeNotifier {
|
|||
int cacheMemUsage() {
|
||||
return _contacts.cacheMemUsage();
|
||||
}
|
||||
|
||||
void downloadReset(String fileKey) {
|
||||
this._downloads.remove(fileKey);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,10 +73,10 @@ class LinuxNotificationsManager implements NotificationsManager {
|
|||
|
||||
// Cwtch can install in non flutter supported ways on linux, this code detects where the assets are on Linux
|
||||
Future<String> detectLinuxAssetsPath() async {
|
||||
var devStat = FileStat.stat("assets");
|
||||
var localStat = FileStat.stat("data/flutter_assets");
|
||||
var homeStat = FileStat.stat((Platform.environment["HOME"] ?? "") + "/.local/share/cwtch/data/flutter_assets");
|
||||
var rootStat = FileStat.stat("/usr/share/cwtch/data/flutter_assets");
|
||||
var devStat = FileStat.stat("assets");
|
||||
var localStat = FileStat.stat("data/flutter_assets");
|
||||
var homeStat = FileStat.stat((Platform.environment["HOME"] ?? "") + "/.local/share/cwtch/data/flutter_assets");
|
||||
var rootStat = FileStat.stat("/usr/share/cwtch/data/flutter_assets");
|
||||
|
||||
if ((await devStat).type == FileSystemEntityType.directory) {
|
||||
return Directory.current.path; //appPath;
|
||||
|
@ -94,7 +94,7 @@ class LinuxNotificationsManager implements NotificationsManager {
|
|||
var iconPath = Uri.file(path.join(assetsPath, "assets/knott.png"));
|
||||
client.notify(message, appName: "cwtch", appIcon: iconPath.toString(), replacesId: this.previous_id).then((linux_notifications.Notification value) async {
|
||||
previous_id = value.id;
|
||||
if ((await value.closeReason) == linux_notifications.NotificationClosedReason.dismissed) {
|
||||
if ((await value.closeReason) == linux_notifications.NotificationClosedReason.dismissed) {
|
||||
this.notificationSelectConvo(profile, conversationId);
|
||||
}
|
||||
});
|
||||
|
@ -115,9 +115,9 @@ class NotificationPayload {
|
|||
convoId = json['convoId'];
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'profileOnion': profileOnion,
|
||||
'convoId': convoId,
|
||||
};
|
||||
'profileOnion': profileOnion,
|
||||
'convoId': convoId,
|
||||
};
|
||||
}
|
||||
|
||||
// FlutterLocalNotificationsPlugin based NotificationManager that handles MacOS
|
||||
|
@ -138,10 +138,10 @@ class NixNotificationManager implements NotificationsManager {
|
|||
final InitializationSettings initializationSettings = InitializationSettings(android: null, iOS: null, macOS: initializationSettingsMacOS, linux: initializationSettingsLinux);
|
||||
scheduleMicrotask(() async {
|
||||
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<MacOSFlutterLocalNotificationsPlugin>()?.requestPermissions(
|
||||
alert: true,
|
||||
badge: false,
|
||||
sound: false,
|
||||
);
|
||||
alert: true,
|
||||
badge: false,
|
||||
sound: false,
|
||||
);
|
||||
|
||||
await flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: selectNotification);
|
||||
});
|
||||
|
|
|
@ -130,7 +130,10 @@ class _ContactsViewState extends State<ContactsView> {
|
|||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _modalAddImportChoice,
|
||||
tooltip: AppLocalizations.of(context)!.tooltipAddContact,
|
||||
child: Icon(CwtchIcons.person_add_alt_1_24px, color: Provider.of<Settings>(context).theme.defaultButtonTextColor,),
|
||||
child: Icon(
|
||||
CwtchIcons.person_add_alt_1_24px,
|
||||
color: Provider.of<Settings>(context).theme.defaultButtonTextColor,
|
||||
),
|
||||
),
|
||||
body: showSearchBar || Provider.of<ContactListState>(context).isFiltered ? _buildFilterable() : _buildContactList());
|
||||
}
|
||||
|
|
|
@ -464,7 +464,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
Visibility(
|
||||
visible: EnvironmentConfig.BUILD_VER == dev_version && !Platform.isAndroid,
|
||||
child: FutureBuilder(
|
||||
future: EnvironmentConfig.BUILD_VER != dev_version ||Platform.isAndroid ? null : Provider.of<FlwtchState>(context).cwtch.GetDebugInfo(),
|
||||
future: EnvironmentConfig.BUILD_VER != dev_version || Platform.isAndroid ? null : Provider.of<FlwtchState>(context).cwtch.GetDebugInfo(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return Column(
|
||||
|
|
|
@ -183,7 +183,9 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
|
|||
onPressed: () {
|
||||
showAlertDialog(context);
|
||||
},
|
||||
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.backgroundPaneColor), foregroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.mainTextColor)),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.backgroundPaneColor),
|
||||
foregroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.mainTextColor)),
|
||||
icon: Icon(CwtchIcons.leave_group),
|
||||
label: Text(
|
||||
AppLocalizations.of(context)!.leaveConversation,
|
||||
|
|
|
@ -269,7 +269,9 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
|
|||
onPressed: () {
|
||||
showAlertDialog(context);
|
||||
},
|
||||
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.backgroundPaneColor), foregroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.mainTextColor)),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.backgroundPaneColor),
|
||||
foregroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.mainTextColor)),
|
||||
icon: Icon(CwtchIcons.leave_group),
|
||||
label: Text(
|
||||
AppLocalizations.of(context)!.leaveConversation,
|
||||
|
|
|
@ -48,7 +48,7 @@ class FileBubbleState extends State<FileBubble> {
|
|||
Widget build(BuildContext context) {
|
||||
var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
|
||||
var flagStarted = Provider.of<MessageMetadata>(context).attributes["file-downloaded"] == "true";
|
||||
var borderRadiousEh = 15.0;
|
||||
var borderRadius = 15.0;
|
||||
var showFileSharing = Provider.of<Settings>(context).isExperimentEnabled(FileSharingExperiment);
|
||||
DateTime messageDate = Provider.of<MessageMetadata>(context).timestamp;
|
||||
|
||||
|
@ -56,7 +56,7 @@ class FileBubbleState extends State<FileBubble> {
|
|||
var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey());
|
||||
|
||||
// If we haven't stored the filepath in message attributes then save it
|
||||
if (metadata.attributes["filepath"] != null) {
|
||||
if (metadata.attributes["filepath"] != null && metadata.attributes["filepath"].toString().isNotEmpty) {
|
||||
path = metadata.attributes["filepath"];
|
||||
} else if (path != null && metadata.attributes["filepath"] == null) {
|
||||
Provider.of<FlwtchState>(context).cwtch.SetMessageAttribute(metadata.profileOnion, metadata.conversationIdentifier, 0, metadata.messageID, "filepath", path);
|
||||
|
@ -72,6 +72,21 @@ class FileBubbleState extends State<FileBubble> {
|
|||
if (myFile == null) {
|
||||
setState(() {
|
||||
myFile = new File(path!);
|
||||
|
||||
// reset
|
||||
if (myFile?.existsSync() == false) {
|
||||
myFile = null;
|
||||
Provider.of<ProfileInfoState>(context).downloadReset(widget.fileKey());
|
||||
Provider.of<MessageMetadata>(context).attributes["filepath"] = null;
|
||||
Provider.of<MessageMetadata>(context).attributes["file-downloaded"] = "false";
|
||||
Provider.of<MessageMetadata>(context).attributes["file-missing"] = "true";
|
||||
Provider.of<FlwtchState>(context).cwtch.SetMessageAttribute(metadata.profileOnion, metadata.conversationIdentifier, 0, metadata.messageID, "file-downloaded", "false");
|
||||
Provider.of<FlwtchState>(context).cwtch.SetMessageAttribute(metadata.profileOnion, metadata.conversationIdentifier, 0, metadata.messageID, "filepath", "");
|
||||
Provider.of<FlwtchState>(context).cwtch.SetMessageAttribute(metadata.profileOnion, metadata.conversationIdentifier, 0, metadata.messageID, "file-missing", "true");
|
||||
} else {
|
||||
Provider.of<MessageMetadata>(context).attributes["file-missing"] = "false";
|
||||
Provider.of<FlwtchState>(context).cwtch.SetMessageAttribute(metadata.profileOnion, metadata.conversationIdentifier, 0, metadata.messageID, "file-missing", "false");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +137,7 @@ class FileBubbleState extends State<FileBubble> {
|
|||
padding: EdgeInsets.all(1.0),
|
||||
child: Image.file(
|
||||
myFile!,
|
||||
cacheWidth: 2048,
|
||||
cacheWidth: (MediaQuery.of(bcontext).size.width * 0.6).floor(),
|
||||
// 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,
|
||||
|
@ -167,7 +182,9 @@ class FileBubbleState extends State<FileBubble> {
|
|||
}
|
||||
} else if (!senderIsContact) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.msgAddToAccept);
|
||||
} else if (!widget.isAuto) {
|
||||
} else if (!widget.isAuto || Provider.of<MessageMetadata>(context).attributes["file-missing"] == "false") {
|
||||
//Note: we need this second case to account for scenarios where a user deletes the downloaded file, we won't automatically
|
||||
// fetch it again, so we need to offer the user the ability to restart..
|
||||
wdgDecorations = Visibility(
|
||||
visible: widget.interactive,
|
||||
child: Center(
|
||||
|
@ -185,10 +202,10 @@ class FileBubbleState extends State<FileBubble> {
|
|||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor,
|
||||
border: Border.all(color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor, width: 1),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(borderRadiousEh),
|
||||
topRight: Radius.circular(borderRadiousEh),
|
||||
bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero,
|
||||
bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh),
|
||||
topLeft: Radius.circular(borderRadius),
|
||||
topRight: Radius.circular(borderRadius),
|
||||
bottomLeft: fromMe ? Radius.circular(borderRadius) : Radius.zero,
|
||||
bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadius),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
|
|
|
@ -118,37 +118,37 @@ class MessageBubbleState extends State<MessageBubble> {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(AppLocalizations.of(context)!.clickableLinksWarning),
|
||||
Text(AppLocalizations.of(bcontext)!.clickableLinksWarning),
|
||||
Flex(direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.clickableLinksCopy, semanticsLabel: AppLocalizations.of(context)!.clickableLinksCopy),
|
||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinksCopy, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinksCopy),
|
||||
onPressed: () {
|
||||
Clipboard.setData(new ClipboardData(text: link.url));
|
||||
|
||||
final snackBar = SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification),
|
||||
content: Text(AppLocalizations.of(bcontext)!.copiedToClipboardNotification),
|
||||
);
|
||||
|
||||
Navigator.pop(bcontext);
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
||||
},
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.clickableLinkOpen, semanticsLabel: AppLocalizations.of(context)!.clickableLinkOpen),
|
||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinkOpen, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinkOpen),
|
||||
onPressed: () async {
|
||||
if (await canLaunch(link.url)) {
|
||||
await launch(link.url);
|
||||
Navigator.pop(bcontext);
|
||||
} else {
|
||||
final snackBar = SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.clickableLinkError),
|
||||
content: Text(AppLocalizations.of(bcontext)!.clickableLinkError),
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue