forked from cwtch.im/cwtch-ui
wip image previews
This commit is contained in:
parent
6a5309427f
commit
33a8a30170
|
@ -96,5 +96,8 @@ abstract class Cwtch {
|
|||
// ignore: non_constant_identifier_names
|
||||
void Shutdown();
|
||||
|
||||
// non-ffi
|
||||
String defaultDownloadPath();
|
||||
|
||||
void dispose();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)]),
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
]));
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue