refactor: Dialogs as views

This commit is contained in:
Christian Pauly 2021-04-24 09:15:59 +02:00
commit 2e8c11fd76
15 changed files with 18 additions and 18 deletions

View file

@ -1,276 +0,0 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:famedlysdk/encryption.dart';
import 'package:famedlysdk/encryption/utils/bootstrap.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/widgets/dialogs/adaptive_flat_button.dart';
import 'package:flutter/services.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'key_verification_dialog.dart';
class BootstrapDialog extends StatefulWidget {
final bool wipe;
final Client client;
const BootstrapDialog({
Key key,
this.wipe = false,
@required this.client,
}) : super(key: key);
Future<bool> show(BuildContext context) => PlatformInfos.isCupertinoStyle
? showCupertinoDialog(
context: context,
builder: (context) => this,
useRootNavigator: false,
barrierDismissible: true,
)
: showDialog(
context: context,
builder: (context) => this,
useRootNavigator: false,
barrierDismissible: true,
);
@override
_BootstrapDialogState createState() => _BootstrapDialogState();
}
class _BootstrapDialogState extends State<BootstrapDialog> {
final TextEditingController _recoveryKeyTextEditingController =
TextEditingController();
Bootstrap bootstrap;
String _recoveryKeyInputError;
bool _recoveryKeyInputLoading = false;
String titleText;
bool _recoveryKeyStored = false;
bool _wipe;
void _createBootstrap(bool wipe) {
setState(() {
_wipe = wipe;
titleText = null;
_recoveryKeyStored = false;
bootstrap = widget.client.encryption
.bootstrap(onUpdate: () => setState(() => null));
});
}
@override
Widget build(BuildContext context) {
_wipe ??= widget.wipe;
final buttons = <AdaptiveFlatButton>[];
Widget body = LinearProgressIndicator();
titleText = L10n.of(context).loadingPleaseWait;
if (bootstrap == null) {
titleText = L10n.of(context).chatBackup;
body = Text(L10n.of(context).chatBackupDescription);
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).next,
onPressed: () => _createBootstrap(false),
));
} else if (bootstrap.newSsssKey?.recoveryKey != null &&
_recoveryKeyStored == false) {
final key = bootstrap.newSsssKey.recoveryKey;
titleText = L10n.of(context).securityKey;
body = Container(
alignment: Alignment.center,
width: 200,
height: 128,
child: Text(
key,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
wordSpacing: 38,
fontFamily: 'monospace',
),
),
);
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).copyToClipboard,
onPressed: () => Clipboard.setData(ClipboardData(text: key)),
));
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).next,
onPressed: () => setState(() => _recoveryKeyStored = true),
));
} else {
switch (bootstrap.state) {
case BootstrapState.loading:
break;
case BootstrapState.askWipeSsss:
WidgetsBinding.instance.addPostFrameCallback(
(_) => bootstrap.wipeSsss(_wipe),
);
break;
case BootstrapState.askUseExistingSsss:
WidgetsBinding.instance.addPostFrameCallback(
(_) => bootstrap.useExistingSsss(!_wipe),
);
break;
case BootstrapState.askUnlockSsss:
throw Exception('This state is not supposed to be implemented');
case BootstrapState.askNewSsss:
WidgetsBinding.instance.addPostFrameCallback(
(_) => bootstrap.newSsss(),
);
break;
case BootstrapState.openExistingSsss:
_recoveryKeyStored = true;
titleText =
_recoveryKeyInputError ?? L10n.of(context).pleaseEnterSecurityKey;
body = PlatformInfos.isCupertinoStyle
? CupertinoTextField(
minLines: 1,
maxLines: 1,
autofocus: true,
autocorrect: false,
autofillHints: _recoveryKeyInputLoading
? null
: [AutofillHints.password],
controller: _recoveryKeyTextEditingController,
)
: TextField(
minLines: 1,
maxLines: 1,
autofocus: true,
autocorrect: false,
autofillHints: _recoveryKeyInputLoading
? null
: [AutofillHints.password],
controller: _recoveryKeyTextEditingController,
decoration: InputDecoration(
border: UnderlineInputBorder(),
filled: false,
hintText: L10n.of(context).securityKey,
),
);
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).unlockChatBackup,
onPressed: () async {
setState(() {
_recoveryKeyInputError = null;
_recoveryKeyInputLoading = true;
});
try {
await bootstrap.newSsssKey.unlock(
keyOrPassphrase: _recoveryKeyTextEditingController.text,
);
await bootstrap.openExistingSsss();
} catch (e, s) {
Logs().w('Unable to unlock SSSS', e, s);
setState(() => _recoveryKeyInputError =
L10n.of(context).oopsSomethingWentWrong);
} finally {
setState(() => _recoveryKeyInputLoading = false);
}
}));
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).transferFromAnotherDevice,
onPressed: () async {
final req = await widget
.client.userDeviceKeys[widget.client.userID]
.startVerification();
await KeyVerificationDialog(request: req).show(context);
Navigator.of(context, rootNavigator: false).pop();
},
));
buttons.add(AdaptiveFlatButton(
textColor: Colors.red,
label: L10n.of(context).securityKeyLost,
onPressed: () async {
if (OkCancelResult.ok ==
await showOkCancelAlertDialog(
context: context,
useRootNavigator: false,
title: L10n.of(context).securityKeyLost,
message: L10n.of(context).wipeChatBackup,
okLabel: L10n.of(context).ok,
cancelLabel: L10n.of(context).cancel,
isDestructiveAction: true,
)) {
_createBootstrap(true);
}
},
));
break;
case BootstrapState.askWipeCrossSigning:
WidgetsBinding.instance.addPostFrameCallback(
(_) => bootstrap.wipeCrossSigning(_wipe),
);
break;
case BootstrapState.askSetupCrossSigning:
WidgetsBinding.instance.addPostFrameCallback(
(_) => bootstrap.askSetupCrossSigning(
setupMasterKey: true,
setupSelfSigningKey: true,
setupUserSigningKey: true,
),
);
break;
case BootstrapState.askWipeOnlineKeyBackup:
WidgetsBinding.instance.addPostFrameCallback(
(_) => bootstrap.wipeOnlineKeyBackup(_wipe),
);
break;
case BootstrapState.askSetupOnlineKeyBackup:
WidgetsBinding.instance.addPostFrameCallback(
(_) => bootstrap.askSetupOnlineKeyBackup(true),
);
break;
case BootstrapState.askBadSsss:
case BootstrapState.error:
titleText = L10n.of(context).oopsSomethingWentWrong;
body = ListTile(
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.error_outline, color: Colors.red),
title: Text(L10n.of(context).oopsSomethingWentWrong),
);
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).close,
onPressed: () =>
Navigator.of(context, rootNavigator: false).pop<bool>(false),
));
break;
case BootstrapState.done:
titleText = L10n.of(context).everythingReady;
body = ListTile(
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.check_circle, color: Colors.green),
title: Text(L10n.of(context).keysCached),
);
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).close,
onPressed: () =>
Navigator.of(context, rootNavigator: false).pop<bool>(false),
));
break;
}
}
final title = Text(titleText);
if (PlatformInfos.isCupertinoStyle) {
return CupertinoAlertDialog(
title: title,
content: body,
actions: buttons,
);
}
return AlertDialog(
title: title,
content: body,
actions: buttons,
);
}
}

View file

@ -1,395 +0,0 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:famedlysdk/encryption.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'adaptive_flat_button.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import '../../../utils/string_color.dart';
import '../../../utils/beautify_string_extension.dart';
class KeyVerificationDialog extends StatefulWidget {
Future<void> show(BuildContext context) => PlatformInfos.isCupertinoStyle
? showCupertinoDialog(
context: context,
barrierDismissible: true,
builder: (context) => this,
useRootNavigator: false,
)
: showDialog(
context: context,
barrierDismissible: true,
builder: (context) => this,
useRootNavigator: false,
);
final KeyVerification request;
KeyVerificationDialog({
this.request,
});
@override
_KeyVerificationPageState createState() => _KeyVerificationPageState();
}
class _KeyVerificationPageState extends State<KeyVerificationDialog> {
void Function() originalOnUpdate;
final _scrollController = ScrollController();
@override
void initState() {
originalOnUpdate = widget.request.onUpdate;
widget.request.onUpdate = () {
if (originalOnUpdate != null) {
originalOnUpdate();
}
setState(() => null);
};
widget.request.client.getProfileFromUserId(widget.request.userId).then((p) {
profile = p;
setState(() => null);
});
super.initState();
}
@override
void dispose() {
widget.request.onUpdate =
originalOnUpdate; // don't want to get updates anymore
if (![KeyVerificationState.error, KeyVerificationState.done]
.contains(widget.request.state)) {
widget.request.cancel('m.user');
}
_scrollController.dispose();
super.dispose();
}
Profile profile;
@override
Widget build(BuildContext context) {
Widget body;
final buttons = <Widget>[];
switch (widget.request.state) {
case KeyVerificationState.askSSSS:
// prompt the user for their ssss passphrase / key
final textEditingController = TextEditingController();
String input;
final checkInput = () async {
if (input == null || input.isEmpty) {
return;
}
final valid = await showFutureLoadingDialog(
context: context,
future: () async {
// make sure the loading spinner shows before we test the keys
await Future.delayed(Duration(milliseconds: 100));
var valid = false;
try {
await widget.request.openSSSS(keyOrPassphrase: input);
valid = true;
} catch (_) {
valid = false;
}
return valid;
});
if (valid.error != null) {
await showOkAlertDialog(
context: context,
message: L10n.of(context).incorrectPassphraseOrKey,
useRootNavigator: false,
);
}
};
body = Container(
margin: EdgeInsets.only(left: 8.0, right: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(L10n.of(context).askSSSSSign,
style: TextStyle(fontSize: 20)),
Container(height: 10),
TextField(
controller: textEditingController,
autofocus: false,
autocorrect: false,
onSubmitted: (s) {
input = s;
checkInput();
},
minLines: 1,
maxLines: 1,
obscureText: true,
decoration: InputDecoration(
hintText: L10n.of(context).passphraseOrKey,
prefixStyle: TextStyle(color: Theme.of(context).primaryColor),
suffixStyle: TextStyle(color: Theme.of(context).primaryColor),
border: OutlineInputBorder(),
),
),
],
),
);
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).submit,
onPressed: () {
input = textEditingController.text;
checkInput();
},
));
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).skip,
onPressed: () => widget.request.openSSSS(skip: true),
));
break;
case KeyVerificationState.askAccept:
body = Container(
margin: EdgeInsets.only(left: 8.0, right: 8.0),
child: Text(
L10n.of(context).askVerificationRequest(widget.request.userId),
style: TextStyle(fontSize: 20)),
);
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).accept,
onPressed: () => widget.request.acceptVerification(),
));
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).reject,
onPressed: () {
widget.request.rejectVerification().then((_) {
Navigator.of(context, rootNavigator: false).pop();
});
},
));
break;
case KeyVerificationState.waitingAccept:
body = Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
PlatformInfos.isCupertinoStyle
? CupertinoActivityIndicator()
: CircularProgressIndicator(),
SizedBox(height: 10),
Text(
L10n.of(context).waitingPartnerAcceptRequest,
textAlign: TextAlign.center,
),
],
);
final key = widget.request.client.userDeviceKeys[widget.request.userId]
.deviceKeys[widget.request.deviceId];
if (key != null) {
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).verifyManual,
onPressed: () async {
final result = await showOkCancelAlertDialog(
context: context,
useRootNavigator: false,
title: L10n.of(context).verifyManual,
message: key.ed25519Key.beautified,
);
if (result == OkCancelResult.ok) {
await key.setVerified(true);
}
await widget.request.cancel();
Navigator.of(context, rootNavigator: false).pop();
},
));
}
break;
case KeyVerificationState.askSas:
TextSpan compareWidget;
// maybe add a button to switch between the two and only determine default
// view for if "emoji" is a present sasType or not?
String compareText;
if (widget.request.sasTypes.contains('emoji')) {
compareText = L10n.of(context).compareEmojiMatch;
compareWidget = TextSpan(
children: widget.request.sasEmojis
.map((e) => WidgetSpan(child: _Emoji(e)))
.toList(),
);
} else {
compareText = L10n.of(context).compareNumbersMatch;
final numbers = widget.request.sasNumbers;
final numbstr = '${numbers[0]}-${numbers[1]}-${numbers[2]}';
compareWidget =
TextSpan(text: numbstr, style: TextStyle(fontSize: 40));
}
body = Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Center(
child: Text(
compareText,
style: TextStyle(fontSize: 16),
textAlign: TextAlign.center,
),
),
SizedBox(height: 10),
Text.rich(
compareWidget,
textAlign: TextAlign.center,
),
],
);
buttons.add(AdaptiveFlatButton(
textColor: Colors.red,
label: L10n.of(context).theyDontMatch,
onPressed: () => widget.request.rejectSas(),
));
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).theyMatch,
onPressed: () => widget.request.acceptSas(),
));
break;
case KeyVerificationState.waitingSas:
final acceptText = widget.request.sasTypes.contains('emoji')
? L10n.of(context).waitingPartnerEmoji
: L10n.of(context).waitingPartnerNumbers;
body = Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
PlatformInfos.isCupertinoStyle
? CupertinoActivityIndicator()
: CircularProgressIndicator(),
SizedBox(height: 10),
Text(
acceptText,
textAlign: TextAlign.center,
),
],
);
break;
case KeyVerificationState.done:
body = Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.check_circle_outlined, color: Colors.green, size: 200.0),
SizedBox(height: 10),
Text(
L10n.of(context).verifySuccess,
textAlign: TextAlign.center,
),
],
);
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).close,
onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
));
break;
case KeyVerificationState.error:
body = Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.cancel, color: Colors.red, size: 200.0),
SizedBox(height: 10),
Text(
'Error ${widget.request.canceledCode}: ${widget.request.canceledReason}',
textAlign: TextAlign.center,
),
],
);
buttons.add(AdaptiveFlatButton(
label: L10n.of(context).close,
onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
));
break;
}
body ??= Text('ERROR: Unknown state ' + widget.request.state.toString());
final otherName = profile?.displayname ?? widget.request.userId;
var bottom;
if (widget.request.deviceId != null) {
final deviceName = widget
.request
.client
.userDeviceKeys[widget.request.userId]
?.deviceKeys[widget.request.deviceId]
?.deviceDisplayName ??
'';
bottom = Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
child: Text('$deviceName (${widget.request.deviceId})',
style: TextStyle(color: Theme.of(context).textTheme.caption.color)),
);
}
final userNameTitle = Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
otherName,
maxLines: 1,
style: TextStyle(
color: otherName.color,
fontWeight: FontWeight.bold,
),
),
if (otherName != widget.request.userId)
Text(
' - ' + widget.request.userId,
maxLines: 1,
style: TextStyle(
fontStyle: FontStyle.italic,
),
),
],
);
final title = Text(L10n.of(context).verifyTitle);
final content = Scrollbar(
isAlwaysShown: true,
controller: _scrollController,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
controller: _scrollController,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (PlatformInfos.isCupertinoStyle) ...[
SizedBox(height: 8),
Center(child: userNameTitle),
SizedBox(height: 12),
],
body,
if (bottom != null) bottom,
],
),
),
);
if (PlatformInfos.isCupertinoStyle) {
return CupertinoAlertDialog(
title: title,
content: content,
actions: buttons,
);
}
return AlertDialog(
title: title,
content: content,
actions: buttons,
);
}
}
class _Emoji extends StatelessWidget {
final KeyVerificationEmoji emoji;
_Emoji(this.emoji);
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(emoji.emoji, style: TextStyle(fontSize: 50)),
Text(emoji.name),
Container(height: 10, width: 5),
],
);
}
}

View file

@ -1,97 +0,0 @@
import 'package:fluffychat/views/widgets/dialogs/adaptive_flat_button.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class PermissionSliderDialog extends StatefulWidget {
const PermissionSliderDialog({
Key key,
this.initialPermission = 0,
}) : super(key: key);
Future<int> show(BuildContext context) => PlatformInfos.isCupertinoStyle
? showCupertinoDialog<int>(
context: context,
builder: (context) => this,
useRootNavigator: false,
)
: showDialog<int>(
context: context,
builder: (context) => this,
useRootNavigator: false,
);
final int initialPermission;
@override
_PermissionSliderDialogState createState() => _PermissionSliderDialogState();
}
class _PermissionSliderDialogState extends State<PermissionSliderDialog> {
int _permission;
@override
void initState() {
_permission = widget.initialPermission;
super.initState();
}
@override
Widget build(BuildContext context) {
final slider = PlatformInfos.isCupertinoStyle
? CupertinoSlider(
value: _permission.toDouble(),
onChanged: (d) => setState(() => _permission = d.round()),
max: 100.0,
min: 0.0,
)
: Slider(
value: _permission.toDouble(),
onChanged: (d) => setState(() => _permission = d.round()),
max: 100.0,
min: 0.0,
);
final title = Text(
L10n.of(context).setPermissionsLevel,
textAlign: TextAlign.center,
);
final content = Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Level: ' +
(_permission == 100
? '$_permission (${L10n.of(context).admin})'
: _permission >= 50
? '$_permission (${L10n.of(context).moderator})'
: _permission.toString())),
Container(
height: 56,
child: slider,
),
],
);
final buttons = [
AdaptiveFlatButton(
label: L10n.of(context).cancel,
onPressed: () =>
Navigator.of(context, rootNavigator: false).pop<int>(null),
),
AdaptiveFlatButton(
label: L10n.of(context).confirm,
onPressed: () =>
Navigator.of(context, rootNavigator: false).pop<int>(_permission),
),
];
if (PlatformInfos.isCupertinoStyle) {
return CupertinoAlertDialog(
title: title,
content: content,
actions: buttons,
);
}
return AlertDialog(
title: title,
content: content,
actions: buttons,
);
}
}

View file

@ -1,137 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_sound_lite/flutter_sound.dart';
import 'package:path_provider/path_provider.dart';
class RecordingDialog extends StatefulWidget {
const RecordingDialog({
Key key,
}) : super(key: key);
@override
_RecordingDialogState createState() => _RecordingDialogState();
}
class _RecordingDialogState extends State<RecordingDialog> {
final FlutterSoundRecorder flutterSound = FlutterSoundRecorder();
String time = '00:00:00';
StreamSubscription _recorderSubscription;
bool error = false;
String _recordedPath;
double _decibels = 0;
void startRecording() async {
try {
await flutterSound.openAudioSession();
await flutterSound.setSubscriptionDuration(Duration(milliseconds: 100));
final codec = Codec.aacADTS;
final tempDir = await getTemporaryDirectory();
_recordedPath = '${tempDir.path}/recording${ext[codec.index]}';
// delete any existing file
final outputFile = File(_recordedPath);
if (outputFile.existsSync()) {
await outputFile.delete();
}
await flutterSound.startRecorder(codec: codec, toFile: _recordedPath);
_recorderSubscription = flutterSound.onProgress.listen((e) {
setState(() {
_decibels = e.decibels;
time =
'${e.duration.inMinutes.toString().padLeft(2, '0')}:${(e.duration.inSeconds % 60).toString().padLeft(2, '0')}';
});
});
} catch (e) {
error = true;
}
}
@override
void initState() {
super.initState();
startRecording();
}
@override
void dispose() {
if (flutterSound.isRecording) flutterSound.stopRecorder();
_recorderSubscription?.cancel();
flutterSound.closeAudioSession();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (error) {
Timer(Duration(seconds: 1), () {
Navigator.of(context, rootNavigator: false).pop();
});
}
const maxDecibalWidth = 64.0;
final decibalWidth = min(_decibels / 2, maxDecibalWidth).toDouble();
return AlertDialog(
content: Row(
children: <Widget>[
Container(
width: maxDecibalWidth,
height: maxDecibalWidth,
alignment: Alignment.center,
child: AnimatedContainer(
duration: Duration(milliseconds: 50),
width: decibalWidth,
height: decibalWidth,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(decibalWidth),
),
),
),
SizedBox(width: 8),
Expanded(
child: Text(
'${L10n.of(context).recording}: $time',
style: TextStyle(
fontSize: 18,
),
),
),
],
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
child: Text(
L10n.of(context).cancel.toUpperCase(),
style: TextStyle(
color: Theme.of(context).textTheme.bodyText2.color.withAlpha(150),
),
),
),
TextButton(
onPressed: () async {
await _recorderSubscription?.cancel();
await flutterSound.stopRecorder();
Navigator.of(context, rootNavigator: false)
.pop<String>(_recordedPath);
},
child: Row(
children: <Widget>[
Text(L10n.of(context).send.toUpperCase()),
SizedBox(width: 4),
Icon(Icons.send_outlined, size: 15),
],
),
),
],
);
}
}

View file

@ -1,103 +0,0 @@
import 'package:famedlysdk/famedlysdk.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import '../../../utils/matrix_file_extension.dart';
import '../../../utils/room_send_file_extension.dart';
import '../../../utils/resize_image.dart';
class SendFileDialog extends StatefulWidget {
final Room room;
final MatrixFile file;
const SendFileDialog({
this.room,
this.file,
Key key,
}) : super(key: key);
@override
_SendFileDialogState createState() => _SendFileDialogState();
}
class _SendFileDialogState extends State<SendFileDialog> {
bool origImage = false;
bool _isSending = false;
Future<void> _send() async {
var file = widget.file;
if (file is MatrixImageFile && !origImage) {
try {
file = await resizeImage(file, max: 1600);
} catch (e) {
// couldn't resize
}
}
await widget.room.sendFileEventWithThumbnail(file);
}
@override
Widget build(BuildContext context) {
var sendStr = L10n.of(context).sendFile;
if (widget.file is MatrixImageFile) {
sendStr = L10n.of(context).sendImage;
} else if (widget.file is MatrixAudioFile) {
sendStr = L10n.of(context).sendAudio;
} else if (widget.file is MatrixVideoFile) {
sendStr = L10n.of(context).sendVideo;
}
Widget contentWidget;
if (widget.file is MatrixImageFile) {
contentWidget = Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Flexible(
child: Image.memory(
widget.file.bytes,
fit: BoxFit.contain,
),
),
Text(widget.file.name),
Row(
children: <Widget>[
Checkbox(
value: origImage,
onChanged: (v) => setState(() => origImage = v),
),
InkWell(
onTap: () => setState(() => origImage = !origImage),
child: Text(L10n.of(context).sendOriginal +
' (${widget.file.sizeString})'),
),
],
)
]);
} else {
contentWidget = Text('${widget.file.name} (${widget.file.sizeString})');
}
return AlertDialog(
title: Text(sendStr),
content: contentWidget,
actions: <Widget>[
TextButton(
onPressed: () {
// just close the dialog
Navigator.of(context, rootNavigator: false).pop();
},
child: Text(L10n.of(context).cancel),
),
TextButton(
onPressed: _isSending
? null
: () async {
setState(() {
_isSending = true;
});
await showFutureLoadingDialog(
context: context, future: () => _send());
Navigator.of(context, rootNavigator: false).pop();
},
child: Text(L10n.of(context).send),
),
],
);
}
}

View file

@ -12,7 +12,7 @@ import 'package:pedantic/pedantic.dart';
import '../../../utils/date_time_extension.dart';
import '../avatar.dart';
import '../dialogs/send_file_dialog.dart';
import '../../send_file_dialog.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import '../matrix.dart';
import '../mouse_over_builder.dart';

View file

@ -27,7 +27,7 @@ import 'package:desktop_notifications/desktop_notifications.dart';*/
import '../../utils/beautify_string_extension.dart';
import '../../utils/localized_exception_extension.dart';
import '../../utils/famedlysdk_store.dart';
import 'dialogs/key_verification_dialog.dart';
import '../key_verification_dialog.dart';
import '../../utils/platform_infos.dart';
import '../../config/app_config.dart';
import '../../config/setting_keys.dart';

View file

@ -6,7 +6,7 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/views/widgets/image_bubble.dart';
import 'package:fluffychat/utils/event_extension.dart';
import 'package:fluffychat/utils/matrix_locals.dart';
import 'package:fluffychat/views/widgets/dialogs/key_verification_dialog.dart';
import 'package:fluffychat/views/key_verification_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

View file

@ -3,7 +3,7 @@ import 'dart:math';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/widgets/dialogs/permission_slider_dialog.dart';
import 'package:fluffychat/views/permission_slider_dialog.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:flutter/material.dart';