wip image previews

This commit is contained in:
erinn 2021-12-14 13:33:30 -08:00
parent 6a5309427f
commit 33a8a30170
10 changed files with 180 additions and 14 deletions

View File

@ -96,5 +96,8 @@ abstract class Cwtch {
// ignore: non_constant_identifier_names
void Shutdown();
// non-ffi
String defaultDownloadPath();
void dispose();
}

View File

@ -263,7 +263,7 @@ class CwtchNotifier {
settings.handleUpdate(jsonDecode(data["Data"]));
break;
case "SetAttribute":
if (data["Key"] == "public.name") {
if (data["Key"] == "public.profile.name") {//"public.name") {
profileCN.getProfile(data["ProfileOnion"])?.nickname = data["Data"];
} else {
EnvironmentConfig.debugLog("unhandled set attribute event: ${data['Key']}");
@ -366,6 +366,11 @@ class CwtchNotifier {
EnvironmentConfig.debugLog("unhandled peer attribute event: ${data['Path']}");
}
break;
case "ManifestSizeReceived":
if (!profileCN.getProfile(data["ProfileOnion"])!.downloadActive(data["FileKey"])) {
profileCN.getProfile(data["ProfileOnion"])?.downloadUpdate(data["FileKey"], 0, 1);
}
break;
case "ManifestSaved":
profileCN.getProfile(data["ProfileOnion"])?.downloadMarkManifest(data["FileKey"]);
break;

View File

@ -557,7 +557,7 @@ class CwtchFfi implements Cwtch {
final SetProfileAttribute = setProfileAttribute.asFunction<VoidFromStringStringStringFn>();
final u1 = profile.toNativeUtf8();
final u2 = key.toNativeUtf8();
final u3 = key.toNativeUtf8();
final u3 = val.toNativeUtf8();
SetProfileAttribute(u1, u1.length, u2, u2.length, u3, u3.length);
malloc.free(u1);
malloc.free(u2);
@ -728,4 +728,10 @@ class CwtchFfi implements Cwtch {
final Free = free.asFunction<FreeFn>();
Free(ptr);
}
@override
String defaultDownloadPath() {
Map<String, String> envVars = Platform.environment;
return path.join(envVars['HOME']!, "Downloads");
}
}

View File

@ -28,6 +28,7 @@ class CwtchGomobile implements Cwtch {
late Future<dynamic> androidLibraryDir;
late Future<dynamic> androidHomeDirectory;
String androidHomeDirectoryStr = "";
late CwtchNotifier cwtchNotifier;
CwtchGomobile(CwtchNotifier _cwtchNotifier) {
@ -44,7 +45,8 @@ class CwtchGomobile implements Cwtch {
// ignore: non_constant_identifier_names
Future<void> Start() async {
print("gomobile.dart: Start()...");
var cwtchDir = path.join((await androidHomeDirectory).path, ".cwtch");
androidHomeDirectoryStr = (await androidHomeDirectory).path;
var cwtchDir = path.join(androidHomeDirectoryStr, ".cwtch");
if (EnvironmentConfig.BUILD_VER == dev_version) {
cwtchDir = path.join(cwtchDir, "dev");
}
@ -283,4 +285,9 @@ class CwtchGomobile implements Cwtch {
Future GetMessageByContentHash(String profile, String handle, String contentHash) {
return cwtchPlatform.invokeMethod("GetMessageByContentHash", {"profile": profile, "contact": handle, "contentHash": contentHash});
}
@override
String defaultDownloadPath() {
return this.androidHomeDirectoryStr;
}
}

View File

@ -364,13 +364,11 @@ class ProfileInfoState extends ChangeNotifier {
void downloadUpdate(String fileKey, int progress, int numChunks) {
if (!downloadActive(fileKey)) {
this._downloads[fileKey] = FileDownloadProgress(numChunks, DateTime.now());
if (progress < 0) {
this._downloads[fileKey] = FileDownloadProgress(numChunks, DateTime.now());
this._downloads[fileKey]!.interrupted = true;
notifyListeners();
} else {
print("error: received progress for unknown download " + fileKey);
}
notifyListeners();
} else {
if (this._downloads[fileKey]!.interrupted) {
this._downloads[fileKey]!.interrupted = false;
@ -383,11 +381,10 @@ class ProfileInfoState extends ChangeNotifier {
void downloadMarkManifest(String fileKey) {
if (!downloadActive(fileKey)) {
print("error: received download completion notice for unknown download " + fileKey);
} else {
this._downloads[fileKey]!.gotManifest = true;
notifyListeners();
this._downloads[fileKey] = FileDownloadProgress(1, DateTime.now());
}
this._downloads[fileKey]!.gotManifest = true;
notifyListeners();
}
void downloadMarkFinished(String fileKey, String finalPath) {

View File

@ -11,6 +11,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
const TapirGroupsExperiment = "tapir-groups-experiment";
const ServerManagementExperiment = "servers-experiment";
const FileSharingExperiment = "filesharing";
const ImagePreviewsExperiment = "filesharing-images";
enum DualpaneMode {
Single,
@ -34,6 +35,7 @@ class Settings extends ChangeNotifier {
bool blockUnknownConnections = false;
bool streamerMode = false;
String _downloadPath = "";
/// Set the dark theme.
void setDark() {
@ -90,6 +92,13 @@ class Settings extends ChangeNotifier {
_uiColumnModePortrait = uiColumnModeFromString(settings["UIColumnModePortrait"]);
_uiColumnModeLandscape = uiColumnModeFromString(settings["UIColumnModeLandscape"]);
// image previews/profile pic storage path
for (var i = 0; i < 30; i++) {
print("|");
}
print("setting DownloadPath to " + settings["DownloadPath"]);
_downloadPath = settings["DownloadPath"] ?? "";
// Push the experimental settings to Consumers of Settings
notifyListeners();
}
@ -222,6 +231,12 @@ class Settings extends ChangeNotifier {
}
}
String get downloadPath => _downloadPath;
set downloadPath(String newval) {
_downloadPath = newval;
notifyListeners();
}
/// Construct a default settings object.
Settings(this.locale, this.theme);
@ -242,6 +257,7 @@ class Settings extends ChangeNotifier {
"FirstTime": false,
"UIColumnModePortrait": uiColumnModePortrait.toString(),
"UIColumnModeLandscape": uiColumnModeLandscape.toString(),
"DownloadPath": _downloadPath,
};
}
}

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:cwtch/models/servers.dart';
import 'package:cwtch/widgets/folderpicker.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/settings.dart';
@ -227,6 +228,39 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(Icons.attach_file, color: settings.current().mainTextColor()),
),
Visibility(
visible: settings.isExperimentEnabled(FileSharingExperiment),
child: Column(children:[
SwitchListTile(
title: Text("Image Previews and Profile Pics", style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text("TODO: Write a description with lots of warnings about how image previews can be an insecurity vector and you should only enable them with great caution..."),
value: settings.isExperimentEnabled(ImagePreviewsExperiment),
onChanged: (bool value) {
if (value) {
settings.enableExperiment(ImagePreviewsExperiment);
settings.downloadPath = Provider.of<FlwtchState>(context, listen: false).cwtch.defaultDownloadPath();
} else {
settings.disableExperiment(ImagePreviewsExperiment);
}
saveSettings(context);
},
activeTrackColor: settings.theme.defaultButtonActiveColor(),
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(Icons.attach_file, color: settings.current().mainTextColor()),
),
Visibility(
visible: settings.isExperimentEnabled(ImagePreviewsExperiment),
child: CwtchFolderPicker(
label:"Download folder",//todo:l18n
initialValue: settings.downloadPath,
onSave: (newVal) {
settings.downloadPath = newVal;
saveSettings(context);
},
),
),
]),
),
],
)),
AboutListTile(

View File

@ -33,6 +33,8 @@ class FileBubble extends StatefulWidget {
}
class FileBubbleState extends State<FileBubble> {
File? myFile;
@override
void initState() {
super.initState();
@ -62,6 +64,7 @@ class FileBubbleState extends State<FileBubble> {
child: SelectableText(senderDisplayStr + '\u202F',
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor())));
var isPreview = false;
var wdgMessage = !showFileSharing
? Text(AppLocalizations.of(context)!.messageEnableFileSharing)
: fromMe
@ -76,7 +79,17 @@ class FileBubbleState extends State<FileBubble> {
} else if (Provider.of<ProfileInfoState>(context).downloadComplete(widget.fileKey())) {
// in this case, whatever marked download.complete would have also set the path
var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey())!;
wdgDecorations = Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F');
var lpath = path.toLowerCase();
if (lpath.endsWith("jpg") || lpath.endsWith("jpeg") || lpath.endsWith("png") || lpath.endsWith("gif") || lpath.endsWith("webp") || lpath.endsWith("bmp")) {
if (myFile == null) {
setState(() { myFile = new File(path); });
}
isPreview = true;
wdgDecorations = GestureDetector(child: Image.file(myFile!, width: 200), onTap:(){pop(myFile!, wdgMessage);},);
} else {
wdgDecorations = Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F');
}
} else if (Provider.of<ProfileInfoState>(context).downloadActive(widget.fileKey())) {
if (!Provider.of<ProfileInfoState>(context).downloadGotManifest(widget.fileKey())) {
wdgDecorations = Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F');
@ -136,7 +149,7 @@ class FileBubbleState extends State<FileBubble> {
mainAxisSize: MainAxisSize.min,
children: fromMe
? [wdgMessage, Visibility(visible: widget.interactive, child: wdgDecorations)]
: [wdgSender, wdgMessage, Visibility(visible: widget.interactive, child: wdgDecorations)]),
: [wdgSender, isPreview ? Container() : wdgMessage, Visibility(visible: widget.interactive, child: wdgDecorations)]),
)
])))));
});
@ -289,4 +302,22 @@ class FileBubbleState extends State<FileBubble> {
)),
);
}
void pop(File myFile, Widget meta) async {
await showDialog(
context: context,
builder: (_) => Dialog(
child: Container(padding: EdgeInsets.all(10), width: 500, height: 550, child:Column(children:[meta,Container(
width: 300,
height: 300,
decoration: BoxDecoration(
image: DecorationImage(
image: Image.file(myFile).image,
fit: BoxFit.scaleDown
)
),
),Icon(Icons.arrow_downward)]),
))
);
}
}

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'dart:io';
import 'package:file_picker_desktop/file_picker_desktop.dart';
import 'buttontextfield.dart';
import 'cwtchlabel.dart';
class CwtchFolderPicker extends StatefulWidget {
final String label;
final String initialValue;
final Function(String)? onSave;
const CwtchFolderPicker({Key? key, this.label = "", this.initialValue = "", this.onSave}) : super(key: key);
@override
_CwtchFolderPickerState createState() => _CwtchFolderPickerState();
}
class _CwtchFolderPickerState extends State<CwtchFolderPicker> {
final TextEditingController ctrlrVal = TextEditingController();
@override
void initState() {
super.initState();
ctrlrVal.text = widget.initialValue;
}
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(2),
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
CwtchLabel(label: widget.label),
SizedBox(
height: 20,
),
CwtchButtonTextField(
controller: ctrlrVal,
readonly: Platform.isAndroid,
onPressed: () async {
if (Platform.isAndroid) {
return;
}
try {
var selectedDirectory = await getDirectoryPath();
if (selectedDirectory != null) {
//File directory = File(selectedDirectory);
selectedDirectory += "/";
ctrlrVal.text = selectedDirectory;
if (widget.onSave != null) {
widget.onSave!(selectedDirectory);
}
} else {
// User canceled the picker
}
} catch (e) {
print(e);
}
},
icon: Icon(Icons.folder),
tooltip: "Browse",//todo: l18n
)
]));
}
}

View File

@ -102,7 +102,7 @@ class _ProfileRowState extends State<ProfileRow> {
}
void _pushEditProfile({onion: "", displayName: "", profileImage: "", encrypted: true}) {
Provider.of<ErrorHandler>(context).reset();
Provider.of<ErrorHandler>(context, listen: false).reset();
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) {
return MultiProvider(