Merge pull request #2760 from krille-chan/krille/enable-buildcontext-async-gap-lint
refactor: Enable lint use_build_context_synchronously
This commit is contained in:
commit
e22c33a4b1
50 changed files with 490 additions and 293 deletions
|
|
@ -36,7 +36,6 @@ analyzer:
|
||||||
- dart_code_linter
|
- dart_code_linter
|
||||||
errors:
|
errors:
|
||||||
todo: ignore
|
todo: ignore
|
||||||
use_build_context_synchronously: ignore
|
|
||||||
exclude:
|
exclude:
|
||||||
- lib/l10n/*.dart
|
- lib/l10n/*.dart
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ class ArchiveController extends State<Archive> {
|
||||||
OkCancelResult.ok) {
|
OkCancelResult.ok) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
futureWithProgress: (onProgress) async {
|
futureWithProgress: (onProgress) async {
|
||||||
|
|
|
||||||
|
|
@ -382,6 +382,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
||||||
).wrongRecoveryKey,
|
).wrongRecoveryKey,
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
if (!context.mounted) return;
|
||||||
ErrorReporter(
|
ErrorReporter(
|
||||||
context,
|
context,
|
||||||
'Unable to open SSSS with recovery key',
|
'Unable to open SSSS with recovery key',
|
||||||
|
|
@ -425,6 +426,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context).cancel,
|
||||||
);
|
);
|
||||||
if (consent != OkCancelResult.ok) return;
|
if (consent != OkCancelResult.ok) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
final req = await showFutureLoadingDialog(
|
final req = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
delay: false,
|
delay: false,
|
||||||
|
|
@ -435,11 +437,12 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (req.error != null) return;
|
if (req.error != null) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
final success = await KeyVerificationDialog(
|
final success = await KeyVerificationDialog(
|
||||||
request: req.result!,
|
request: req.result!,
|
||||||
).show(context);
|
).show(context);
|
||||||
if (success != true) return;
|
if (success != true) return;
|
||||||
if (!mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
context: context,
|
context: context,
|
||||||
future: room.leave,
|
future: room.leave,
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
if (success.error != null) return;
|
if (success.error != null) return;
|
||||||
context.go('/rooms');
|
context.go('/rooms');
|
||||||
}
|
}
|
||||||
|
|
@ -475,8 +476,9 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
Future<void>? loadTimelineFuture;
|
Future<void>? loadTimelineFuture;
|
||||||
|
|
||||||
Future<void> _getTimeline({String? eventContextId}) async {
|
Future<void> _getTimeline({String? eventContextId}) async {
|
||||||
await Matrix.of(context).client.roomsLoading;
|
final matrix = Matrix.of(context);
|
||||||
await Matrix.of(context).client.accountDataLoading;
|
await matrix.client.roomsLoading;
|
||||||
|
await matrix.client.accountDataLoading;
|
||||||
if (eventContextId != null &&
|
if (eventContextId != null &&
|
||||||
(!eventContextId.isValidMatrixId || eventContextId.sigil != '\$')) {
|
(!eventContextId.isValidMatrixId || eventContextId.sigil != '\$')) {
|
||||||
eventContextId = null;
|
eventContextId = null;
|
||||||
|
|
@ -632,6 +634,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
Future<void> sendFileAction({FileType type = FileType.any}) async {
|
Future<void> sendFileAction({FileType type = FileType.any}) async {
|
||||||
final files = await selectFiles(context, allowMultiple: true, type: type);
|
final files = await selectFiles(context, allowMultiple: true, type: type);
|
||||||
if (files.isEmpty) return;
|
if (files.isEmpty) return;
|
||||||
|
if (!mounted) return;
|
||||||
await showAdaptiveDialog(
|
await showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => SendFileDialog(
|
builder: (c) => SendFileDialog(
|
||||||
|
|
@ -663,6 +666,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
FocusScope.of(context).requestFocus(FocusNode());
|
FocusScope.of(context).requestFocus(FocusNode());
|
||||||
final file = await ImagePicker().pickImage(source: ImageSource.camera);
|
final file = await ImagePicker().pickImage(source: ImageSource.camera);
|
||||||
if (file == null) return;
|
if (file == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
await showAdaptiveDialog(
|
await showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -684,6 +688,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
maxDuration: const Duration(minutes: 1),
|
maxDuration: const Duration(minutes: 1),
|
||||||
);
|
);
|
||||||
if (file == null) return;
|
if (file == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
await showAdaptiveDialog(
|
await showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -726,26 +731,27 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
mimeType: mimeType,
|
mimeType: mimeType,
|
||||||
);
|
);
|
||||||
|
|
||||||
room
|
try {
|
||||||
.sendFileEvent(
|
await room.sendFileEvent(
|
||||||
file,
|
file,
|
||||||
inReplyTo: replyEvent,
|
inReplyTo: replyEvent,
|
||||||
threadRootEventId: activeThreadId,
|
threadRootEventId: activeThreadId,
|
||||||
extraContent: {
|
extraContent: {
|
||||||
'info': {...file.info, 'duration': duration},
|
'info': {...file.info, 'duration': duration},
|
||||||
'org.matrix.msc3245.voice': {},
|
'org.matrix.msc3245.voice': {},
|
||||||
'org.matrix.msc1767.audio': {
|
'org.matrix.msc1767.audio': {
|
||||||
'duration': duration,
|
'duration': duration,
|
||||||
'waveform': waveform,
|
'waveform': waveform,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
},
|
||||||
.catchError((e) {
|
);
|
||||||
scaffoldMessenger.showSnackBar(
|
} catch (e) {
|
||||||
SnackBar(content: Text((e as Object).toLocalizedString(context))),
|
if (!mounted) return;
|
||||||
);
|
scaffoldMessenger.showSnackBar(
|
||||||
return null;
|
SnackBar(content: Text(e.toLocalizedString(context))),
|
||||||
});
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
replyEvent = null;
|
replyEvent = null;
|
||||||
});
|
});
|
||||||
|
|
@ -807,29 +813,30 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
|
|
||||||
Future<void> reportEventAction() async {
|
Future<void> reportEventAction() async {
|
||||||
final event = selectedEvents.single;
|
final event = selectedEvents.single;
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
final score = await showModalActionPopup<int>(
|
final score = await showModalActionPopup<int>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).reportMessage,
|
title: l10n.reportMessage,
|
||||||
message: L10n.of(context).howOffensiveIsThisContent,
|
message: l10n.howOffensiveIsThisContent,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
actions: [
|
actions: [
|
||||||
AdaptiveModalAction(
|
AdaptiveModalAction(value: -100, label: l10n.extremeOffensive),
|
||||||
value: -100,
|
AdaptiveModalAction(value: -50, label: l10n.offensive),
|
||||||
label: L10n.of(context).extremeOffensive,
|
AdaptiveModalAction(value: 0, label: l10n.inoffensive),
|
||||||
),
|
|
||||||
AdaptiveModalAction(value: -50, label: L10n.of(context).offensive),
|
|
||||||
AdaptiveModalAction(value: 0, label: L10n.of(context).inoffensive),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (score == null) return;
|
if (score == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final reason = await showTextInputDialog(
|
final reason = await showTextInputDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).whyDoYouWantToReportThis,
|
title: l10n.whyDoYouWantToReportThis,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
hintText: L10n.of(context).reason,
|
hintText: l10n.reason,
|
||||||
);
|
);
|
||||||
if (reason == null || reason.isEmpty) return;
|
if (reason == null || reason.isEmpty) return;
|
||||||
|
if (!mounted) return;
|
||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context).client.reportEvent(
|
future: () => Matrix.of(context).client.reportEvent(
|
||||||
|
|
@ -840,12 +847,13 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (result.error != null) return;
|
if (result.error != null) return;
|
||||||
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
showEmojiPicker = false;
|
showEmojiPicker = false;
|
||||||
selectedEvents.clear();
|
selectedEvents.clear();
|
||||||
});
|
});
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).contentHasBeenReported)),
|
SnackBar(content: Text(l10n.contentHasBeenReported)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -861,6 +869,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
}
|
}
|
||||||
setState(selectedEvents.clear);
|
setState(selectedEvents.clear);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
if (!mounted) return;
|
||||||
ErrorReporter(
|
ErrorReporter(
|
||||||
context,
|
context,
|
||||||
'Error while delete error events action',
|
'Error while delete error events action',
|
||||||
|
|
@ -885,6 +894,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
: null;
|
: null;
|
||||||
if (reasonInput == null) return;
|
if (reasonInput == null) return;
|
||||||
final reason = reasonInput.isEmpty ? null : reasonInput;
|
final reason = reasonInput.isEmpty ? null : reasonInput;
|
||||||
|
if (!mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
futureWithProgress: (onProgress) async {
|
futureWithProgress: (onProgress) async {
|
||||||
|
|
@ -1239,6 +1249,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
okLabel: L10n.of(context).unpin,
|
okLabel: L10n.of(context).unpin,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context).cancel,
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
if (response == OkCancelResult.ok) {
|
if (response == OkCancelResult.ok) {
|
||||||
final events = room.pinnedEventIds
|
final events = room.pinnedEventIds
|
||||||
..removeWhere((oldEvent) => oldEvent == eventId);
|
..removeWhere((oldEvent) => oldEvent == eventId);
|
||||||
|
|
@ -1328,17 +1339,18 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
Future<void> onPhoneButtonTap() async {
|
Future<void> onPhoneButtonTap() async {
|
||||||
// VoIP required Android SDK 21
|
// VoIP required Android SDK 21
|
||||||
if (PlatformInfos.isAndroid) {
|
if (PlatformInfos.isAndroid) {
|
||||||
DeviceInfoPlugin().androidInfo.then((value) {
|
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
if (value.version.sdkInt < 21) {
|
if (!mounted) return;
|
||||||
Navigator.pop(context);
|
if (androidInfo.version.sdkInt < 21) {
|
||||||
showOkAlertDialog(
|
Navigator.pop(context);
|
||||||
context: context,
|
await showOkAlertDialog(
|
||||||
title: L10n.of(context).unsupportedAndroidVersion,
|
context: context,
|
||||||
message: L10n.of(context).unsupportedAndroidVersionLong,
|
title: L10n.of(context).unsupportedAndroidVersion,
|
||||||
okLabel: L10n.of(context).close,
|
message: L10n.of(context).unsupportedAndroidVersionLong,
|
||||||
);
|
okLabel: L10n.of(context).close,
|
||||||
}
|
);
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final callType = await showModalActionPopup<CallType>(
|
final callType = await showModalActionPopup<CallType>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -1359,11 +1371,13 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (callType == null) return;
|
if (callType == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
final voipPlugin = Matrix.of(context).voipPlugin;
|
final voipPlugin = Matrix.of(context).voipPlugin;
|
||||||
try {
|
try {
|
||||||
await voipPlugin!.voip.inviteToCall(room, callType);
|
await voipPlugin!.voip.inviteToCall(room, callType);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
context,
|
context,
|
||||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,7 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
||||||
});
|
});
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logs().v('Could not download audio file', e, s);
|
Logs().v('Could not download audio file', e, s);
|
||||||
|
if (!mounted) rethrow;
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
context,
|
context,
|
||||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||||
|
|
@ -208,6 +209,7 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
audioPlayer.play().onError(
|
audioPlayer.play().onError(
|
||||||
ErrorReporter(context, 'Unable to play audio message').onErrorCallback,
|
ErrorReporter(context, 'Unable to play audio message').onErrorCallback,
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ class _CuteContentState extends State<CuteContent> {
|
||||||
Future<void> addOverlay() async {
|
Future<void> addOverlay() async {
|
||||||
_isOverlayShown = true;
|
_isOverlayShown = true;
|
||||||
await Future.delayed(const Duration(milliseconds: 50));
|
await Future.delayed(const Duration(milliseconds: 50));
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
OverlayEntry? overlay;
|
OverlayEntry? overlay;
|
||||||
overlay = OverlayEntry(
|
overlay = OverlayEntry(
|
||||||
|
|
|
||||||
|
|
@ -61,9 +61,11 @@ class MessageContent extends StatelessWidget {
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
final state = await client.getCryptoIdentityState();
|
final state = await client.getCryptoIdentityState();
|
||||||
if (!state.connected) {
|
if (!state.connected) {
|
||||||
|
if (!context.mounted) return;
|
||||||
final success = await context.push('/backup');
|
final success = await context.push('/backup');
|
||||||
if (success != true) return;
|
if (success != true) return;
|
||||||
}
|
}
|
||||||
|
if (!context.mounted) return;
|
||||||
event.requestKey();
|
event.requestKey();
|
||||||
final sender = event.senderFromMemoryOrFallback;
|
final sender = event.senderFromMemoryOrFallback;
|
||||||
await showAdaptiveBottomSheet(
|
await showAdaptiveBottomSheet(
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class PinnedEvents extends StatelessWidget {
|
||||||
const PinnedEvents(this.controller, {super.key});
|
const PinnedEvents(this.controller, {super.key});
|
||||||
|
|
||||||
Future<void> _displayPinnedEventsDialog(BuildContext context) async {
|
Future<void> _displayPinnedEventsDialog(BuildContext context) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final eventsResult = await showFutureLoadingDialog(
|
final eventsResult = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Future.wait(
|
future: () => Future.wait(
|
||||||
|
|
@ -25,13 +26,14 @@ class PinnedEvents extends StatelessWidget {
|
||||||
);
|
);
|
||||||
final events = eventsResult.result;
|
final events = eventsResult.result;
|
||||||
if (events == null) return;
|
if (events == null) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
|
|
||||||
final eventId = events.length == 1
|
final eventId = events.length == 1
|
||||||
? events.single?.eventId
|
? events.single?.eventId
|
||||||
: await showModalActionPopup<String>(
|
: await showModalActionPopup<String>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).pin,
|
title: l10n.pin,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
actions: events
|
actions: events
|
||||||
.map(
|
.map(
|
||||||
(event) => AdaptiveModalAction(
|
(event) => AdaptiveModalAction(
|
||||||
|
|
@ -39,7 +41,7 @@ class PinnedEvents extends StatelessWidget {
|
||||||
icon: const Icon(Icons.push_pin_outlined),
|
icon: const Icon(Icons.push_pin_outlined),
|
||||||
label:
|
label:
|
||||||
event?.calcLocalizedBodyFallback(
|
event?.calcLocalizedBodyFallback(
|
||||||
MatrixLocals(L10n.of(context)),
|
MatrixLocals(l10n),
|
||||||
withSenderNamePrefix: true,
|
withSenderNamePrefix: true,
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
) ??
|
) ??
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ class RecordingViewModelState extends State<RecordingViewModel> {
|
||||||
room.client.getConfig(); // Preload server file configuration.
|
room.client.getConfig(); // Preload server file configuration.
|
||||||
if (PlatformInfos.isAndroid) {
|
if (PlatformInfos.isAndroid) {
|
||||||
final info = await DeviceInfoPlugin().androidInfo;
|
final info = await DeviceInfoPlugin().androidInfo;
|
||||||
|
if (!mounted) return;
|
||||||
if (info.version.sdkInt < 19) {
|
if (info.version.sdkInt < 19) {
|
||||||
showOkAlertDialog(
|
showOkAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -76,6 +77,7 @@ class RecordingViewModelState extends State<RecordingViewModel> {
|
||||||
|
|
||||||
final result = await audioRecorder.hasPermission();
|
final result = await audioRecorder.hasPermission();
|
||||||
if (result != true) {
|
if (result != true) {
|
||||||
|
if (!mounted) return;
|
||||||
showOkAlertDialog(
|
showOkAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).oopsSomethingWentWrong,
|
title: L10n.of(context).oopsSomethingWentWrong,
|
||||||
|
|
@ -97,10 +99,12 @@ class RecordingViewModelState extends State<RecordingViewModel> {
|
||||||
),
|
),
|
||||||
path: path ?? '',
|
path: path ?? '',
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
setState(() => duration = Duration.zero);
|
setState(() => duration = Duration.zero);
|
||||||
_subscribe();
|
_subscribe();
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logs().w('Unable to start voice message recording', e, s);
|
Logs().w('Unable to start voice message recording', e, s);
|
||||||
|
if (!mounted) return;
|
||||||
showOkAlertDialog(
|
showOkAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).oopsSomethingWentWrong,
|
title: L10n.of(context).oopsSomethingWentWrong,
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ class SendFileDialogState extends State<SendFileDialog> {
|
||||||
scaffoldMessenger.clearSnackBars();
|
scaffoldMessenger.clearSnackBars();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
scaffoldMessenger.clearSnackBars();
|
scaffoldMessenger.clearSnackBars();
|
||||||
|
if (!mounted || !widget.outerContext.mounted) rethrow;
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
scaffoldMessenger.showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ class SendLocationDialogState extends State<SendLocationDialog> {
|
||||||
context: context,
|
context: context,
|
||||||
future: () => widget.room.sendLocation(body, uri),
|
future: () => widget.room.sendLocation(body, uri),
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
Navigator.of(context, rootNavigator: false).pop();
|
Navigator.of(context, rootNavigator: false).pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ class _StartPollBottomSheetState extends State<StartPollBottomSheet> {
|
||||||
maxSelections: _allowMultipleAnswers ? _answers.length : 1,
|
maxSelections: _allowMultipleAnswers ? _answers.length : 1,
|
||||||
txid: _txid,
|
txid: _txid,
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logs().w('Unable to create poll', e, s);
|
Logs().w('Unable to create poll', e, s);
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,7 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateRoomAction() async {
|
Future<void> updateRoomAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final roomVersion = room
|
final roomVersion = room
|
||||||
.getState(EventTypes.RoomCreate)!
|
.getState(EventTypes.RoomCreate)!
|
||||||
.content
|
.content
|
||||||
|
|
@ -170,10 +171,11 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
||||||
);
|
);
|
||||||
final capabilities = capabilitiesResult.result;
|
final capabilities = capabilitiesResult.result;
|
||||||
if (capabilities == null) return;
|
if (capabilities == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final newVersion = await showModalActionPopup<String>(
|
final newVersion = await showModalActionPopup<String>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).replaceRoomWithNewerVersion,
|
title: l10n.replaceRoomWithNewerVersion,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
actions: capabilities.mRoomVersions!.available.entries
|
actions: capabilities.mRoomVersions!.available.entries
|
||||||
.where((r) => r.key != roomVersion)
|
.where((r) => r.key != roomVersion)
|
||||||
.map(
|
.map(
|
||||||
|
|
@ -185,18 +187,20 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
if (newVersion == null ||
|
if (newVersion == null) return;
|
||||||
OkCancelResult.cancel ==
|
if (!mounted) return;
|
||||||
await showOkCancelAlertDialog(
|
final confirmUpgrade = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: l10n.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
title: L10n.of(context).areYouSure,
|
title: l10n.areYouSure,
|
||||||
message: L10n.of(context).roomUpgradeDescription,
|
message: l10n.roomUpgradeDescription,
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
)) {
|
);
|
||||||
|
if (confirmUpgrade == OkCancelResult.cancel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
futureWithProgress: (onProgress) async {
|
futureWithProgress: (onProgress) async {
|
||||||
|
|
@ -243,6 +247,7 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addAlias() async {
|
Future<void> addAlias() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final domain = room.client.userID?.domain;
|
final domain = room.client.userID?.domain;
|
||||||
if (domain == null) {
|
if (domain == null) {
|
||||||
throw Exception('userID or domain is null! This should never happen.');
|
throw Exception('userID or domain is null! This should never happen.');
|
||||||
|
|
@ -250,11 +255,12 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
||||||
|
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).editRoomAliases,
|
title: l10n.editRoomAliases,
|
||||||
prefixText: '#',
|
prefixText: '#',
|
||||||
suffixText: domain,
|
suffixText: domain,
|
||||||
hintText: L10n.of(context).alias,
|
hintText: l10n.alias,
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
final aliasLocalpart = input?.trim();
|
final aliasLocalpart = input?.trim();
|
||||||
if (aliasLocalpart == null || aliasLocalpart.isEmpty) return;
|
if (aliasLocalpart == null || aliasLocalpart.isEmpty) return;
|
||||||
final alias = '#$aliasLocalpart:$domain';
|
final alias = '#$aliasLocalpart:$domain';
|
||||||
|
|
@ -264,17 +270,19 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
||||||
future: () => room.client.setRoomAlias(alias, room.id),
|
future: () => room.client.setRoomAlias(alias, room.id),
|
||||||
);
|
);
|
||||||
if (result.error != null) return;
|
if (result.error != null) return;
|
||||||
|
if (!mounted) return;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
||||||
if (!room.canChangeStateEvent(EventTypes.RoomCanonicalAlias)) return;
|
if (!room.canChangeStateEvent(EventTypes.RoomCanonicalAlias)) return;
|
||||||
|
|
||||||
final canonicalAliasConsent = await showOkCancelAlertDialog(
|
final canonicalAliasConsent = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).setAsCanonicalAlias,
|
title: l10n.setAsCanonicalAlias,
|
||||||
message: alias,
|
message: alias,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: l10n.yes,
|
||||||
cancelLabel: L10n.of(context).no,
|
cancelLabel: l10n.no,
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
final altAliases =
|
final altAliases =
|
||||||
room
|
room
|
||||||
|
|
|
||||||
|
|
@ -37,69 +37,78 @@ class ChatDetailsController extends State<ChatDetails> {
|
||||||
String? get roomId => widget.roomId;
|
String? get roomId => widget.roomId;
|
||||||
|
|
||||||
Future<void> setDisplaynameAction() async {
|
Future<void> setDisplaynameAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).changeTheNameOfTheGroup,
|
title: l10n.changeTheNameOfTheGroup,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
initialText: room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
initialText: room.getLocalizedDisplayname(MatrixLocals(l10n)),
|
||||||
);
|
);
|
||||||
if (input == null) return;
|
if (input == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.setName(input),
|
future: () => room.setName(input),
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).displaynameHasBeenChanged)),
|
SnackBar(content: Text(l10n.displaynameHasBeenChanged)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setTopicAction() async {
|
Future<void> setTopicAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).setChatDescription,
|
title: l10n.setChatDescription,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
hintText: L10n.of(context).noChatDescriptionYet,
|
hintText: l10n.noChatDescriptionYet,
|
||||||
initialText: room.topic,
|
initialText: room.topic,
|
||||||
minLines: 4,
|
minLines: 4,
|
||||||
maxLines: 8,
|
maxLines: 8,
|
||||||
);
|
);
|
||||||
if (input == null) return;
|
if (input == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.setDescription(input),
|
future: () => room.setDescription(input),
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).chatDescriptionHasBeenChanged)),
|
SnackBar(content: Text(l10n.chatDescriptionHasBeenChanged)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setAvatarAction() async {
|
Future<void> setAvatarAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId!);
|
final room = Matrix.of(context).client.getRoomById(roomId!);
|
||||||
final actions = [
|
final actions = [
|
||||||
if (PlatformInfos.isMobile)
|
if (PlatformInfos.isMobile)
|
||||||
AdaptiveModalAction(
|
AdaptiveModalAction(
|
||||||
value: AvatarAction.camera,
|
value: AvatarAction.camera,
|
||||||
label: L10n.of(context).openCamera,
|
label: l10n.openCamera,
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
icon: const Icon(Icons.camera_alt_outlined),
|
icon: const Icon(Icons.camera_alt_outlined),
|
||||||
),
|
),
|
||||||
AdaptiveModalAction(
|
AdaptiveModalAction(
|
||||||
value: AvatarAction.file,
|
value: AvatarAction.file,
|
||||||
label: L10n.of(context).openGallery,
|
label: l10n.openGallery,
|
||||||
icon: const Icon(Icons.photo_outlined),
|
icon: const Icon(Icons.photo_outlined),
|
||||||
),
|
),
|
||||||
if (room?.avatar != null)
|
if (room?.avatar != null)
|
||||||
AdaptiveModalAction(
|
AdaptiveModalAction(
|
||||||
value: AvatarAction.remove,
|
value: AvatarAction.remove,
|
||||||
label: L10n.of(context).delete,
|
label: l10n.delete,
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
icon: const Icon(Icons.delete_outlined),
|
icon: const Icon(Icons.delete_outlined),
|
||||||
),
|
),
|
||||||
|
|
@ -108,11 +117,12 @@ class ChatDetailsController extends State<ChatDetails> {
|
||||||
? actions.single.value
|
? actions.single.value
|
||||||
: await showModalActionPopup<AvatarAction>(
|
: await showModalActionPopup<AvatarAction>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).editRoomAvatar,
|
title: l10n.editRoomAvatar,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
if (action == null) return;
|
if (action == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
if (action == AvatarAction.remove) {
|
if (action == AvatarAction.remove) {
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -131,6 +141,7 @@ class ChatDetailsController extends State<ChatDetails> {
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
file = MatrixFile(bytes: await result.readAsBytes(), name: result.path);
|
file = MatrixFile(bytes: await result.readAsBytes(), name: result.path);
|
||||||
} else {
|
} else {
|
||||||
|
if (!mounted) return;
|
||||||
final picked = await selectFiles(
|
final picked = await selectFiles(
|
||||||
context,
|
context,
|
||||||
allowMultiple: false,
|
allowMultiple: false,
|
||||||
|
|
@ -143,6 +154,7 @@ class ChatDetailsController extends State<ChatDetails> {
|
||||||
name: pickedFile.name,
|
name: pickedFile.name,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room!.setAvatar(file),
|
future: () => room!.setAvatar(file),
|
||||||
|
|
|
||||||
|
|
@ -30,38 +30,40 @@ class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> enableEncryption(_) async {
|
Future<void> enableEncryption(_) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
if (room.encrypted) {
|
if (room.encrypted) {
|
||||||
showOkAlertDialog(
|
showOkAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).sorryThatsNotPossible,
|
title: l10n.sorryThatsNotPossible,
|
||||||
message: L10n.of(context).disableEncryptionWarning,
|
message: l10n.disableEncryptionWarning,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (room.joinRules == JoinRules.public) {
|
if (room.joinRules == JoinRules.public) {
|
||||||
showOkAlertDialog(
|
showOkAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).sorryThatsNotPossible,
|
title: l10n.sorryThatsNotPossible,
|
||||||
message: L10n.of(context).noEncryptionForPublicRooms,
|
message: l10n.noEncryptionForPublicRooms,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!room.canChangeStateEvent(EventTypes.Encryption)) {
|
if (!room.canChangeStateEvent(EventTypes.Encryption)) {
|
||||||
showOkAlertDialog(
|
showOkAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).sorryThatsNotPossible,
|
title: l10n.sorryThatsNotPossible,
|
||||||
message: L10n.of(context).noPermission,
|
message: l10n.noPermission,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final consent = await showOkCancelAlertDialog(
|
final consent = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: l10n.areYouSure,
|
||||||
message: L10n.of(context).enableEncryptionWarning,
|
message: l10n.enableEncryptionWarning,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: l10n.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
);
|
);
|
||||||
if (consent != OkCancelResult.ok) return;
|
if (consent != OkCancelResult.ok) return;
|
||||||
|
if (!mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.enableEncryption(),
|
future: () => room.enableEncryption(),
|
||||||
|
|
@ -69,14 +71,16 @@ class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startVerification() async {
|
Future<void> startVerification() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final consent = await showOkCancelAlertDialog(
|
final consent = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).verifyOtherUser,
|
title: l10n.verifyOtherUser,
|
||||||
message: L10n.of(context).verifyOtherUserDescription,
|
message: l10n.verifyOtherUserDescription,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
);
|
);
|
||||||
if (consent != OkCancelResult.ok) return;
|
if (consent != OkCancelResult.ok) return;
|
||||||
|
if (!mounted) return;
|
||||||
final req = await room.client.userDeviceKeys[room.directChatMatrixID]!
|
final req = await room.client.userDeviceKeys[room.directChatMatrixID]!
|
||||||
.startVerification();
|
.startVerification();
|
||||||
req.onUpdate = () {
|
req.onUpdate = () {
|
||||||
|
|
@ -84,6 +88,7 @@ class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (!mounted) return;
|
||||||
await KeyVerificationDialog(request: req).show(context);
|
await KeyVerificationDialog(request: req).show(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,8 @@ class ChatListController extends State<ChatList>
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> onChatTap(Room room) async {
|
Future<void> onChatTap(Room room) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
if (room.membership == Membership.invite) {
|
if (room.membership == Membership.invite) {
|
||||||
final joinResult = await showFutureLoadingDialog(
|
final joinResult = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -105,10 +107,11 @@ class ChatListController extends State<ChatList>
|
||||||
);
|
);
|
||||||
if (joinResult.error != null) return;
|
if (joinResult.error != null) return;
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
if (room.membership == Membership.ban) {
|
if (room.membership == Membership.ban) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).youHaveBeenBannedFromThisChat)),
|
SnackBar(content: Text(l10n.youHaveBeenBannedFromThisChat)),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -156,23 +159,25 @@ class ChatListController extends State<ChatList>
|
||||||
static const String _serverStoreNamespace = 'im.fluffychat.search.server';
|
static const String _serverStoreNamespace = 'im.fluffychat.search.server';
|
||||||
|
|
||||||
Future<void> setServer() async {
|
Future<void> setServer() async {
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final newServer = await showTextInputDialog(
|
final newServer = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
title: L10n.of(context).changeTheHomeserver,
|
title: l10n.changeTheHomeserver,
|
||||||
context: context,
|
context: context,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
prefixText: 'https://',
|
prefixText: 'https://',
|
||||||
hintText: Matrix.of(context).client.homeserver?.host,
|
hintText: matrix.client.homeserver?.host,
|
||||||
initialText: searchServer,
|
initialText: searchServer,
|
||||||
keyboardType: TextInputType.url,
|
keyboardType: TextInputType.url,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
validator: (server) => server.contains('.') == true
|
validator: (server) =>
|
||||||
? null
|
server.contains('.') == true ? null : l10n.invalidServerName,
|
||||||
: L10n.of(context).invalidServerName,
|
|
||||||
);
|
);
|
||||||
if (newServer == null) return;
|
if (newServer == null) return;
|
||||||
Matrix.of(context).store.setString(_serverStoreNamespace, newServer);
|
if (!mounted) return;
|
||||||
|
matrix.store.setString(_serverStoreNamespace, newServer);
|
||||||
setState(() {
|
setState(() {
|
||||||
searchServer = newServer;
|
searchServer = newServer;
|
||||||
});
|
});
|
||||||
|
|
@ -185,6 +190,7 @@ class ChatListController extends State<ChatList>
|
||||||
|
|
||||||
Future<void> _search() async {
|
Future<void> _search() async {
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
if (!isSearching) {
|
if (!isSearching) {
|
||||||
setState(() {
|
setState(() {
|
||||||
isSearching = true;
|
isSearching = true;
|
||||||
|
|
@ -227,9 +233,10 @@ class ChatListController extends State<ChatList>
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logs().w('Searching has crashed', e, s);
|
Logs().w('Searching has crashed', e, s);
|
||||||
ScaffoldMessenger.of(
|
if (!mounted) return;
|
||||||
context,
|
scaffoldMessenger.showSnackBar(
|
||||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
SnackBar(content: Text(e.toLocalizedString(context))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!isSearchMode) return;
|
if (!isSearchMode) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -293,9 +300,8 @@ class ChatListController extends State<ChatList>
|
||||||
|
|
||||||
Future<void> editSpace(BuildContext context, String spaceId) async {
|
Future<void> editSpace(BuildContext context, String spaceId) async {
|
||||||
await Matrix.of(context).client.getRoomById(spaceId)!.postLoad();
|
await Matrix.of(context).client.getRoomById(spaceId)!.postLoad();
|
||||||
if (mounted) {
|
if (!context.mounted) return;
|
||||||
context.push('/rooms/$spaceId/details');
|
context.push('/rooms/$spaceId/details');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needs to match GroupsSpacesEntry for 'separate group' checking.
|
// Needs to match GroupsSpacesEntry for 'separate group' checking.
|
||||||
|
|
@ -742,6 +748,7 @@ class ChatListController extends State<ChatList>
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
if (space == null) return;
|
if (space == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => space.setSpaceChild(room.id),
|
future: () => space.setSpaceChild(room.id),
|
||||||
|
|
@ -767,16 +774,18 @@ class ChatListController extends State<ChatList>
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setStatus() async {
|
Future<void> setStatus() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
final currentPresence = await client.fetchCurrentPresence(client.userID!);
|
final currentPresence = await client.fetchCurrentPresence(client.userID!);
|
||||||
|
if (!mounted) return;
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).setStatus,
|
title: l10n.setStatus,
|
||||||
message: L10n.of(context).leaveEmptyToClearStatus,
|
message: l10n.leaveEmptyToClearStatus,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
hintText: L10n.of(context).statusExampleMessage,
|
hintText: l10n.statusExampleMessage,
|
||||||
maxLines: 6,
|
maxLines: 6,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
maxLength: 255,
|
maxLength: 255,
|
||||||
|
|
@ -904,18 +913,21 @@ class ChatListController extends State<ChatList>
|
||||||
if (action == null) return;
|
if (action == null) return;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case EditBundleAction.addToBundle:
|
case EditBundleAction.addToBundle:
|
||||||
|
if (!mounted) return;
|
||||||
final bundle = await showTextInputDialog(
|
final bundle = await showTextInputDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: l10n.bundleName,
|
title: l10n.bundleName,
|
||||||
hintText: l10n.bundleName,
|
hintText: l10n.bundleName,
|
||||||
);
|
);
|
||||||
if (bundle == null || bundle.isEmpty || bundle.isEmpty) return;
|
if (bundle == null || bundle.isEmpty || bundle.isEmpty) return;
|
||||||
|
if (!mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => client.setAccountBundle(bundle),
|
future: () => client.setAccountBundle(bundle),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case EditBundleAction.removeFromBundle:
|
case EditBundleAction.removeFromBundle:
|
||||||
|
if (!mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => client.removeFromAccountBundle(activeBundle!),
|
future: () => client.removeFromAccountBundle(activeBundle!),
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@ class ClientChooserButton extends StatelessWidget {
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context).cancel,
|
||||||
);
|
);
|
||||||
if (consent != OkCancelResult.ok) return;
|
if (consent != OkCancelResult.ok) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
context.go('/rooms/settings/addaccount');
|
context.go('/rooms/settings/addaccount');
|
||||||
break;
|
break;
|
||||||
case SettingsAction.newGroup:
|
case SettingsAction.newGroup:
|
||||||
|
|
|
||||||
|
|
@ -170,14 +170,17 @@ class _SpaceViewState extends State<SpaceView> {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case SpaceActions.settings:
|
case SpaceActions.settings:
|
||||||
await space?.postLoad();
|
await space?.postLoad();
|
||||||
|
if (!mounted) return;
|
||||||
context.push('/rooms/${widget.spaceId}/details');
|
context.push('/rooms/${widget.spaceId}/details');
|
||||||
break;
|
break;
|
||||||
case SpaceActions.invite:
|
case SpaceActions.invite:
|
||||||
await space?.postLoad();
|
await space?.postLoad();
|
||||||
|
if (!mounted) return;
|
||||||
context.push('/rooms/${widget.spaceId}/invite');
|
context.push('/rooms/${widget.spaceId}/invite');
|
||||||
break;
|
break;
|
||||||
case SpaceActions.members:
|
case SpaceActions.members:
|
||||||
await space?.postLoad();
|
await space?.postLoad();
|
||||||
|
if (!mounted) return;
|
||||||
context.push('/rooms/${widget.spaceId}/details/members');
|
context.push('/rooms/${widget.spaceId}/details/members');
|
||||||
break;
|
break;
|
||||||
case SpaceActions.leave:
|
case SpaceActions.leave:
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
||||||
currentLevel: currentLevel,
|
currentLevel: currentLevel,
|
||||||
);
|
);
|
||||||
if (newLevel == null) return;
|
if (newLevel == null) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
final content = Map<String, dynamic>.from(
|
final content = Map<String, dynamic>.from(
|
||||||
room.getState(EventTypes.RoomPowerLevels)!.content,
|
room.getState(EventTypes.RoomPowerLevels)!.content,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,15 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
||||||
Future<void> _checkChatBackup() async {
|
Future<void> _checkChatBackup() async {
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
final state = await client.getCryptoIdentityState();
|
final state = await client.getCryptoIdentityState();
|
||||||
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
chatBackupEnabled = state.initialized && !state.connected;
|
chatBackupEnabled = state.initialized && !state.connected;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeDevicesAction(List<Device> devices) async {
|
Future<void> removeDevicesAction(List<Device> devices) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
|
|
||||||
final wellKnown = await Result.capture(client.getWellknown());
|
final wellKnown = await Result.capture(client.getWellknown());
|
||||||
|
|
@ -57,18 +60,19 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
||||||
launchUrlString(accountManageUrl, mode: LaunchMode.inAppBrowserView);
|
launchUrlString(accountManageUrl, mode: LaunchMode.inAppBrowserView);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: l10n.areYouSure,
|
||||||
okLabel: L10n.of(context).remove,
|
okLabel: l10n.remove,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
message: L10n.of(context).removeDevicesDescription,
|
message: l10n.removeDevicesDescription,
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.cancel) {
|
OkCancelResult.cancel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final matrix = Matrix.of(context);
|
if (!mounted) return;
|
||||||
final deviceIds = <String>[];
|
final deviceIds = <String>[];
|
||||||
for (final userDevice in devices) {
|
for (final userDevice in devices) {
|
||||||
deviceIds.add(userDevice.deviceId);
|
deviceIds.add(userDevice.deviceId);
|
||||||
|
|
@ -85,19 +89,21 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> renameDeviceAction(Device device) async {
|
Future<void> renameDeviceAction(Device device) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
final displayName = await showTextInputDialog(
|
final displayName = await showTextInputDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).changeDeviceName,
|
title: l10n.changeDeviceName,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
hintText: device.displayName,
|
hintText: device.displayName,
|
||||||
);
|
);
|
||||||
if (displayName == null) return;
|
if (displayName == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(
|
future: () =>
|
||||||
context,
|
matrix.client.updateDevice(device.deviceId, displayName: displayName),
|
||||||
).client.updateDevice(device.deviceId, displayName: displayName),
|
|
||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
reload();
|
reload();
|
||||||
|
|
@ -105,17 +111,20 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> verifyDeviceAction(Device device) async {
|
Future<void> verifyDeviceAction(Device device) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
final consent = await showOkCancelAlertDialog(
|
final consent = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).verifyOtherDevice,
|
title: l10n.verifyOtherDevice,
|
||||||
message: L10n.of(context).verifyOtherDeviceDescription,
|
message: l10n.verifyOtherDeviceDescription,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
);
|
);
|
||||||
if (consent != OkCancelResult.ok) return;
|
if (consent != OkCancelResult.ok) return;
|
||||||
final req = await Matrix.of(context)
|
if (!mounted) return;
|
||||||
|
final req = await matrix
|
||||||
.client
|
.client
|
||||||
.userDeviceKeys[Matrix.of(context).client.userID!]!
|
.userDeviceKeys[matrix.client.userID!]!
|
||||||
.deviceKeys[device.deviceId]!
|
.deviceKeys[device.deviceId]!
|
||||||
.startVerification();
|
.startVerification();
|
||||||
req.onUpdate = () {
|
req.onUpdate = () {
|
||||||
|
|
@ -126,6 +135,7 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (!mounted) return;
|
||||||
await KeyVerificationDialog(request: req).show(context);
|
await KeyVerificationDialog(request: req).show(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,10 +92,12 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} on IOException catch (e) {
|
} on IOException catch (e) {
|
||||||
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
context,
|
context,
|
||||||
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
if (!mounted) return;
|
||||||
ErrorReporter(context, 'Unable to play video').onErrorCallback(e, s);
|
ErrorReporter(context, 'Unable to play video').onErrorCallback(e, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import 'package:fluffychat/widgets/matrix.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
Future<void> restoreBackupFlow(BuildContext context) async {
|
Future<void> restoreBackupFlow(BuildContext context) async {
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
final picked = await selectFiles(context);
|
final picked = await selectFiles(context);
|
||||||
final file = picked.firstOrNull;
|
final file = picked.firstOrNull;
|
||||||
if (file == null) return;
|
if (file == null) return;
|
||||||
|
|
@ -12,9 +13,9 @@ Future<void> restoreBackupFlow(BuildContext context) async {
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () async {
|
future: () async {
|
||||||
final client = await Matrix.of(context).getLoginClient();
|
final client = await matrix.getLoginClient();
|
||||||
await client.importDump(String.fromCharCodes(await file.readAsBytes()));
|
await client.importDump(String.fromCharCodes(await file.readAsBytes()));
|
||||||
Matrix.of(context).initMatrix();
|
matrix.initMatrix();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,7 @@ class IntroPage extends StatelessWidget {
|
||||||
final client = await Matrix.of(
|
final client = await Matrix.of(
|
||||||
context,
|
context,
|
||||||
).getLoginClient();
|
).getLoginClient();
|
||||||
|
if (!context.mounted) return;
|
||||||
context.go(
|
context.go(
|
||||||
'${GoRouterState.of(context).uri.path}/login',
|
'${GoRouterState.of(context).uri.path}/login',
|
||||||
extra: client,
|
extra: client,
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,8 @@ class _IntroPagePresenterState extends State<IntroPagePresenter> {
|
||||||
final client = await Matrix.of(context).getLoginClient();
|
final client = await Matrix.of(context).getLoginClient();
|
||||||
await client.checkHomeserver(homeserverUrl);
|
await client.checkHomeserver(homeserverUrl);
|
||||||
await client.oidcLogin(session: session, code: code, state: state);
|
await client.oidcLogin(session: session, code: code, state: state);
|
||||||
if (context.mounted) context.go('/backup');
|
if (!mounted) return;
|
||||||
|
context.go('/backup');
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logs().w('Unable to login via OIDC', e, s);
|
Logs().w('Unable to login via OIDC', e, s);
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
||||||
String id,
|
String id,
|
||||||
String displayname,
|
String displayname,
|
||||||
) async {
|
) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||||
|
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
|
|
@ -61,10 +63,9 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
||||||
future: () => room.invite(id),
|
future: () => room.invite(id),
|
||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
if (!context.mounted) return;
|
||||||
SnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
content: Text(L10n.of(context).contactHasBeenInvitedToTheGroup),
|
SnackBar(content: Text(l10n.contactHasBeenInvitedToTheGroup)),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,6 +92,7 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
||||||
try {
|
try {
|
||||||
response = await matrix.client.searchUserDirectory(text, limit: 10);
|
response = await matrix.client.searchUserDirectory(text, limit: 10);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (!context.mounted) return;
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
context,
|
context,
|
||||||
).showSnackBar(SnackBar(content: Text((e).toLocalizedString(context))));
|
).showSnackBar(SnackBar(content: Text((e).toLocalizedString(context))));
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (valid.error != null) {
|
if (valid.error != null) {
|
||||||
|
if (!mounted) return;
|
||||||
await showOkAlertDialog(
|
await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -178,9 +179,10 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
||||||
);
|
);
|
||||||
buttons.add(
|
buttons.add(
|
||||||
AdaptiveDialogAction(
|
AdaptiveDialogAction(
|
||||||
onPressed: () => widget.request.rejectVerification().then(
|
onPressed: () => widget.request.rejectVerification().then((_) {
|
||||||
(_) => Navigator.of(context, rootNavigator: false).pop(false),
|
if (!context.mounted) return;
|
||||||
),
|
Navigator.of(context, rootNavigator: false).pop(false);
|
||||||
|
}),
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).reject,
|
L10n.of(context).reject,
|
||||||
style: TextStyle(color: theme.colorScheme.error),
|
style: TextStyle(color: theme.colorScheme.error),
|
||||||
|
|
|
||||||
|
|
@ -130,15 +130,19 @@ class LoginController extends State<Login> {
|
||||||
Logs().v(
|
Logs().v(
|
||||||
'$newDomain is not running a homeserver, asking to use $oldHomeserver',
|
'$newDomain is not running a homeserver, asking to use $oldHomeserver',
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final dialogResult = await showOkCancelAlertDialog(
|
final dialogResult = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
title: L10n.of(
|
title: l10n.noMatrixServer(
|
||||||
context,
|
newDomain.toString(),
|
||||||
).noMatrixServer(newDomain.toString(), oldHomeserver.toString()),
|
oldHomeserver.toString(),
|
||||||
okLabel: L10n.of(context).ok,
|
),
|
||||||
cancelLabel: L10n.of(context).cancel,
|
okLabel: l10n.ok,
|
||||||
|
cancelLabel: l10n.cancel,
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
if (dialogResult == OkCancelResult.ok) {
|
if (dialogResult == OkCancelResult.ok) {
|
||||||
if (mounted) setState(() => usernameError = null);
|
if (mounted) setState(() => usernameError = null);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -156,26 +160,30 @@ class LoginController extends State<Login> {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
widget.client.homeserver = oldHomeserver;
|
widget.client.homeserver = oldHomeserver;
|
||||||
|
if (!mounted) return;
|
||||||
usernameError = e.toLocalizedString(context);
|
usernameError = e.toLocalizedString(context);
|
||||||
if (mounted) setState(() {});
|
if (mounted) setState(() {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> passwordForgotten() async {
|
Future<void> passwordForgotten() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).passwordForgotten,
|
title: l10n.passwordForgotten,
|
||||||
message: L10n.of(context).enterAnEmailAddress,
|
message: l10n.enterAnEmailAddress,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
initialText: usernameController.text.isEmail
|
initialText: usernameController.text.isEmail
|
||||||
? usernameController.text
|
? usernameController.text
|
||||||
: '',
|
: '',
|
||||||
hintText: L10n.of(context).enterAnEmailAddress,
|
hintText: l10n.enterAnEmailAddress,
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
);
|
);
|
||||||
if (input == null) return;
|
if (input == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final clientSecret = DateTime.now().millisecondsSinceEpoch.toString();
|
final clientSecret = DateTime.now().millisecondsSinceEpoch.toString();
|
||||||
final response = await showFutureLoadingDialog(
|
final response = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -186,27 +194,30 @@ class LoginController extends State<Login> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (response.error != null) return;
|
if (response.error != null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final password = await showTextInputDialog(
|
final password = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).passwordForgotten,
|
title: l10n.passwordForgotten,
|
||||||
message: L10n.of(context).chooseAStrongPassword,
|
message: l10n.chooseAStrongPassword,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
hintText: '******',
|
hintText: '******',
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
);
|
);
|
||||||
if (password == null) return;
|
if (password == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final ok = await showOkAlertDialog(
|
final ok = await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).weSentYouAnEmail,
|
title: l10n.weSentYouAnEmail,
|
||||||
message: L10n.of(context).pleaseClickOnLink,
|
message: l10n.pleaseClickOnLink,
|
||||||
okLabel: L10n.of(context).iHaveClickedOnLink,
|
okLabel: l10n.iHaveClickedOnLink,
|
||||||
);
|
);
|
||||||
if (ok != OkCancelResult.ok) return;
|
if (ok != OkCancelResult.ok) return;
|
||||||
|
if (!mounted) return;
|
||||||
final data = <String, dynamic>{
|
final data = <String, dynamic>{
|
||||||
'new_password': password,
|
'new_password': password,
|
||||||
'logout_devices': false,
|
'logout_devices': false,
|
||||||
|
|
@ -226,9 +237,10 @@ class LoginController extends State<Login> {
|
||||||
data: data,
|
data: data,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).passwordHasBeenChanged)),
|
SnackBar(content: Text(l10n.passwordHasBeenChanged)),
|
||||||
);
|
);
|
||||||
usernameController.text = input;
|
usernameController.text = input;
|
||||||
passwordController.text = password;
|
passwordController.text = password;
|
||||||
|
|
|
||||||
|
|
@ -81,17 +81,19 @@ class NewPrivateChatController extends State<NewPrivateChat> {
|
||||||
void inviteAction() => FluffyShare.shareInviteLink(context);
|
void inviteAction() => FluffyShare.shareInviteLink(context);
|
||||||
|
|
||||||
Future<void> openScannerAction() async {
|
Future<void> openScannerAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
if (PlatformInfos.isAndroid) {
|
if (PlatformInfos.isAndroid) {
|
||||||
final info = await DeviceInfoPlugin().androidInfo;
|
final info = await DeviceInfoPlugin().androidInfo;
|
||||||
|
if (!mounted) return;
|
||||||
if (info.version.sdkInt < 21) {
|
if (info.version.sdkInt < 21) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(
|
SnackBar(content: Text(l10n.unsupportedAndroidVersionLong)),
|
||||||
content: Text(L10n.of(context).unsupportedAndroidVersionLong),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
await showAdaptiveBottomSheet(
|
await showAdaptiveBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => QrScannerModal(
|
builder: (_) => QrScannerModal(
|
||||||
|
|
@ -101,12 +103,15 @@ class NewPrivateChatController extends State<NewPrivateChat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> copyUserId() async {
|
Future<void> copyUserId() async {
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
|
final l10n = L10n.of(context);
|
||||||
await Clipboard.setData(
|
await Clipboard.setData(
|
||||||
ClipboardData(text: Matrix.of(context).client.userID!),
|
ClipboardData(text: Matrix.of(context).client.userID!),
|
||||||
);
|
);
|
||||||
ScaffoldMessenger.of(
|
if (!mounted) return;
|
||||||
context,
|
scaffoldMessenger.showSnackBar(
|
||||||
).showSnackBar(SnackBar(content: Text(L10n.of(context).copiedToClipboard)));
|
SnackBar(content: Text(l10n.copiedToClipboard)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void openUserModal(Profile profile) =>
|
void openUserModal(Profile profile) =>
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ class QrScannerModalState extends State<QrScannerModal> {
|
||||||
late StreamSubscription sub;
|
late StreamSubscription sub;
|
||||||
sub = controller.scannedDataStream.listen((scanData) {
|
sub = controller.scannedDataStream.listen((scanData) {
|
||||||
sub.cancel();
|
sub.cancel();
|
||||||
|
if (!mounted) return;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
final data = scanData.code;
|
final data = scanData.code;
|
||||||
if (data != null) widget.onScan(data);
|
if (data != null) widget.onScan(data);
|
||||||
|
|
|
||||||
|
|
@ -35,18 +35,20 @@ class SettingsController extends State<Settings> {
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> setDisplaynameAction() async {
|
Future<void> setDisplaynameAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
final profile = await profileFuture;
|
final profile = await profileFuture;
|
||||||
|
if (!mounted) return;
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).editDisplayname,
|
title: l10n.editDisplayname,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
initialText:
|
initialText: profile?.displayName ?? matrix.client.userID!.localpart,
|
||||||
profile?.displayName ?? Matrix.of(context).client.userID!.localpart,
|
|
||||||
);
|
);
|
||||||
if (input == null) return;
|
if (input == null) return;
|
||||||
final matrix = Matrix.of(context);
|
if (!mounted) return;
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => matrix.client.setProfileField(
|
future: () => matrix.client.setProfileField(
|
||||||
|
|
@ -61,19 +63,21 @@ class SettingsController extends State<Settings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> logoutAction() async {
|
Future<void> logoutAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSureYouWantToLogout,
|
title: l10n.areYouSureYouWantToLogout,
|
||||||
message: L10n.of(context).noBackupWarning,
|
message: l10n.noBackupWarning,
|
||||||
isDestructive: cryptoIdentityConnected == false,
|
isDestructive: cryptoIdentityConnected == false,
|
||||||
okLabel: L10n.of(context).logout,
|
okLabel: l10n.logout,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.cancel) {
|
OkCancelResult.cancel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final matrix = Matrix.of(context);
|
if (!mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => matrix.client.logout(),
|
future: () => matrix.client.logout(),
|
||||||
|
|
@ -81,24 +85,27 @@ class SettingsController extends State<Settings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setAvatarAction() async {
|
Future<void> setAvatarAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
final profile = await profileFuture;
|
final profile = await profileFuture;
|
||||||
|
if (!mounted) return;
|
||||||
final actions = [
|
final actions = [
|
||||||
if (PlatformInfos.isMobile)
|
if (PlatformInfos.isMobile)
|
||||||
AdaptiveModalAction(
|
AdaptiveModalAction(
|
||||||
value: AvatarAction.camera,
|
value: AvatarAction.camera,
|
||||||
label: L10n.of(context).openCamera,
|
label: l10n.openCamera,
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
icon: const Icon(Icons.camera_alt_outlined),
|
icon: const Icon(Icons.camera_alt_outlined),
|
||||||
),
|
),
|
||||||
AdaptiveModalAction(
|
AdaptiveModalAction(
|
||||||
value: AvatarAction.file,
|
value: AvatarAction.file,
|
||||||
label: L10n.of(context).openGallery,
|
label: l10n.openGallery,
|
||||||
icon: const Icon(Icons.photo_outlined),
|
icon: const Icon(Icons.photo_outlined),
|
||||||
),
|
),
|
||||||
if (profile?.avatarUrl != null)
|
if (profile?.avatarUrl != null)
|
||||||
AdaptiveModalAction(
|
AdaptiveModalAction(
|
||||||
value: AvatarAction.remove,
|
value: AvatarAction.remove,
|
||||||
label: L10n.of(context).removeYourAvatar,
|
label: l10n.removeYourAvatar,
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
icon: const Icon(Icons.delete_outlined),
|
icon: const Icon(Icons.delete_outlined),
|
||||||
),
|
),
|
||||||
|
|
@ -107,12 +114,12 @@ class SettingsController extends State<Settings> {
|
||||||
? actions.single.value
|
? actions.single.value
|
||||||
: await showModalActionPopup<AvatarAction>(
|
: await showModalActionPopup<AvatarAction>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).changeYourAvatar,
|
title: l10n.changeYourAvatar,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
if (action == null) return;
|
if (action == null) return;
|
||||||
final matrix = Matrix.of(context);
|
if (!mounted) return;
|
||||||
if (action == AvatarAction.remove) {
|
if (action == AvatarAction.remove) {
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -134,6 +141,7 @@ class SettingsController extends State<Settings> {
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
file = MatrixFile(bytes: await result.readAsBytes(), name: result.path);
|
file = MatrixFile(bytes: await result.readAsBytes(), name: result.path);
|
||||||
} else {
|
} else {
|
||||||
|
if (!mounted) return;
|
||||||
final result = await selectFiles(context, type: FileType.image);
|
final result = await selectFiles(context, type: FileType.image);
|
||||||
final pickedFile = result.firstOrNull;
|
final pickedFile = result.firstOrNull;
|
||||||
if (pickedFile == null) return;
|
if (pickedFile == null) return;
|
||||||
|
|
@ -142,6 +150,7 @@ class SettingsController extends State<Settings> {
|
||||||
name: pickedFile.name,
|
name: pickedFile.name,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => matrix.client.setAvatar(file),
|
future: () => matrix.client.setAvatar(file),
|
||||||
|
|
@ -168,6 +177,7 @@ class SettingsController extends State<Settings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
final state = await client.getCryptoIdentityState();
|
final state = await client.getCryptoIdentityState();
|
||||||
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
cryptoIdentityConnected = state.initialized && state.connected;
|
cryptoIdentityConnected = state.initialized && state.connected;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,41 +19,48 @@ class Settings3Pid extends StatefulWidget {
|
||||||
|
|
||||||
class Settings3PidController extends State<Settings3Pid> {
|
class Settings3PidController extends State<Settings3Pid> {
|
||||||
Future<void> add3PidAction() async {
|
Future<void> add3PidAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).enterAnEmailAddress,
|
title: l10n.enterAnEmailAddress,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
hintText: L10n.of(context).enterAnEmailAddress,
|
hintText: l10n.enterAnEmailAddress,
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
);
|
);
|
||||||
if (input == null) return;
|
if (input == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final clientSecret = DateTime.now().millisecondsSinceEpoch.toString();
|
final clientSecret = DateTime.now().millisecondsSinceEpoch.toString();
|
||||||
final response = await showFutureLoadingDialog(
|
final response = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context).client.requestTokenToRegisterEmail(
|
future: () => matrix.client.requestTokenToRegisterEmail(
|
||||||
clientSecret,
|
clientSecret,
|
||||||
input,
|
input,
|
||||||
Settings3Pid.sendAttempt++,
|
Settings3Pid.sendAttempt++,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (response.error != null) return;
|
if (response.error != null) return;
|
||||||
|
if (!mounted) return;
|
||||||
final ok = await showOkAlertDialog(
|
final ok = await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).weSentYouAnEmail,
|
title: l10n.weSentYouAnEmail,
|
||||||
message: L10n.of(context).pleaseClickOnLink,
|
message: l10n.pleaseClickOnLink,
|
||||||
okLabel: L10n.of(context).iHaveClickedOnLink,
|
okLabel: l10n.iHaveClickedOnLink,
|
||||||
);
|
);
|
||||||
if (ok != OkCancelResult.ok) return;
|
if (ok != OkCancelResult.ok) return;
|
||||||
|
if (!mounted) return;
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
delay: false,
|
delay: false,
|
||||||
future: () => Matrix.of(context).client.uiaRequestBackground(
|
future: () => matrix.client.uiaRequestBackground(
|
||||||
(auth) => Matrix.of(
|
(auth) => matrix.client.add3PID(
|
||||||
context,
|
clientSecret,
|
||||||
).client.add3PID(clientSecret, response.result!.sid, auth: auth),
|
response.result!.sid,
|
||||||
|
auth: auth,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (success.error != null) return;
|
if (success.error != null) return;
|
||||||
|
|
@ -63,21 +70,25 @@ class Settings3PidController extends State<Settings3Pid> {
|
||||||
Future<List<ThirdPartyIdentifier>?>? request;
|
Future<List<ThirdPartyIdentifier>?>? request;
|
||||||
|
|
||||||
Future<void> delete3Pid(ThirdPartyIdentifier identifier) async {
|
Future<void> delete3Pid(ThirdPartyIdentifier identifier) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: l10n.areYouSure,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: l10n.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
) !=
|
) !=
|
||||||
OkCancelResult.ok) {
|
OkCancelResult.ok) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(
|
future: () => matrix.client.delete3pidFromAccount(
|
||||||
context,
|
identifier.address,
|
||||||
).client.delete3pidFromAccount(identifier.address, identifier.medium),
|
identifier.medium,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
if (success.error != null) return;
|
if (success.error != null) return;
|
||||||
setState(() => request = null);
|
setState(() => request = null);
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _addEmotePack() async {
|
Future<void> _addEmotePack() async {
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
setState(() {
|
setState(() {
|
||||||
_loading = true;
|
_loading = true;
|
||||||
_progress = 0;
|
_progress = 0;
|
||||||
|
|
@ -148,7 +149,7 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
||||||
} else {
|
} else {
|
||||||
mxcFile = thumbnail;
|
mxcFile = thumbnail;
|
||||||
}
|
}
|
||||||
final uri = await Matrix.of(context).client.uploadContent(
|
final uri = await matrix.client.uploadContent(
|
||||||
mxcFile.bytes,
|
mxcFile.bytes,
|
||||||
filename: mxcFile.name,
|
filename: mxcFile.name,
|
||||||
contentType: mxcFile.mimeType,
|
contentType: mxcFile.mimeType,
|
||||||
|
|
@ -178,6 +179,7 @@ class _ImportEmoteArchiveDialogState extends State<ImportEmoteArchiveDialog> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
await widget.controller.save(context);
|
await widget.controller.save(context);
|
||||||
_importMap.removeWhere(
|
_importMap.removeWhere(
|
||||||
(key, value) => successfulUploads.contains(key.name),
|
(key, value) => successfulUploads.contains(key.name),
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createStickers() async {
|
Future<void> createStickers() async {
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
final pickedFiles = await selectFiles(
|
final pickedFiles = await selectFiles(
|
||||||
context,
|
context,
|
||||||
type: FileType.image,
|
type: FileType.image,
|
||||||
|
|
@ -315,7 +316,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
||||||
nativeImplementations: ClientManager.nativeImplementations,
|
nativeImplementations: ClientManager.nativeImplementations,
|
||||||
) ??
|
) ??
|
||||||
file;
|
file;
|
||||||
final uri = await Matrix.of(context).client.uploadContent(
|
final uri = await matrix.client.uploadContent(
|
||||||
file.bytes,
|
file.bytes,
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
contentType: file.mimeType,
|
contentType: file.mimeType,
|
||||||
|
|
@ -361,6 +362,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
||||||
final buffer = InputMemoryStream(await result.single.readAsBytes());
|
final buffer = InputMemoryStream(await result.single.readAsBytes());
|
||||||
|
|
||||||
final archive = ZipDecoder().decodeStream(buffer);
|
final archive = ZipDecoder().decodeStream(buffer);
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -375,7 +377,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
||||||
Future<void> exportAsZip() async {
|
Future<void> exportAsZip() async {
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
|
|
||||||
await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog<MatrixFile>(
|
||||||
context: context,
|
context: context,
|
||||||
future: () async {
|
future: () async {
|
||||||
final pack = _getPack();
|
final pack = _getPack();
|
||||||
|
|
@ -397,11 +399,12 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
||||||
'${pack.pack.displayName ?? client.userID?.localpart ?? 'emotes'}.zip';
|
'${pack.pack.displayName ?? client.userID?.localpart ?? 'emotes'}.zip';
|
||||||
final output = ZipEncoder().encode(archive);
|
final output = ZipEncoder().encode(archive);
|
||||||
|
|
||||||
MatrixFile(
|
return MatrixFile(name: fileName, bytes: Uint8List.fromList(output));
|
||||||
name: fileName,
|
|
||||||
bytes: Uint8List.fromList(output),
|
|
||||||
).save(context);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
final file = result.result;
|
||||||
|
if (file == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
|
file.save(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ class SettingsNotificationsController extends State<SettingsNotifications> {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (delete != true) return;
|
if (delete != true) return;
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ class SettingsPasswordController extends State<SettingsPassword> {
|
||||||
bool loading = false;
|
bool loading = false;
|
||||||
|
|
||||||
Future<void> changePassword() async {
|
Future<void> changePassword() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
setState(() {
|
setState(() {
|
||||||
oldPasswordError = newPassword1Error = newPassword2Error = null;
|
oldPasswordError = newPassword1Error = newPassword2Error = null;
|
||||||
});
|
});
|
||||||
|
|
@ -51,13 +53,13 @@ class SettingsPasswordController extends State<SettingsPassword> {
|
||||||
loading = true;
|
loading = true;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
|
||||||
await Matrix.of(context).client.changePassword(
|
await Matrix.of(context).client.changePassword(
|
||||||
newPassword1Controller.text,
|
newPassword1Controller.text,
|
||||||
oldPassword: oldPasswordController.text,
|
oldPassword: oldPasswordController.text,
|
||||||
);
|
);
|
||||||
|
if (!mounted) return;
|
||||||
scaffoldMessenger.showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).passwordHasBeenChanged)),
|
SnackBar(content: Text(l10n.passwordHasBeenChanged)),
|
||||||
);
|
);
|
||||||
if (mounted) context.pop();
|
if (mounted) context.pop();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -19,20 +19,21 @@ class SettingsSecurity extends StatefulWidget {
|
||||||
|
|
||||||
class SettingsSecurityController extends State<SettingsSecurity> {
|
class SettingsSecurityController extends State<SettingsSecurity> {
|
||||||
Future<void> setAppLockAction() async {
|
Future<void> setAppLockAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
if (AppLock.of(context).isActive) {
|
if (AppLock.of(context).isActive) {
|
||||||
AppLock.of(context).showLockScreen();
|
AppLock.of(context).showLockScreen();
|
||||||
}
|
}
|
||||||
final newLock = await showTextInputDialog(
|
final newLock = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).pleaseChooseAPasscode,
|
title: l10n.pleaseChooseAPasscode,
|
||||||
message: L10n.of(context).pleaseEnter4Digits,
|
message: l10n.pleaseEnter4Digits,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
validator: (text) {
|
validator: (text) {
|
||||||
if (text.isEmpty || (text.length == 4 && int.tryParse(text)! >= 0)) {
|
if (text.isEmpty || (text.length == 4 && int.tryParse(text)! >= 0)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return L10n.of(context).pleaseEnter4Digits;
|
return l10n.pleaseEnter4Digits;
|
||||||
},
|
},
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
|
|
@ -41,53 +42,55 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
||||||
maxLength: 4,
|
maxLength: 4,
|
||||||
);
|
);
|
||||||
if (newLock != null) {
|
if (newLock != null) {
|
||||||
|
if (!mounted) return;
|
||||||
await AppLock.of(context).changePincode(newLock);
|
await AppLock.of(context).changePincode(newLock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteAccountAction() async {
|
Future<void> deleteAccountAction() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final matrix = Matrix.of(context);
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).warning,
|
title: l10n.warning,
|
||||||
message: L10n.of(context).deactivateAccountWarning,
|
message: l10n.deactivateAccountWarning,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: l10n.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.cancel) {
|
OkCancelResult.cancel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final supposedMxid = Matrix.of(context).client.userID!;
|
if (!mounted) return;
|
||||||
|
final supposedMxid = matrix.client.userID!;
|
||||||
final mxid = await showTextInputDialog(
|
final mxid = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).confirmMatrixId,
|
title: l10n.confirmMatrixId,
|
||||||
validator: (text) => text == supposedMxid
|
validator: (text) =>
|
||||||
? null
|
text == supposedMxid ? null : l10n.supposedMxid(supposedMxid),
|
||||||
: L10n.of(context).supposedMxid(supposedMxid),
|
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
okLabel: L10n.of(context).delete,
|
okLabel: l10n.delete,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
);
|
);
|
||||||
if (mxid == null || mxid.isEmpty || mxid != supposedMxid) {
|
if (mxid == null || mxid.isEmpty || mxid != supposedMxid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!mounted) return;
|
||||||
final resp = await showFutureLoadingDialog(
|
final resp = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
delay: false,
|
delay: false,
|
||||||
future: () =>
|
future: () => matrix.client.uiaRequestBackground<IdServerUnbindResult?>(
|
||||||
Matrix.of(context).client.uiaRequestBackground<IdServerUnbindResult?>(
|
(auth) => matrix.client.deactivateAccount(auth: auth, erase: true),
|
||||||
(auth) => Matrix.of(
|
),
|
||||||
context,
|
|
||||||
).client.deactivateAccount(auth: auth, erase: true),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!resp.isError) {
|
if (!resp.isError) {
|
||||||
|
if (!mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context).client.logout(),
|
future: () => matrix.client.logout(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ class SettingsStyleController extends State<SettingsStyle> {
|
||||||
final picked = await selectFiles(context, type: FileType.image);
|
final picked = await selectFiles(context, type: FileType.image);
|
||||||
final pickedFile = picked.firstOrNull;
|
final pickedFile = picked.firstOrNull;
|
||||||
if (pickedFile == null) return;
|
if (pickedFile == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ abstract class FluffyShare {
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
bool copyOnly = false,
|
bool copyOnly = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
if (PlatformInfos.isMobile && !copyOnly) {
|
if (PlatformInfos.isMobile && !copyOnly) {
|
||||||
final box = context.findRenderObject() as RenderBox;
|
final box = context.findRenderObject() as RenderBox;
|
||||||
await SharePlus.instance.share(
|
await SharePlus.instance.share(
|
||||||
|
|
@ -24,21 +26,20 @@ abstract class FluffyShare {
|
||||||
}
|
}
|
||||||
await Clipboard.setData(ClipboardData(text: text));
|
await Clipboard.setData(ClipboardData(text: text));
|
||||||
if (!PlatformInfos.isMobile) {
|
if (!PlatformInfos.isMobile) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(
|
SnackBar(showCloseIcon: true, content: Text(l10n.copiedToClipboard)),
|
||||||
showCloseIcon: true,
|
|
||||||
content: Text(L10n.of(context).copiedToClipboard),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> shareInviteLink(BuildContext context) async {
|
static Future<void> shareInviteLink(BuildContext context) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
final ownProfile = await client.fetchOwnProfile();
|
final ownProfile = await client.fetchOwnProfile();
|
||||||
|
if (!context.mounted) return;
|
||||||
await FluffyShare.share(
|
await FluffyShare.share(
|
||||||
L10n.of(context).inviteText(
|
l10n.inviteText(
|
||||||
ownProfile.displayName ?? client.userID!,
|
ownProfile.displayName ?? client.userID!,
|
||||||
'https://matrix.to/#/${client.userID}?client=im.fluffychat',
|
'https://matrix.to/#/${client.userID}?client=im.fluffychat',
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,14 @@ extension LocalizedBody on Event {
|
||||||
|
|
||||||
Future<void> saveFile(BuildContext context) async {
|
Future<void> saveFile(BuildContext context) async {
|
||||||
final matrixFile = await _getFile(context);
|
final matrixFile = await _getFile(context);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
|
||||||
matrixFile.result?.save(context);
|
matrixFile.result?.save(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> shareFile(BuildContext context) async {
|
Future<void> shareFile(BuildContext context) async {
|
||||||
final matrixFile = await _getFile(context);
|
final matrixFile = await _getFile(context);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
|
||||||
matrixFile.result?.share(context);
|
matrixFile.result?.share(context);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,15 +50,17 @@ abstract class PlatformInfos {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> showDialog(BuildContext context) async {
|
static Future<void> showDialog(BuildContext context) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final version = await PlatformInfos.getVersion();
|
final version = await PlatformInfos.getVersion();
|
||||||
|
if (!context.mounted) return;
|
||||||
showAboutDialog(
|
showAboutDialog(
|
||||||
context: context,
|
context: context,
|
||||||
children: [
|
children: [
|
||||||
Text(L10n.of(context).versionWithNumber(version)),
|
Text(l10n.versionWithNumber(version)),
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
onPressed: () => launchUrlString(AppConfig.sourceCodeUrl),
|
onPressed: () => launchUrlString(AppConfig.sourceCodeUrl),
|
||||||
icon: const Icon(Icons.source_outlined),
|
icon: const Icon(Icons.source_outlined),
|
||||||
label: Text(L10n.of(context).sourceCode),
|
label: Text(l10n.sourceCode),
|
||||||
),
|
),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (innerContext) {
|
builder: (innerContext) {
|
||||||
|
|
@ -68,7 +70,7 @@ abstract class PlatformInfos {
|
||||||
Navigator.of(innerContext).pop();
|
Navigator.of(innerContext).pop();
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.list_outlined),
|
icon: const Icon(Icons.list_outlined),
|
||||||
label: Text(L10n.of(context).logs),
|
label: Text(l10n.logs),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -80,7 +82,7 @@ abstract class PlatformInfos {
|
||||||
Navigator.of(innerContext).pop();
|
Navigator.of(innerContext).pop();
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.settings_applications_outlined),
|
icon: const Icon(Icons.settings_applications_outlined),
|
||||||
label: Text(L10n.of(context).advancedConfigs),
|
label: Text(l10n.advancedConfigs),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ abstract class UpdateNotifier {
|
||||||
|
|
||||||
static Future<void> showUpdateSnackBar(BuildContext context) async {
|
static Future<void> showUpdateSnackBar(BuildContext context) async {
|
||||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final currentVersion = await PlatformInfos.getVersion();
|
final currentVersion = await PlatformInfos.getVersion();
|
||||||
final store = await SharedPreferences.getInstance();
|
final store = await SharedPreferences.getInstance();
|
||||||
final storedVersion = store.getString(versionStoreKey);
|
final storedVersion = store.getString(versionStoreKey);
|
||||||
|
|
@ -20,9 +21,9 @@ abstract class UpdateNotifier {
|
||||||
SnackBar(
|
SnackBar(
|
||||||
duration: const Duration(seconds: 30),
|
duration: const Duration(seconds: 30),
|
||||||
showCloseIcon: true,
|
showCloseIcon: true,
|
||||||
content: Text(L10n.of(context).updateInstalled(currentVersion)),
|
content: Text(l10n.updateInstalled(currentVersion)),
|
||||||
action: SnackBarAction(
|
action: SnackBarAction(
|
||||||
label: L10n.of(context).changelog,
|
label: l10n.changelog,
|
||||||
onPressed: () => launchUrlString(AppConfig.changelogUrl),
|
onPressed: () => launchUrlString(AppConfig.changelogUrl),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ Future<void> connectToHomeserverFlow(
|
||||||
|
|
||||||
if ((kIsWeb || PlatformInfos.isLinux) &&
|
if ((kIsWeb || PlatformInfos.isLinux) &&
|
||||||
(supportsSso || authMetadata != null || (signUp && regLink != null))) {
|
(supportsSso || authMetadata != null || (signUp && regLink != null))) {
|
||||||
|
if (!context.mounted) return;
|
||||||
final consent = await showOkCancelAlertDialog(
|
final consent = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: l10n.appWantsToUseForLogin(homeserverInput),
|
title: l10n.appWantsToUseForLogin(homeserverInput),
|
||||||
|
|
@ -45,7 +46,9 @@ Future<void> connectToHomeserverFlow(
|
||||||
okLabel: l10n.continueText,
|
okLabel: l10n.continueText,
|
||||||
);
|
);
|
||||||
if (consent != OkCancelResult.ok) return;
|
if (consent != OkCancelResult.ok) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
}
|
}
|
||||||
|
if (!context.mounted) return;
|
||||||
|
|
||||||
if (authMetadata != null && AppSettings.enableMatrixNativeOIDC.value) {
|
if (authMetadata != null && AppSettings.enableMatrixNativeOIDC.value) {
|
||||||
await oidcLoginFlow(client, context, signUp);
|
await oidcLoginFlow(client, context, signUp);
|
||||||
|
|
@ -55,6 +58,7 @@ Future<void> connectToHomeserverFlow(
|
||||||
if (signUp && regLink != null) {
|
if (signUp && regLink != null) {
|
||||||
await launchUrlString(regLink);
|
await launchUrlString(regLink);
|
||||||
}
|
}
|
||||||
|
if (!context.mounted) return;
|
||||||
final pathSegments = List.of(
|
final pathSegments = List.of(
|
||||||
GoRouter.of(context).routeInformationProvider.value.uri.pathSegments,
|
GoRouter.of(context).routeInformationProvider.value.uri.pathSegments,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ class UrlLauncher {
|
||||||
const UrlLauncher(this.context, this.url, [this.name]);
|
const UrlLauncher(this.context, this.url, [this.name]);
|
||||||
|
|
||||||
Future<void> launchUrl() async {
|
Future<void> launchUrl() async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
if (url!.toLowerCase().startsWith(AppConfig.deepLinkPrefix) ||
|
if (url!.toLowerCase().startsWith(AppConfig.deepLinkPrefix) ||
|
||||||
url!.toLowerCase().startsWith(AppConfig.inviteLinkPrefix) ||
|
url!.toLowerCase().startsWith(AppConfig.inviteLinkPrefix) ||
|
||||||
{'#', '@', '!', '+', '\$'}.contains(url![0]) ||
|
{'#', '@', '!', '+', '\$'}.contains(url![0]) ||
|
||||||
|
|
@ -36,8 +38,8 @@ class UrlLauncher {
|
||||||
final uri = Uri.tryParse(url!);
|
final uri = Uri.tryParse(url!);
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
// we can't open this thing
|
// we can't open this thing
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).cantOpenUri(url!))),
|
SnackBar(content: Text(l10n.cantOpenUri(url!))),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -47,10 +49,10 @@ class UrlLauncher {
|
||||||
// that the user can see the actual url before opening the browser.
|
// that the user can see the actual url before opening the browser.
|
||||||
final consent = await showOkCancelAlertDialog(
|
final consent = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).openLinkInBrowser,
|
title: l10n.openLinkInBrowser,
|
||||||
message: url,
|
message: url,
|
||||||
okLabel: L10n.of(context).open,
|
okLabel: l10n.open,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
);
|
);
|
||||||
if (consent != OkCancelResult.ok) return;
|
if (consent != OkCancelResult.ok) return;
|
||||||
}
|
}
|
||||||
|
|
@ -90,8 +92,8 @@ class UrlLauncher {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (uri.host.isEmpty) {
|
if (uri.host.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
scaffoldMessenger.showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).cantOpenUri(url!))),
|
SnackBar(content: Text(l10n.cantOpenUri(url!))),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -161,6 +163,7 @@ class UrlLauncher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
servers.addAll(identityParts.via);
|
servers.addAll(identityParts.via);
|
||||||
|
if (!context.mounted) return;
|
||||||
if (room != null) {
|
if (room != null) {
|
||||||
if (room.isSpace) {
|
if (room.isSpace) {
|
||||||
// TODO: Implement navigate to space
|
// TODO: Implement navigate to space
|
||||||
|
|
@ -178,6 +181,7 @@ class UrlLauncher {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
if (!context.mounted) return;
|
||||||
await showAdaptiveDialog(
|
await showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) =>
|
builder: (c) =>
|
||||||
|
|
@ -185,6 +189,7 @@ class UrlLauncher {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (roomIdOrAlias.sigil == '!') {
|
if (roomIdOrAlias.sigil == '!') {
|
||||||
|
if (!context.mounted) return;
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -192,6 +197,7 @@ class UrlLauncher {
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.ok) {
|
OkCancelResult.ok) {
|
||||||
roomId = roomIdOrAlias;
|
roomId = roomIdOrAlias;
|
||||||
|
if (!context.mounted) return;
|
||||||
final response = await showFutureLoadingDialog(
|
final response = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => matrix.client.joinRoom(
|
future: () => matrix.client.joinRoom(
|
||||||
|
|
@ -200,11 +206,13 @@ class UrlLauncher {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (response.error != null) return;
|
if (response.error != null) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
// wait for two seconds so that it probably came down /sync
|
// wait for two seconds so that it probably came down /sync
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Future.delayed(const Duration(seconds: 2)),
|
future: () => Future.delayed(const Duration(seconds: 2)),
|
||||||
);
|
);
|
||||||
|
if (!context.mounted) return;
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
context.go(
|
context.go(
|
||||||
Uri(
|
Uri(
|
||||||
|
|
@ -228,6 +236,7 @@ class UrlLauncher {
|
||||||
return Profile(userId: userId);
|
return Profile(userId: userId);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
if (!context.mounted) return;
|
||||||
await UserDialog.show(
|
await UserDialog.show(
|
||||||
context: context,
|
context: context,
|
||||||
profile: profileResult.result!,
|
profile: profileResult.result!,
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
const PublicRoomDialog({super.key, this.roomAlias, this.chunk, this.via});
|
const PublicRoomDialog({super.key, this.roomAlias, this.chunk, this.via});
|
||||||
|
|
||||||
Future<void> _joinRoom(BuildContext context) async {
|
Future<void> _joinRoom(BuildContext context) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
final chunk = this.chunk;
|
final chunk = this.chunk;
|
||||||
final knock = chunk?.joinRule == 'knock';
|
final knock = chunk?.joinRule == 'knock';
|
||||||
|
|
@ -48,12 +49,13 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
);
|
);
|
||||||
final roomId = result.result;
|
final roomId = result.result;
|
||||||
if (roomId == null) return;
|
if (roomId == null) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
if (knock && client.getRoomById(roomId) == null) {
|
if (knock && client.getRoomById(roomId) == null) {
|
||||||
Navigator.of(context).pop<bool>(true);
|
Navigator.of(context).pop<bool>(true);
|
||||||
await showOkAlertDialog(
|
await showOkAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).youHaveKnocked,
|
title: l10n.youHaveKnocked,
|
||||||
message: L10n.of(context).pleaseWaitUntilInvited,
|
message: l10n.pleaseWaitUntilInvited,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -73,6 +75,7 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
bool _testRoom(PublishedRoomsChunk r) => r.canonicalAlias == roomAlias;
|
bool _testRoom(PublishedRoomsChunk r) => r.canonicalAlias == roomAlias;
|
||||||
|
|
||||||
Future<PublishedRoomsChunk> _search(BuildContext context) async {
|
Future<PublishedRoomsChunk> _search(BuildContext context) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final chunk = this.chunk;
|
final chunk = this.chunk;
|
||||||
if (chunk != null) return chunk;
|
if (chunk != null) return chunk;
|
||||||
final query = await Matrix.of(context).client.queryPublicRooms(
|
final query = await Matrix.of(context).client.queryPublicRooms(
|
||||||
|
|
@ -80,7 +83,7 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
filter: PublicRoomQueryFilter(genericSearchTerm: roomAlias),
|
filter: PublicRoomQueryFilter(genericSearchTerm: roomAlias),
|
||||||
);
|
);
|
||||||
if (!query.chunk.any(_testRoom)) {
|
if (!query.chunk.any(_testRoom)) {
|
||||||
throw (L10n.of(context).noRoomsFound);
|
throw (l10n.noRoomsFound);
|
||||||
}
|
}
|
||||||
return query.chunk.firstWhere(_testRoom);
|
return query.chunk.firstWhere(_testRoom);
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +251,7 @@ class PublicRoomDialog extends StatelessWidget {
|
||||||
hintText: L10n.of(context).reason,
|
hintText: L10n.of(context).reason,
|
||||||
);
|
);
|
||||||
if (reason == null || reason.isEmpty) return;
|
if (reason == null || reason.isEmpty) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context).client.reportRoom(
|
future: () => Matrix.of(context).client.reportRoom(
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@ class UserDialog extends StatelessWidget {
|
||||||
hintText: L10n.of(context).reason,
|
hintText: L10n.of(context).reason,
|
||||||
);
|
);
|
||||||
if (reason == null || reason.isEmpty) return;
|
if (reason == null || reason.isEmpty) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(
|
future: () => Matrix.of(
|
||||||
|
|
|
||||||
|
|
@ -53,16 +53,18 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
||||||
onSelected: (choice) async {
|
onSelected: (choice) async {
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case ChatPopupMenuActions.leave:
|
case ChatPopupMenuActions.leave:
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final router = GoRouter.of(context);
|
final router = GoRouter.of(context);
|
||||||
final confirmed = await showOkCancelAlertDialog(
|
final confirmed = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: l10n.areYouSure,
|
||||||
message: L10n.of(context).archiveRoomDescription,
|
message: l10n.archiveRoomDescription,
|
||||||
okLabel: L10n.of(context).leave,
|
okLabel: l10n.leave,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
);
|
);
|
||||||
if (confirmed != OkCancelResult.ok) return;
|
if (confirmed != OkCancelResult.ok) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => widget.room.leave(),
|
future: () => widget.room.leave(),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:fluffychat/l10n/l10n.dart';
|
||||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||||
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
|
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:matrix/matrix_api_lite/utils/logs.dart';
|
||||||
|
|
||||||
/// Displays a loading dialog which reacts to the given [future]. The dialog
|
/// Displays a loading dialog which reacts to the given [future]. The dialog
|
||||||
/// will be dismissed and the value will be returned when the future completes.
|
/// will be dismissed and the value will be returned when the future completes.
|
||||||
|
|
@ -40,6 +41,15 @@ Future<Result<T>> showFutureLoadingDialog<T>({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!context.mounted) {
|
||||||
|
Logs().e(
|
||||||
|
'Unable to show loading dialog!',
|
||||||
|
Exception('The BuildContext is not mounted!'),
|
||||||
|
StackTrace.current,
|
||||||
|
);
|
||||||
|
return Result.capture(futureExec);
|
||||||
|
}
|
||||||
|
|
||||||
final result = await showAdaptiveDialog<Result<T>>(
|
final result = await showAdaptiveDialog<Result<T>>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: barrierDismissible,
|
barrierDismissible: barrierDismissible,
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import 'package:universal_html/html.dart' as html;
|
||||||
|
|
||||||
extension LocalNotificationsExtension on MatrixState {
|
extension LocalNotificationsExtension on MatrixState {
|
||||||
Future<void> showLocalNotification(Event event) async {
|
Future<void> showLocalNotification(Event event) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final roomId = event.room.id;
|
final roomId = event.room.id;
|
||||||
if (activeRoomId == roomId) {
|
if (activeRoomId == roomId) {
|
||||||
if (WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) {
|
if (WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) {
|
||||||
|
|
@ -114,11 +115,11 @@ extension LocalNotificationsExtension on MatrixState {
|
||||||
actions: [
|
actions: [
|
||||||
NotificationAction(
|
NotificationAction(
|
||||||
DesktopNotificationActions.openChat.name,
|
DesktopNotificationActions.openChat.name,
|
||||||
L10n.of(context).openChat,
|
l10n.openChat,
|
||||||
),
|
),
|
||||||
NotificationAction(
|
NotificationAction(
|
||||||
DesktopNotificationActions.seen.name,
|
DesktopNotificationActions.seen.name,
|
||||||
L10n.of(context).markAsRead,
|
l10n.markAsRead,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
hints: hints,
|
hints: hints,
|
||||||
|
|
|
||||||
|
|
@ -265,12 +265,19 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
||||||
InitWithRestoreExtension.deleteSessionBackup(name);
|
InitWithRestoreExtension.deleteSessionBackup(name);
|
||||||
|
|
||||||
if (loggedInWithMultipleClients) {
|
if (loggedInWithMultipleClients) {
|
||||||
|
final snackbarContext =
|
||||||
|
FluffyChatApp
|
||||||
|
.router
|
||||||
|
.routerDelegate
|
||||||
|
.navigatorKey
|
||||||
|
.currentContext ??
|
||||||
|
context;
|
||||||
|
|
||||||
|
if (!snackbarContext.mounted) return;
|
||||||
|
final l10n = L10n.of(snackbarContext);
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ??
|
snackbarContext,
|
||||||
context,
|
).showSnackBar(SnackBar(content: Text(l10n.oneClientLoggedOut)));
|
||||||
).showSnackBar(
|
|
||||||
SnackBar(content: Text(L10n.of(context).oneClientLoggedOut)),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FluffyChatApp.router.go('/');
|
FluffyChatApp.router.go('/');
|
||||||
|
|
@ -382,15 +389,17 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> dehydrateAction(BuildContext context) async {
|
Future<void> dehydrateAction(BuildContext context) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
final response = await showOkCancelAlertDialog(
|
final response = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
title: L10n.of(context).dehydrate,
|
title: l10n.dehydrate,
|
||||||
message: L10n.of(context).dehydrateWarning,
|
message: l10n.dehydrateWarning,
|
||||||
);
|
);
|
||||||
if (response != OkCancelResult.ok) {
|
if (response != OkCancelResult.ok) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!context.mounted) return;
|
||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: client.exportDump,
|
future: client.exportDump,
|
||||||
|
|
@ -404,6 +413,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
||||||
'fluffychat-export-${DateFormat(DateFormat.YEAR_MONTH_DAY).format(DateTime.now())}.fluffybackup';
|
'fluffychat-export-${DateFormat(DateFormat.YEAR_MONTH_DAY).format(DateTime.now())}.fluffybackup';
|
||||||
|
|
||||||
final file = MatrixFile(bytes: exportBytes, name: exportFileName);
|
final file = MatrixFile(bytes: exportBytes, name: exportFileName);
|
||||||
|
if (!context.mounted) return;
|
||||||
file.save(context);
|
file.save(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ Future<void> showMemberActionsPopupMenu({
|
||||||
required User user,
|
required User user,
|
||||||
void Function()? onMention,
|
void Function()? onMention,
|
||||||
}) async {
|
}) async {
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final displayname = user.calcDisplayname();
|
final displayname = user.calcDisplayname();
|
||||||
final isMe = user.room.client.userID == user.id;
|
final isMe = user.room.client.userID == user.id;
|
||||||
|
|
@ -245,12 +247,13 @@ Future<void> showMemberActionsPopupMenu({
|
||||||
case _MemberActions.kick:
|
case _MemberActions.kick:
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: l10n.areYouSure,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: l10n.yes,
|
||||||
cancelLabel: L10n.of(context).no,
|
cancelLabel: l10n.no,
|
||||||
message: L10n.of(context).kickUserDescription,
|
message: l10n.kickUserDescription,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.ok) {
|
OkCancelResult.ok) {
|
||||||
|
if (!context.mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => user.kick(),
|
future: () => user.kick(),
|
||||||
|
|
@ -260,12 +263,13 @@ Future<void> showMemberActionsPopupMenu({
|
||||||
case _MemberActions.ban:
|
case _MemberActions.ban:
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: l10n.areYouSure,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: l10n.yes,
|
||||||
cancelLabel: L10n.of(context).no,
|
cancelLabel: l10n.no,
|
||||||
message: L10n.of(context).banUserDescription,
|
message: l10n.banUserDescription,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.ok) {
|
OkCancelResult.ok) {
|
||||||
|
if (!context.mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => user.ban(),
|
future: () => user.ban(),
|
||||||
|
|
@ -275,20 +279,22 @@ Future<void> showMemberActionsPopupMenu({
|
||||||
case _MemberActions.report:
|
case _MemberActions.report:
|
||||||
final reason = await showTextInputDialog(
|
final reason = await showTextInputDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).whyDoYouWantToReportThis,
|
title: l10n.whyDoYouWantToReportThis,
|
||||||
okLabel: L10n.of(context).report,
|
okLabel: l10n.report,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: l10n.cancel,
|
||||||
hintText: L10n.of(context).reason,
|
hintText: l10n.reason,
|
||||||
);
|
);
|
||||||
if (reason == null || reason.isEmpty) return;
|
if (reason == null || reason.isEmpty) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
|
|
||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => user.room.client.reportUser(user.id, reason),
|
future: () => user.room.client.reportUser(user.id, reason),
|
||||||
);
|
);
|
||||||
if (result.error != null) return;
|
if (result.error != null) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
if (!context.mounted) return;
|
||||||
SnackBar(content: Text(L10n.of(context).contentHasBeenReported)),
|
scaffoldMessenger.showSnackBar(
|
||||||
|
SnackBar(content: Text(l10n.contentHasBeenReported)),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
case _MemberActions.info:
|
case _MemberActions.info:
|
||||||
|
|
@ -304,12 +310,13 @@ Future<void> showMemberActionsPopupMenu({
|
||||||
case _MemberActions.unban:
|
case _MemberActions.unban:
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: l10n.areYouSure,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: l10n.yes,
|
||||||
cancelLabel: L10n.of(context).no,
|
cancelLabel: l10n.no,
|
||||||
message: L10n.of(context).unbanUserDescription,
|
message: l10n.unbanUserDescription,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.ok) {
|
OkCancelResult.ok) {
|
||||||
|
if (!context.mounted) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => user.unban(),
|
future: () => user.unban(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue