Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
2c8d415475
34 changed files with 495 additions and 727 deletions
|
|
@ -3,7 +3,6 @@ import 'dart:ui';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/semantics.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
|
@ -24,6 +23,8 @@ import 'widgets/fluffy_chat_app.dart';
|
|||
|
||||
ReceivePort? mainIsolateReceivePort;
|
||||
|
||||
bool _vodozemacInitialized = false;
|
||||
|
||||
void main() async {
|
||||
if (PlatformInfos.isAndroid) {
|
||||
final port = mainIsolateReceivePort = ReceivePort();
|
||||
|
|
@ -63,7 +64,10 @@ void main() async {
|
|||
final store = await AppSettings.init();
|
||||
Logs().i('Welcome to ${AppSettings.applicationName.value} <3');
|
||||
|
||||
await vod.init(wasmPath: './assets/assets/vodozemac/');
|
||||
if (!_vodozemacInitialized) {
|
||||
await vod.init(wasmPath: './assets/assets/vodozemac/');
|
||||
_vodozemacInitialized = true;
|
||||
}
|
||||
|
||||
Logs().nativeColors = !PlatformInfos.isIOS;
|
||||
final clients = await ClientManager.getClients(store: store);
|
||||
|
|
@ -117,9 +121,6 @@ Future<void> startGui(List<Client> clients, SharedPreferences store) async {
|
|||
await firstClient?.accountDataLoading;
|
||||
|
||||
runApp(FluffyChatApp(clients: clients, pincode: pin, store: store));
|
||||
if (const String.fromEnvironment('WITH_SEMANTICS') == 'true') {
|
||||
SemanticsBinding.instance.ensureSemantics();
|
||||
}
|
||||
}
|
||||
|
||||
/// Watches the lifecycle changes to start the application when it
|
||||
|
|
|
|||
|
|
@ -204,39 +204,31 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_supportsSecureStorage)
|
||||
Semantics(
|
||||
identifier: 'store_in_secure_storage',
|
||||
child: CheckboxListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
),
|
||||
value: _storeInSecureStorage,
|
||||
activeColor: theme.colorScheme.primary,
|
||||
onChanged: (b) {
|
||||
setState(() {
|
||||
_storeInSecureStorage = b;
|
||||
});
|
||||
},
|
||||
title: Text(_getSecureStorageLocalizedName()),
|
||||
subtitle: Text(
|
||||
L10n.of(context).storeInSecureStorageDescription,
|
||||
),
|
||||
CheckboxListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
value: _storeInSecureStorage,
|
||||
activeColor: theme.colorScheme.primary,
|
||||
onChanged: (b) {
|
||||
setState(() {
|
||||
_storeInSecureStorage = b;
|
||||
});
|
||||
},
|
||||
title: Text(_getSecureStorageLocalizedName()),
|
||||
subtitle: Text(
|
||||
L10n.of(context).storeInSecureStorageDescription,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Semantics(
|
||||
identifier: 'copy_to_clipboard',
|
||||
child: CheckboxListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
value: _recoveryKeyCopied,
|
||||
activeColor: theme.colorScheme.primary,
|
||||
onChanged: (b) {
|
||||
FluffyShare.share(key!, context);
|
||||
setState(() => _recoveryKeyCopied = true);
|
||||
},
|
||||
title: Text(L10n.of(context).copyToClipboard),
|
||||
subtitle: Text(L10n.of(context).saveKeyManuallyDescription),
|
||||
),
|
||||
CheckboxListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
value: _recoveryKeyCopied,
|
||||
activeColor: theme.colorScheme.primary,
|
||||
onChanged: (b) {
|
||||
FluffyShare.share(key!, context, copyOnly: true);
|
||||
setState(() => _recoveryKeyCopied = true);
|
||||
},
|
||||
title: Text(L10n.of(context).copyToClipboard),
|
||||
subtitle: Text(L10n.of(context).saveKeyManuallyDescription),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton.icon(
|
||||
|
|
|
|||
|
|
@ -397,6 +397,7 @@ class ChatInputRow extends StatelessWidget {
|
|||
),
|
||||
)
|
||||
: IconButton(
|
||||
key: Key('send_button'),
|
||||
tooltip: L10n.of(context).send,
|
||||
onPressed: controller.send,
|
||||
style: IconButton.styleFrom(
|
||||
|
|
|
|||
|
|
@ -385,6 +385,7 @@ class InputBar extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Autocomplete<Map<String, String?>>(
|
||||
key: Key('chat_input_field'),
|
||||
focusNode: focusNode,
|
||||
textEditingController: controller,
|
||||
optionsBuilder: getSuggestions,
|
||||
|
|
|
|||
|
|
@ -173,23 +173,20 @@ class ClientChooserButton extends StatelessWidget {
|
|||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
color: Colors.transparent,
|
||||
child: Semantics(
|
||||
identifier: 'accounts_and_settings',
|
||||
child: PopupMenuButton<Object>(
|
||||
tooltip: 'Accounts and settings',
|
||||
popUpAnimationStyle: FluffyThemes.isColumnMode(context)
|
||||
? AnimationStyle.noAnimation
|
||||
: null, // https://github.com/flutter/flutter/issues/167180
|
||||
onSelected: (o) => _clientSelected(o, context),
|
||||
itemBuilder: _bundleMenuItems,
|
||||
child: Center(
|
||||
child: Avatar(
|
||||
mxContent: snapshot.data?.avatarUrl,
|
||||
name:
|
||||
snapshot.data?.displayName ??
|
||||
matrix.client.userID?.localpart,
|
||||
size: 32,
|
||||
),
|
||||
child: PopupMenuButton<Object>(
|
||||
key: Key('accounts_and_settings_buttons'),
|
||||
tooltip: 'Accounts and settings',
|
||||
popUpAnimationStyle: FluffyThemes.isColumnMode(context)
|
||||
? AnimationStyle.noAnimation
|
||||
: null, // https://github.com/flutter/flutter/issues/167180
|
||||
onSelected: (o) => _clientSelected(o, context),
|
||||
itemBuilder: _bundleMenuItems,
|
||||
child: Center(
|
||||
child: Avatar(
|
||||
mxContent: snapshot.data?.avatarUrl,
|
||||
name:
|
||||
snapshot.data?.displayName ?? matrix.client.userID?.localpart,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ class SettingsController extends State<Settings> {
|
|||
context: context,
|
||||
future: () => matrix.client.logout(),
|
||||
);
|
||||
context.go('/');
|
||||
}
|
||||
|
||||
Future<void> setAvatarAction() async {
|
||||
|
|
|
|||
|
|
@ -96,104 +96,98 @@ class SignInPage extends StatelessWidget {
|
|||
itemBuilder: (context, i) {
|
||||
final server = publicHomeservers[i];
|
||||
final website = server.website;
|
||||
return Semantics(
|
||||
identifier: 'homeserver_tile_$i',
|
||||
child: RadioListTile(
|
||||
value: server,
|
||||
enabled:
|
||||
state.loginLoading.connectionState !=
|
||||
ConnectionState.waiting,
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(server.name ?? 'Unknown'),
|
||||
),
|
||||
if (website != null)
|
||||
SizedBox.square(
|
||||
dimension: 32,
|
||||
child: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.open_in_new_outlined,
|
||||
size: 16,
|
||||
),
|
||||
onPressed: () =>
|
||||
launchUrlString(website),
|
||||
return RadioListTile(
|
||||
value: server,
|
||||
enabled:
|
||||
state.loginLoading.connectionState !=
|
||||
ConnectionState.waiting,
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(server.name ?? 'Unknown'),
|
||||
),
|
||||
if (website != null)
|
||||
SizedBox.square(
|
||||
dimension: 32,
|
||||
child: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.open_in_new_outlined,
|
||||
size: 16,
|
||||
),
|
||||
onPressed: () =>
|
||||
launchUrlString(website),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
spacing: 4.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (server.features?.isNotEmpty == true)
|
||||
Wrap(
|
||||
spacing: 4.0,
|
||||
runSpacing: 4.0,
|
||||
children: [
|
||||
...?server.languages?.map(
|
||||
(language) => Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.tertiaryContainer,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 6.0,
|
||||
vertical: 3.0,
|
||||
),
|
||||
child: Text(
|
||||
language,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onTertiaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
...server.features!.map(
|
||||
(feature) => Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 6.0,
|
||||
vertical: 3.0,
|
||||
),
|
||||
child: Text(
|
||||
feature,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
server.description ??
|
||||
'A matrix homeserver',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
spacing: 4.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (server.features?.isNotEmpty == true)
|
||||
Wrap(
|
||||
spacing: 4.0,
|
||||
runSpacing: 4.0,
|
||||
children: [
|
||||
...?server.languages?.map(
|
||||
(language) => Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.tertiaryContainer,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 6.0,
|
||||
vertical: 3.0,
|
||||
),
|
||||
child: Text(
|
||||
language,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onTertiaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
...server.features!.map(
|
||||
(feature) => Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 6.0,
|
||||
vertical: 3.0,
|
||||
),
|
||||
child: Text(
|
||||
feature,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
server.description ?? 'A matrix homeserver',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -217,29 +211,26 @@ class SignInPage extends StatelessWidget {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SafeArea(
|
||||
child: Semantics(
|
||||
identifier: 'connect_to_homeserver_button',
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: theme.colorScheme.onPrimary,
|
||||
),
|
||||
onPressed:
|
||||
state.loginLoading.connectionState ==
|
||||
ConnectionState.waiting
|
||||
? null
|
||||
: () => connectToHomeserverFlow(
|
||||
selectedHomserver,
|
||||
context,
|
||||
viewModel.setLoginLoading,
|
||||
signUp,
|
||||
),
|
||||
child:
|
||||
state.loginLoading.connectionState ==
|
||||
ConnectionState.waiting
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: Text(L10n.of(context).continueText),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: theme.colorScheme.onPrimary,
|
||||
),
|
||||
onPressed:
|
||||
state.loginLoading.connectionState ==
|
||||
ConnectionState.waiting
|
||||
? null
|
||||
: () => connectToHomeserverFlow(
|
||||
selectedHomserver,
|
||||
context,
|
||||
viewModel.setLoginLoading,
|
||||
signUp,
|
||||
),
|
||||
child:
|
||||
state.loginLoading.connectionState ==
|
||||
ConnectionState.waiting
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: Text(L10n.of(context).continueText),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -24,9 +24,14 @@ abstract class FluffyShare {
|
|||
return;
|
||||
}
|
||||
await Clipboard.setData(ClipboardData(text: text));
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(L10n.of(context).copiedToClipboard)));
|
||||
if (!PlatformInfos.isMobile) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
showCloseIcon: true,
|
||||
content: Text(L10n.of(context).copiedToClipboard),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,11 +41,13 @@ Future<OkCancelResult?> showOkCancelAlertDialog({
|
|||
),
|
||||
actions: [
|
||||
AdaptiveDialogAction(
|
||||
key: Key('ok_cancel_alert_dialog_cancel_button'),
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop<OkCancelResult>(OkCancelResult.cancel),
|
||||
child: Text(cancelLabel ?? L10n.of(context).cancel),
|
||||
),
|
||||
AdaptiveDialogAction(
|
||||
key: Key('ok_cancel_alert_dialog_ok_button'),
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop<OkCancelResult>(OkCancelResult.ok),
|
||||
autofocus: true,
|
||||
|
|
|
|||
|
|
@ -85,7 +85,11 @@ class LoadingDialogState<T> extends State<LoadingDialog> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
widget.future.then(
|
||||
(result) => Navigator.of(context).pop<Result<T>>(Result.value(result)),
|
||||
(result) {
|
||||
if (!mounted) return;
|
||||
if (!Navigator.of(context).canPop()) return;
|
||||
Navigator.of(context).pop<Result<T>>(Result.value(result));
|
||||
},
|
||||
onError: (e, s) => setState(() {
|
||||
exception = e;
|
||||
stackTrace = s;
|
||||
|
|
|
|||
|
|
@ -371,7 +371,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
onKeyVerificationRequestSub.values.map((s) => s.cancel());
|
||||
onLogoutSub.values.map((s) => s.cancel());
|
||||
onNotification.values.map((s) => s.cancel());
|
||||
client.httpClient.close();
|
||||
|
||||
linuxNotifications?.close();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue