mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-20 02:59:26 +01:00
Merge branch 'krille/migrate-to-nullsafety' into 'main'
refactor: Migrate to null safety See merge request famedly/fluffychat!701
This commit is contained in:
commit
ff6f67e1b2
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
abstract class AppEmojis {
|
abstract class AppEmojis {
|
||||||
static const List<String> emojis = [
|
static const List<String> emojis = [
|
||||||
'👍',
|
'👍',
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:vrouter/vrouter.dart';
|
import 'package:vrouter/vrouter.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
abstract class SettingKeys {
|
abstract class SettingKeys {
|
||||||
static const String jitsiInstance = 'chat.fluffy.jitsi_instance';
|
static const String jitsiInstance = 'chat.fluffy.jitsi_instance';
|
||||||
static const String wallpaper = 'chat.fluffy.wallpaper';
|
static const String wallpaper = 'chat.fluffy.wallpaper';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// @dart=2.11
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -67,14 +65,14 @@ void main() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FluffyChatApp extends StatefulWidget {
|
class FluffyChatApp extends StatefulWidget {
|
||||||
final Widget testWidget;
|
final Widget? testWidget;
|
||||||
final List<Client> clients;
|
final List<Client> clients;
|
||||||
final Map<String, String> queryParameters;
|
final Map<String, String>? queryParameters;
|
||||||
|
|
||||||
const FluffyChatApp({
|
const FluffyChatApp({
|
||||||
Key key,
|
Key? key,
|
||||||
this.testWidget,
|
this.testWidget,
|
||||||
@required this.clients,
|
required this.clients,
|
||||||
this.queryParameters,
|
this.queryParameters,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -88,9 +86,9 @@ class FluffyChatApp extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _FluffyChatAppState extends State<FluffyChatApp> {
|
class _FluffyChatAppState extends State<FluffyChatApp> {
|
||||||
GlobalKey<VRouterState> _router;
|
GlobalKey<VRouterState>? _router;
|
||||||
bool columnMode;
|
bool? columnMode;
|
||||||
String _initialUrl;
|
String? _initialUrl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -133,15 +131,12 @@ class _FluffyChatAppState extends State<FluffyChatApp> {
|
|||||||
localizationsDelegates: L10n.localizationsDelegates,
|
localizationsDelegates: L10n.localizationsDelegates,
|
||||||
supportedLocales: L10n.supportedLocales,
|
supportedLocales: L10n.supportedLocales,
|
||||||
initialUrl: _initialUrl ?? '/',
|
initialUrl: _initialUrl ?? '/',
|
||||||
locale: kIsWeb
|
|
||||||
? Locale(html.window.navigator.language.split('-').first)
|
|
||||||
: null,
|
|
||||||
routes: AppRoutes(columnMode ?? false).routes,
|
routes: AppRoutes(columnMode ?? false).routes,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
LoadingDialog.defaultTitle = L10n.of(context).loadingPleaseWait;
|
LoadingDialog.defaultTitle = L10n.of(context)!.loadingPleaseWait;
|
||||||
LoadingDialog.defaultBackLabel = L10n.of(context).close;
|
LoadingDialog.defaultBackLabel = L10n.of(context)!.close;
|
||||||
LoadingDialog.defaultOnError =
|
LoadingDialog.defaultOnError =
|
||||||
(e) => (e as Object).toLocalizedString(context);
|
(e) => (e as Object?)!.toLocalizedString(context);
|
||||||
WidgetsBinding.instance?.addPostFrameCallback((_) {
|
WidgetsBinding.instance?.addPostFrameCallback((_) {
|
||||||
SystemChrome.setSystemUIOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
SystemUiOverlayStyle(
|
SystemUiOverlayStyle(
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
@ -18,12 +18,12 @@ class BootstrapDialog extends StatefulWidget {
|
|||||||
final bool wipe;
|
final bool wipe;
|
||||||
final Client client;
|
final Client client;
|
||||||
const BootstrapDialog({
|
const BootstrapDialog({
|
||||||
Key key,
|
Key? key,
|
||||||
this.wipe = false,
|
this.wipe = false,
|
||||||
@required this.client,
|
required this.client,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
Future<bool> show(BuildContext context) => PlatformInfos.isCupertinoStyle
|
Future<bool?> show(BuildContext context) => PlatformInfos.isCupertinoStyle
|
||||||
? showCupertinoDialog(
|
? showCupertinoDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => this,
|
builder: (context) => this,
|
||||||
@ -45,18 +45,18 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
final TextEditingController _recoveryKeyTextEditingController =
|
final TextEditingController _recoveryKeyTextEditingController =
|
||||||
TextEditingController();
|
TextEditingController();
|
||||||
|
|
||||||
Bootstrap bootstrap;
|
late Bootstrap bootstrap;
|
||||||
|
|
||||||
String _recoveryKeyInputError;
|
String? _recoveryKeyInputError;
|
||||||
|
|
||||||
bool _recoveryKeyInputLoading = false;
|
bool _recoveryKeyInputLoading = false;
|
||||||
|
|
||||||
String titleText;
|
String? titleText;
|
||||||
|
|
||||||
bool _recoveryKeyStored = false;
|
bool _recoveryKeyStored = false;
|
||||||
bool _recoveryKeyCopied = false;
|
bool _recoveryKeyCopied = false;
|
||||||
|
|
||||||
bool _wipe;
|
bool? _wipe;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -68,8 +68,8 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
_wipe = wipe;
|
_wipe = wipe;
|
||||||
titleText = null;
|
titleText = null;
|
||||||
_recoveryKeyStored = false;
|
_recoveryKeyStored = false;
|
||||||
bootstrap = widget.client.encryption
|
bootstrap =
|
||||||
.bootstrap(onUpdate: () => setState(() => null));
|
widget.client.encryption!.bootstrap(onUpdate: () => setState(() {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -79,12 +79,12 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
Widget body = PlatformInfos.isCupertinoStyle
|
Widget body = PlatformInfos.isCupertinoStyle
|
||||||
? const CupertinoActivityIndicator()
|
? const CupertinoActivityIndicator()
|
||||||
: const LinearProgressIndicator();
|
: const LinearProgressIndicator();
|
||||||
titleText = L10n.of(context).loadingPleaseWait;
|
titleText = L10n.of(context)!.loadingPleaseWait;
|
||||||
|
|
||||||
if (bootstrap.newSsssKey?.recoveryKey != null &&
|
if (bootstrap.newSsssKey?.recoveryKey != null &&
|
||||||
_recoveryKeyStored == false) {
|
_recoveryKeyStored == false) {
|
||||||
final key = bootstrap.newSsssKey.recoveryKey;
|
final key = bootstrap.newSsssKey!.recoveryKey;
|
||||||
titleText = L10n.of(context).securityKey;
|
titleText = L10n.of(context)!.securityKey;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
@ -92,7 +92,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: Navigator.of(context).pop,
|
onPressed: Navigator.of(context).pop,
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).securityKey),
|
title: Text(L10n.of(context)!.securityKey),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
@ -102,7 +102,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context).chatBackupDescription,
|
L10n.of(context)!.chatBackupDescription,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@ -119,9 +119,9 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
icon: const Icon(Icons.save_alt_outlined),
|
icon: const Icon(Icons.save_alt_outlined),
|
||||||
label: Text(L10n.of(context).saveTheSecurityKeyNow),
|
label: Text(L10n.of(context)!.saveTheSecurityKeyNow),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Share.share(key);
|
Share.share(key!);
|
||||||
setState(() => _recoveryKeyCopied = true);
|
setState(() => _recoveryKeyCopied = true);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -132,7 +132,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
onPrimary: Theme.of(context).primaryColor,
|
onPrimary: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
icon: const Icon(Icons.check_outlined),
|
icon: const Icon(Icons.check_outlined),
|
||||||
label: Text(L10n.of(context).next),
|
label: Text(L10n.of(context)!.next),
|
||||||
onPressed: _recoveryKeyCopied
|
onPressed: _recoveryKeyCopied
|
||||||
? () => setState(() => _recoveryKeyStored = true)
|
? () => setState(() => _recoveryKeyStored = true)
|
||||||
: null,
|
: null,
|
||||||
@ -147,27 +147,27 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
case BootstrapState.loading:
|
case BootstrapState.loading:
|
||||||
break;
|
break;
|
||||||
case BootstrapState.askWipeSsss:
|
case BootstrapState.askWipeSsss:
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance!.addPostFrameCallback(
|
||||||
(_) => bootstrap.wipeSsss(_wipe),
|
(_) => bootstrap.wipeSsss(_wipe!),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case BootstrapState.askBadSsss:
|
case BootstrapState.askBadSsss:
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance!.addPostFrameCallback(
|
||||||
(_) => bootstrap.ignoreBadSecrets(true),
|
(_) => bootstrap.ignoreBadSecrets(true),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case BootstrapState.askUseExistingSsss:
|
case BootstrapState.askUseExistingSsss:
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance!.addPostFrameCallback(
|
||||||
(_) => bootstrap.useExistingSsss(!_wipe),
|
(_) => bootstrap.useExistingSsss(!_wipe!),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case BootstrapState.askUnlockSsss:
|
case BootstrapState.askUnlockSsss:
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance!.addPostFrameCallback(
|
||||||
(_) => bootstrap.unlockedSsss(),
|
(_) => bootstrap.unlockedSsss(),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case BootstrapState.askNewSsss:
|
case BootstrapState.askNewSsss:
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance!.addPostFrameCallback(
|
||||||
(_) => bootstrap.newSsss(),
|
(_) => bootstrap.newSsss(),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@ -180,7 +180,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: Navigator.of(context).pop,
|
onPressed: Navigator.of(context).pop,
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).pleaseEnterSecurityKey),
|
title: Text(L10n.of(context)!.pleaseEnterSecurityKey),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
@ -190,7 +190,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context).pleaseEnterSecurityKeyDescription,
|
L10n.of(context)!.pleaseEnterSecurityKeyDescription,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@ -209,7 +209,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
controller: _recoveryKeyTextEditingController,
|
controller: _recoveryKeyTextEditingController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Abc123 Def456',
|
hintText: 'Abc123 Def456',
|
||||||
labelText: L10n.of(context).securityKey,
|
labelText: L10n.of(context)!.securityKey,
|
||||||
errorText: _recoveryKeyInputError,
|
errorText: _recoveryKeyInputError,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -218,7 +218,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
icon: _recoveryKeyInputLoading
|
icon: _recoveryKeyInputLoading
|
||||||
? const CircularProgressIndicator.adaptive()
|
? const CircularProgressIndicator.adaptive()
|
||||||
: const Icon(Icons.lock_open_outlined),
|
: const Icon(Icons.lock_open_outlined),
|
||||||
label: Text(L10n.of(context).unlockChatBackup),
|
label: Text(L10n.of(context)!.unlockChatBackup),
|
||||||
onPressed: _recoveryKeyInputLoading
|
onPressed: _recoveryKeyInputLoading
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
@ -229,11 +229,12 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
try {
|
try {
|
||||||
final key =
|
final key =
|
||||||
_recoveryKeyTextEditingController.text;
|
_recoveryKeyTextEditingController.text;
|
||||||
await bootstrap.newSsssKey.unlock(
|
await bootstrap.newSsssKey!.unlock(
|
||||||
keyOrPassphrase: key,
|
keyOrPassphrase: key,
|
||||||
);
|
);
|
||||||
Logs().d('SSSS unlocked');
|
Logs().d('SSSS unlocked');
|
||||||
await bootstrap.client.encryption.crossSigning
|
await bootstrap
|
||||||
|
.client.encryption!.crossSigning
|
||||||
.selfSign(
|
.selfSign(
|
||||||
keyOrPassphrase: key,
|
keyOrPassphrase: key,
|
||||||
);
|
);
|
||||||
@ -242,7 +243,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logs().w('Unable to unlock SSSS', e, s);
|
Logs().w('Unable to unlock SSSS', e, s);
|
||||||
setState(() => _recoveryKeyInputError =
|
setState(() => _recoveryKeyInputError =
|
||||||
L10n.of(context).oopsSomethingWentWrong);
|
L10n.of(context)!.oopsSomethingWentWrong);
|
||||||
} finally {
|
} finally {
|
||||||
setState(
|
setState(
|
||||||
() => _recoveryKeyInputLoading = false);
|
() => _recoveryKeyInputLoading = false);
|
||||||
@ -253,7 +254,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
const Expanded(child: Divider()),
|
const Expanded(child: Divider()),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Text(L10n.of(context).or),
|
child: Text(L10n.of(context)!.or),
|
||||||
),
|
),
|
||||||
const Expanded(child: Divider()),
|
const Expanded(child: Divider()),
|
||||||
]),
|
]),
|
||||||
@ -264,18 +265,18 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
onPrimary: Theme.of(context).primaryColor,
|
onPrimary: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
icon: const Icon(Icons.cast_connected_outlined),
|
icon: const Icon(Icons.cast_connected_outlined),
|
||||||
label: Text(L10n.of(context).transferFromAnotherDevice),
|
label: Text(L10n.of(context)!.transferFromAnotherDevice),
|
||||||
onPressed: _recoveryKeyInputLoading
|
onPressed: _recoveryKeyInputLoading
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
final req = await showFutureLoadingDialog(
|
final req = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => widget
|
future: () => widget.client
|
||||||
.client.userDeviceKeys[widget.client.userID]
|
.userDeviceKeys[widget.client.userID!]!
|
||||||
.startVerification(),
|
.startVerification(),
|
||||||
);
|
);
|
||||||
if (req.error != null) return;
|
if (req.error != null) return;
|
||||||
await KeyVerificationDialog(request: req.result)
|
await KeyVerificationDialog(request: req.result!)
|
||||||
.show(context);
|
.show(context);
|
||||||
Navigator.of(context, rootNavigator: false).pop();
|
Navigator.of(context, rootNavigator: false).pop();
|
||||||
},
|
},
|
||||||
@ -287,7 +288,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
onPrimary: Colors.red,
|
onPrimary: Colors.red,
|
||||||
),
|
),
|
||||||
icon: const Icon(Icons.delete_outlined),
|
icon: const Icon(Icons.delete_outlined),
|
||||||
label: Text(L10n.of(context).securityKeyLost),
|
label: Text(L10n.of(context)!.securityKeyLost),
|
||||||
onPressed: _recoveryKeyInputLoading
|
onPressed: _recoveryKeyInputLoading
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
@ -295,10 +296,10 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
await showOkCancelAlertDialog(
|
await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).securityKeyLost,
|
title: L10n.of(context)!.securityKeyLost,
|
||||||
message: L10n.of(context).wipeChatBackup,
|
message: L10n.of(context)!.wipeChatBackup,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
)) {
|
)) {
|
||||||
setState(() => _createBootstrap(true));
|
setState(() => _createBootstrap(true));
|
||||||
@ -311,12 +312,12 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
case BootstrapState.askWipeCrossSigning:
|
case BootstrapState.askWipeCrossSigning:
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance!.addPostFrameCallback(
|
||||||
(_) => bootstrap.wipeCrossSigning(_wipe),
|
(_) => bootstrap.wipeCrossSigning(_wipe!),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case BootstrapState.askSetupCrossSigning:
|
case BootstrapState.askSetupCrossSigning:
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance!.addPostFrameCallback(
|
||||||
(_) => bootstrap.askSetupCrossSigning(
|
(_) => bootstrap.askSetupCrossSigning(
|
||||||
setupMasterKey: true,
|
setupMasterKey: true,
|
||||||
setupSelfSigningKey: true,
|
setupSelfSigningKey: true,
|
||||||
@ -325,36 +326,36 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case BootstrapState.askWipeOnlineKeyBackup:
|
case BootstrapState.askWipeOnlineKeyBackup:
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance!.addPostFrameCallback(
|
||||||
(_) => bootstrap.wipeOnlineKeyBackup(_wipe),
|
(_) => bootstrap.wipeOnlineKeyBackup(_wipe!),
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case BootstrapState.askSetupOnlineKeyBackup:
|
case BootstrapState.askSetupOnlineKeyBackup:
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance!.addPostFrameCallback(
|
||||||
(_) => bootstrap.askSetupOnlineKeyBackup(true),
|
(_) => bootstrap.askSetupOnlineKeyBackup(true),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case BootstrapState.error:
|
case BootstrapState.error:
|
||||||
titleText = L10n.of(context).oopsSomethingWentWrong;
|
titleText = L10n.of(context)!.oopsSomethingWentWrong;
|
||||||
body = const Icon(Icons.error_outline, color: Colors.red, size: 40);
|
body = const Icon(Icons.error_outline, color: Colors.red, size: 40);
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).close,
|
label: L10n.of(context)!.close,
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
Navigator.of(context, rootNavigator: false).pop<bool>(false),
|
Navigator.of(context, rootNavigator: false).pop<bool>(false),
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
case BootstrapState.done:
|
case BootstrapState.done:
|
||||||
titleText = L10n.of(context).everythingReady;
|
titleText = L10n.of(context)!.everythingReady;
|
||||||
body = Column(
|
body = Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Image.asset('assets/backup.png', fit: BoxFit.contain),
|
Image.asset('assets/backup.png', fit: BoxFit.contain),
|
||||||
Text(L10n.of(context).yourChatBackupHasBeenSetUp),
|
Text(L10n.of(context)!.yourChatBackupHasBeenSetUp),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).close,
|
label: L10n.of(context)!.close,
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
Navigator.of(context, rootNavigator: false).pop<bool>(false),
|
Navigator.of(context, rootNavigator: false).pop<bool>(false),
|
||||||
));
|
));
|
||||||
@ -362,7 +363,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final title = Text(titleText);
|
final title = Text(titleText!);
|
||||||
if (PlatformInfos.isCupertinoStyle) {
|
if (PlatformInfos.isCupertinoStyle) {
|
||||||
return CupertinoAlertDialog(
|
return CupertinoAlertDialog(
|
||||||
title: title,
|
title: title,
|
||||||
|
@ -34,31 +34,31 @@ import 'send_location_dialog.dart';
|
|||||||
import 'sticker_picker_dialog.dart';
|
import 'sticker_picker_dialog.dart';
|
||||||
|
|
||||||
class Chat extends StatefulWidget {
|
class Chat extends StatefulWidget {
|
||||||
final Widget sideView;
|
final Widget? sideView;
|
||||||
|
|
||||||
const Chat({Key key, this.sideView}) : super(key: key);
|
const Chat({Key? key, this.sideView}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ChatController createState() => ChatController();
|
ChatController createState() => ChatController();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatController extends State<Chat> {
|
class ChatController extends State<Chat> {
|
||||||
Room room;
|
Room? room;
|
||||||
|
|
||||||
Client sendingClient;
|
Client? sendingClient;
|
||||||
|
|
||||||
Timeline timeline;
|
Timeline? timeline;
|
||||||
|
|
||||||
MatrixState matrix;
|
MatrixState? matrix;
|
||||||
|
|
||||||
String get roomId => context.vRouter.pathParameters['roomid'];
|
String? get roomId => context.vRouter.pathParameters['roomid'];
|
||||||
|
|
||||||
final AutoScrollController scrollController = AutoScrollController();
|
final AutoScrollController scrollController = AutoScrollController();
|
||||||
|
|
||||||
FocusNode inputFocus = FocusNode();
|
FocusNode inputFocus = FocusNode();
|
||||||
|
|
||||||
Timer typingCoolDown;
|
Timer? typingCoolDown;
|
||||||
Timer typingTimeout;
|
Timer? typingTimeout;
|
||||||
bool currentlyTyping = false;
|
bool currentlyTyping = false;
|
||||||
bool dragging = false;
|
bool dragging = false;
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ class ChatController extends State<Chat> {
|
|||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
name: xfile.name,
|
name: xfile.name,
|
||||||
).detectFileType,
|
).detectFileType,
|
||||||
room: room,
|
room: room!,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -96,13 +96,13 @@ class ChatController extends State<Chat> {
|
|||||||
|
|
||||||
List<Event> selectedEvents = [];
|
List<Event> selectedEvents = [];
|
||||||
|
|
||||||
List<Event> filteredEvents;
|
late List<Event> filteredEvents;
|
||||||
|
|
||||||
final Set<String> unfolded = {};
|
final Set<String> unfolded = {};
|
||||||
|
|
||||||
Event replyEvent;
|
Event? replyEvent;
|
||||||
|
|
||||||
Event editEvent;
|
Event? editEvent;
|
||||||
|
|
||||||
bool showScrollDownButton = false;
|
bool showScrollDownButton = false;
|
||||||
|
|
||||||
@ -115,8 +115,8 @@ class ChatController extends State<Chat> {
|
|||||||
String pendingText = '';
|
String pendingText = '';
|
||||||
|
|
||||||
bool get canLoadMore =>
|
bool get canLoadMore =>
|
||||||
timeline.events.isEmpty ||
|
timeline!.events.isEmpty ||
|
||||||
timeline.events.last.type != EventTypes.RoomCreate;
|
timeline!.events.last.type != EventTypes.RoomCreate;
|
||||||
|
|
||||||
bool showEmojiPicker = false;
|
bool showEmojiPicker = false;
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ class ChatController extends State<Chat> {
|
|||||||
|
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.sendEvent({
|
future: () => room!.sendEvent({
|
||||||
'msgtype': Matrix.callNamespace,
|
'msgtype': Matrix.callNamespace,
|
||||||
'body': url,
|
'body': url,
|
||||||
}));
|
}));
|
||||||
@ -137,12 +137,12 @@ class ChatController extends State<Chat> {
|
|||||||
void requestHistory() async {
|
void requestHistory() async {
|
||||||
if (canLoadMore) {
|
if (canLoadMore) {
|
||||||
try {
|
try {
|
||||||
await timeline.requestHistory(historyCount: _loadHistoryCount);
|
await timeline!.requestHistory(historyCount: _loadHistoryCount);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
(err as Object).toLocalizedString(context),
|
(err).toLocalizedString(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -157,8 +157,8 @@ class ChatController extends State<Chat> {
|
|||||||
}
|
}
|
||||||
if (scrollController.position.pixels ==
|
if (scrollController.position.pixels ==
|
||||||
scrollController.position.maxScrollExtent &&
|
scrollController.position.maxScrollExtent &&
|
||||||
timeline.events.isNotEmpty &&
|
timeline!.events.isNotEmpty &&
|
||||||
timeline.events[timeline.events.length - 1].type !=
|
timeline!.events[timeline!.events.length - 1].type !=
|
||||||
EventTypes.RoomCreate) {
|
EventTypes.RoomCreate) {
|
||||||
requestHistory();
|
requestHistory();
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ class ChatController extends State<Chat> {
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(
|
setState(
|
||||||
() {
|
() {
|
||||||
filteredEvents = timeline.getFilteredEvents(unfolded: unfolded);
|
filteredEvents = timeline!.getFilteredEvents(unfolded: unfolded);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -192,22 +192,22 @@ class ChatController extends State<Chat> {
|
|||||||
unfolded.add(filteredEvents[i].eventId);
|
unfolded.add(filteredEvents[i].eventId);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
filteredEvents = timeline.getFilteredEvents(unfolded: unfolded);
|
filteredEvents = timeline!.getFilteredEvents(unfolded: unfolded);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> getTimeline() async {
|
Future<bool> getTimeline() async {
|
||||||
if (timeline == null) {
|
if (timeline == null) {
|
||||||
timeline = await room.getTimeline(onUpdate: updateView);
|
timeline = await room!.getTimeline(onUpdate: updateView);
|
||||||
if (timeline.events.isNotEmpty) {
|
if (timeline!.events.isNotEmpty) {
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
if (room.markedUnread) room.markUnread(false);
|
if (room!.markedUnread) room!.markUnread(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when the scroll controller is attached we want to scroll to an event id, if specified
|
// when the scroll controller is attached we want to scroll to an event id, if specified
|
||||||
// and update the scroll controller...which will trigger a request history, if the
|
// and update the scroll controller...which will trigger a request history, if the
|
||||||
// "load more" button is visible on the screen
|
// "load more" button is visible on the screen
|
||||||
SchedulerBinding.instance.addPostFrameCallback((_) async {
|
SchedulerBinding.instance!.addPostFrameCallback((_) async {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
final event = VRouter.of(context).queryParameters['event'];
|
final event = VRouter.of(context).queryParameters['event'];
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
@ -217,16 +217,15 @@ class ChatController extends State<Chat> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
filteredEvents = timeline.getFilteredEvents(unfolded: unfolded);
|
filteredEvents = timeline!.getFilteredEvents(unfolded: unfolded);
|
||||||
timeline.requestKeys();
|
timeline!.requestKeys();
|
||||||
if (room.notificationCount != null &&
|
if (room!.notificationCount > 0 &&
|
||||||
room.notificationCount > 0 &&
|
|
||||||
timeline != null &&
|
timeline != null &&
|
||||||
timeline.events.isNotEmpty &&
|
timeline!.events.isNotEmpty &&
|
||||||
Matrix.of(context).webHasFocus) {
|
Matrix.of(context).webHasFocus) {
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
timeline.setReadMarker();
|
timeline!.setReadMarker();
|
||||||
room.client.updateIosBadge();
|
room!.client.updateIosBadge();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -240,13 +239,13 @@ class ChatController extends State<Chat> {
|
|||||||
|
|
||||||
TextEditingController sendController = TextEditingController();
|
TextEditingController sendController = TextEditingController();
|
||||||
|
|
||||||
void setSendingClient(Client c) {
|
void setSendingClient(Client? c) {
|
||||||
// first cancle typing with the old sending client
|
// first cancle typing with the old sending client
|
||||||
if (currentlyTyping) {
|
if (currentlyTyping) {
|
||||||
// no need to have the setting typing to false be blocking
|
// no need to have the setting typing to false be blocking
|
||||||
typingCoolDown?.cancel();
|
typingCoolDown?.cancel();
|
||||||
typingCoolDown = null;
|
typingCoolDown = null;
|
||||||
room.setTyping(false);
|
room!.setTyping(false);
|
||||||
currentlyTyping = false;
|
currentlyTyping = false;
|
||||||
}
|
}
|
||||||
// then set the new sending client
|
// then set the new sending client
|
||||||
@ -263,22 +262,22 @@ class ChatController extends State<Chat> {
|
|||||||
|
|
||||||
final commandMatch = RegExp(r'^\/(\w+)').firstMatch(sendController.text);
|
final commandMatch = RegExp(r'^\/(\w+)').firstMatch(sendController.text);
|
||||||
if (commandMatch != null &&
|
if (commandMatch != null &&
|
||||||
!room.client.commands.keys.contains(commandMatch[1].toLowerCase())) {
|
!room!.client.commands.keys.contains(commandMatch[1]!.toLowerCase())) {
|
||||||
final l10n = L10n.of(context);
|
final l10n = L10n.of(context)!;
|
||||||
final dialogResult = await showOkCancelAlertDialog(
|
final dialogResult = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
title: l10n.commandInvalid,
|
title: l10n.commandInvalid,
|
||||||
message: l10n.commandMissing(commandMatch[0]),
|
message: l10n.commandMissing(commandMatch[0]!),
|
||||||
okLabel: l10n.sendAsText,
|
okLabel: l10n.sendAsText,
|
||||||
cancelLabel: l10n.cancel,
|
cancelLabel: l10n.cancel,
|
||||||
);
|
);
|
||||||
if (dialogResult == null || dialogResult == OkCancelResult.cancel) return;
|
if (dialogResult == OkCancelResult.cancel) return;
|
||||||
parseCommands = false;
|
parseCommands = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
room.sendTextEvent(sendController.text,
|
room!.sendTextEvent(sendController.text,
|
||||||
inReplyTo: replyEvent,
|
inReplyTo: replyEvent,
|
||||||
editEventId: editEvent?.eventId,
|
editEventId: editEvent?.eventId,
|
||||||
parseCommands: parseCommands);
|
parseCommands: parseCommands);
|
||||||
@ -298,16 +297,16 @@ class ChatController extends State<Chat> {
|
|||||||
void sendFileAction() async {
|
void sendFileAction() async {
|
||||||
final result =
|
final result =
|
||||||
await FilePickerCross.importFromStorage(type: FileTypeCross.any);
|
await FilePickerCross.importFromStorage(type: FileTypeCross.any);
|
||||||
if (result == null) return;
|
if (result.fileName == null) return;
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
builder: (c) => SendFileDialog(
|
builder: (c) => SendFileDialog(
|
||||||
file: MatrixFile(
|
file: MatrixFile(
|
||||||
bytes: result.toUint8List(),
|
bytes: result.toUint8List(),
|
||||||
name: result.fileName,
|
name: result.fileName!,
|
||||||
).detectFileType,
|
).detectFileType,
|
||||||
room: room,
|
room: room!,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -315,16 +314,16 @@ class ChatController extends State<Chat> {
|
|||||||
void sendImageAction() async {
|
void sendImageAction() async {
|
||||||
final result =
|
final result =
|
||||||
await FilePickerCross.importFromStorage(type: FileTypeCross.image);
|
await FilePickerCross.importFromStorage(type: FileTypeCross.image);
|
||||||
if (result == null) return;
|
if (result.fileName == null) return;
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
builder: (c) => SendFileDialog(
|
builder: (c) => SendFileDialog(
|
||||||
file: MatrixImageFile(
|
file: MatrixImageFile(
|
||||||
bytes: result.toUint8List(),
|
bytes: result.toUint8List(),
|
||||||
name: result.fileName,
|
name: result.fileName!,
|
||||||
),
|
),
|
||||||
room: room,
|
room: room!,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -343,7 +342,7 @@ class ChatController extends State<Chat> {
|
|||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
name: file.path,
|
name: file.path,
|
||||||
),
|
),
|
||||||
room: room,
|
room: room!,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -362,7 +361,7 @@ class ChatController extends State<Chat> {
|
|||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
name: file.path,
|
name: file.path,
|
||||||
),
|
),
|
||||||
room: room,
|
room: room!,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -371,7 +370,7 @@ class ChatController extends State<Chat> {
|
|||||||
final sticker = await showModalBottomSheet<ImagePackImageContent>(
|
final sticker = await showModalBottomSheet<ImagePackImageContent>(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
builder: (c) => StickerPickerDialog(room: room),
|
builder: (c) => StickerPickerDialog(room: room!),
|
||||||
);
|
);
|
||||||
if (sticker == null) return;
|
if (sticker == null) return;
|
||||||
final eventContent = <String, dynamic>{
|
final eventContent = <String, dynamic>{
|
||||||
@ -382,7 +381,7 @@ class ChatController extends State<Chat> {
|
|||||||
// send the sticker
|
// send the sticker
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.sendEvent(
|
future: () => room!.sendEvent(
|
||||||
eventContent,
|
eventContent,
|
||||||
type: EventTypes.Sticker,
|
type: EventTypes.Sticker,
|
||||||
),
|
),
|
||||||
@ -405,7 +404,7 @@ class ChatController extends State<Chat> {
|
|||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () =>
|
future: () =>
|
||||||
room.sendFileEvent(file, inReplyTo: replyEvent, extraContent: {
|
room!.sendFileEvent(file, inReplyTo: replyEvent, extraContent: {
|
||||||
'info': {
|
'info': {
|
||||||
...file.info,
|
...file.info,
|
||||||
'duration': result.duration,
|
'duration': result.duration,
|
||||||
@ -426,7 +425,7 @@ class ChatController extends State<Chat> {
|
|||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
builder: (c) => SendLocationDialog(room: room),
|
builder: (c) => SendLocationDialog(room: room!),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,13 +433,13 @@ class ChatController extends State<Chat> {
|
|||||||
var copyString = '';
|
var copyString = '';
|
||||||
if (selectedEvents.length == 1) {
|
if (selectedEvents.length == 1) {
|
||||||
return selectedEvents.first
|
return selectedEvents.first
|
||||||
.getDisplayEvent(timeline)
|
.getDisplayEvent(timeline!)
|
||||||
.getLocalizedBody(MatrixLocals(L10n.of(context)));
|
.getLocalizedBody(MatrixLocals(L10n.of(context)!));
|
||||||
}
|
}
|
||||||
for (final event in selectedEvents) {
|
for (final event in selectedEvents) {
|
||||||
if (copyString.isNotEmpty) copyString += '\n\n';
|
if (copyString.isNotEmpty) copyString += '\n\n';
|
||||||
copyString += event.getDisplayEvent(timeline).getLocalizedBody(
|
copyString += event.getDisplayEvent(timeline!).getLocalizedBody(
|
||||||
MatrixLocals(L10n.of(context)),
|
MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: true);
|
withSenderNamePrefix: true);
|
||||||
}
|
}
|
||||||
return copyString;
|
return copyString;
|
||||||
@ -458,37 +457,37 @@ class ChatController extends State<Chat> {
|
|||||||
final event = selectedEvents.single;
|
final event = selectedEvents.single;
|
||||||
final score = await showConfirmationDialog<int>(
|
final score = await showConfirmationDialog<int>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).reportMessage,
|
title: L10n.of(context)!.reportMessage,
|
||||||
message: L10n.of(context).howOffensiveIsThisContent,
|
message: L10n.of(context)!.howOffensiveIsThisContent,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
actions: [
|
actions: [
|
||||||
AlertDialogAction(
|
AlertDialogAction(
|
||||||
key: -100,
|
key: -100,
|
||||||
label: L10n.of(context).extremeOffensive,
|
label: L10n.of(context)!.extremeOffensive,
|
||||||
),
|
),
|
||||||
AlertDialogAction(
|
AlertDialogAction(
|
||||||
key: -50,
|
key: -50,
|
||||||
label: L10n.of(context).offensive,
|
label: L10n.of(context)!.offensive,
|
||||||
),
|
),
|
||||||
AlertDialogAction(
|
AlertDialogAction(
|
||||||
key: 0,
|
key: 0,
|
||||||
label: L10n.of(context).inoffensive,
|
label: L10n.of(context)!.inoffensive,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
if (score == null) return;
|
if (score == null) return;
|
||||||
final reason = await showTextInputDialog(
|
final reason = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).whyDoYouWantToReportThis,
|
title: L10n.of(context)!.whyDoYouWantToReportThis,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [DialogTextField(hintText: L10n.of(context).reason)]);
|
textFields: [DialogTextField(hintText: L10n.of(context)!.reason)]);
|
||||||
if (reason == null || reason.single.isEmpty) return;
|
if (reason == null || reason.single.isEmpty) return;
|
||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context).client.reportContent(
|
future: () => Matrix.of(context).client.reportContent(
|
||||||
event.roomId,
|
event.roomId!,
|
||||||
event.eventId,
|
event.eventId,
|
||||||
reason: reason.single,
|
reason: reason.single,
|
||||||
score: score,
|
score: score,
|
||||||
@ -500,16 +499,16 @@ class ChatController extends State<Chat> {
|
|||||||
selectedEvents.clear();
|
selectedEvents.clear();
|
||||||
});
|
});
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).contentHasBeenReported)));
|
SnackBar(content: Text(L10n.of(context)!.contentHasBeenReported)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void redactEventsAction() async {
|
void redactEventsAction() async {
|
||||||
final confirmed = await showOkCancelAlertDialog(
|
final confirmed = await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).messageWillBeRemovedWarning,
|
title: L10n.of(context)!.messageWillBeRemovedWarning,
|
||||||
okLabel: L10n.of(context).remove,
|
okLabel: L10n.of(context)!.remove,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.ok;
|
OkCancelResult.ok;
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
@ -522,12 +521,12 @@ class ChatController extends State<Chat> {
|
|||||||
await event.redactEvent();
|
await event.redactEvent();
|
||||||
} else {
|
} else {
|
||||||
final client = currentRoomBundle.firstWhere(
|
final client = currentRoomBundle.firstWhere(
|
||||||
(cl) => selectedEvents.first.senderId == cl.userID,
|
(cl) => selectedEvents.first.senderId == cl!.userID,
|
||||||
orElse: () => null);
|
orElse: () => null);
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final room = client.getRoomById(roomId);
|
final room = client.getRoomById(roomId!)!;
|
||||||
await Event.fromJson(event.toJson(), room).redactEvent();
|
await Event.fromJson(event.toJson(), room).redactEvent();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -541,17 +540,17 @@ class ChatController extends State<Chat> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Client> get currentRoomBundle {
|
List<Client?> get currentRoomBundle {
|
||||||
final clients = matrix.currentBundle;
|
final clients = matrix!.currentBundle!;
|
||||||
clients.removeWhere((c) => c.getRoomById(roomId) == null);
|
clients.removeWhere((c) => c!.getRoomById(roomId!) == null);
|
||||||
return clients;
|
return clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get canRedactSelectedEvents {
|
bool get canRedactSelectedEvents {
|
||||||
final clients = matrix.currentBundle;
|
final clients = matrix!.currentBundle;
|
||||||
for (final event in selectedEvents) {
|
for (final event in selectedEvents) {
|
||||||
if (event.canRedact == false &&
|
if (event.canRedact == false &&
|
||||||
!(clients.any((cl) => event.senderId == cl.userID))) return false;
|
!(clients!.any((cl) => event.senderId == cl!.userID))) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -561,7 +560,7 @@ class ChatController extends State<Chat> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return currentRoomBundle
|
return currentRoomBundle
|
||||||
.any((cl) => selectedEvents.first.senderId == cl.userID);
|
.any((cl) => selectedEvents.first.senderId == cl!.userID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void forwardEventsAction() async {
|
void forwardEventsAction() async {
|
||||||
@ -583,7 +582,7 @@ class ChatController extends State<Chat> {
|
|||||||
event.sendAgain();
|
event.sendAgain();
|
||||||
}
|
}
|
||||||
final allEditEvents = event
|
final allEditEvents = event
|
||||||
.aggregatedEvents(timeline, RelationshipTypes.edit)
|
.aggregatedEvents(timeline!, RelationshipTypes.edit)
|
||||||
.where((e) => e.status.isError);
|
.where((e) => e.status.isError);
|
||||||
for (final e in allEditEvents) {
|
for (final e in allEditEvents) {
|
||||||
e.sendAgain();
|
e.sendAgain();
|
||||||
@ -591,7 +590,7 @@ class ChatController extends State<Chat> {
|
|||||||
setState(() => selectedEvents.clear());
|
setState(() => selectedEvents.clear());
|
||||||
}
|
}
|
||||||
|
|
||||||
void replyAction({Event replyTo}) {
|
void replyAction({Event? replyTo}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
replyEvent = replyTo ?? selectedEvents.first;
|
replyEvent = replyTo ?? selectedEvents.first;
|
||||||
selectedEvents.clear();
|
selectedEvents.clear();
|
||||||
@ -609,7 +608,7 @@ class ChatController extends State<Chat> {
|
|||||||
future: () async {
|
future: () async {
|
||||||
// okay, we first have to fetch if the event is in the room
|
// okay, we first have to fetch if the event is in the room
|
||||||
try {
|
try {
|
||||||
final event = await timeline.getEventById(eventId);
|
final event = await timeline!.getEventById(eventId);
|
||||||
if (event == null) {
|
if (event == null) {
|
||||||
// event is null...meaning something is off
|
// event is null...meaning something is off
|
||||||
return;
|
return;
|
||||||
@ -628,7 +627,7 @@ class ChatController extends State<Chat> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await timeline.requestHistory(historyCount: _loadHistoryCount);
|
await timeline!.requestHistory(historyCount: _loadHistoryCount);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err is TimeoutException) {
|
if (err is TimeoutException) {
|
||||||
// loading the history timed out...so let's do nothing
|
// loading the history timed out...so let's do nothing
|
||||||
@ -662,7 +661,7 @@ class ChatController extends State<Chat> {
|
|||||||
return sendEmojiAction(emoji.emoji);
|
return sendEmojiAction(emoji.emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<Event> _allReactionEvents;
|
late Iterable<Event> _allReactionEvents;
|
||||||
|
|
||||||
void cancelEmojiPicker() => setState(() => showEmojiPicker = false);
|
void cancelEmojiPicker() => setState(() => showEmojiPicker = false);
|
||||||
|
|
||||||
@ -671,13 +670,13 @@ class ChatController extends State<Chat> {
|
|||||||
setState(() => showEmojiPicker = true);
|
setState(() => showEmojiPicker = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendEmojiAction(String emoji) async {
|
void sendEmojiAction(String? emoji) async {
|
||||||
final events = List<Event>.from(selectedEvents);
|
final events = List<Event>.from(selectedEvents);
|
||||||
setState(() => selectedEvents.clear());
|
setState(() => selectedEvents.clear());
|
||||||
for (final event in events) {
|
for (final event in events) {
|
||||||
await room.sendReaction(
|
await room!.sendReaction(
|
||||||
event.eventId,
|
event.eventId,
|
||||||
emoji,
|
emoji!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -695,7 +694,7 @@ class ChatController extends State<Chat> {
|
|||||||
|
|
||||||
void editSelectedEventAction() {
|
void editSelectedEventAction() {
|
||||||
final client = currentRoomBundle.firstWhere(
|
final client = currentRoomBundle.firstWhere(
|
||||||
(cl) => selectedEvents.first.senderId == cl.userID,
|
(cl) => selectedEvents.first.senderId == cl!.userID,
|
||||||
orElse: () => null);
|
orElse: () => null);
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
return;
|
return;
|
||||||
@ -704,9 +703,9 @@ class ChatController extends State<Chat> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
pendingText = sendController.text;
|
pendingText = sendController.text;
|
||||||
editEvent = selectedEvents.first;
|
editEvent = selectedEvents.first;
|
||||||
inputText = sendController.text = editEvent
|
inputText = sendController.text = editEvent!
|
||||||
.getDisplayEvent(timeline)
|
.getDisplayEvent(timeline!)
|
||||||
.getLocalizedBody(MatrixLocals(L10n.of(context)),
|
.getLocalizedBody(MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: false, hideReply: true);
|
withSenderNamePrefix: false, hideReply: true);
|
||||||
selectedEvents.clear();
|
selectedEvents.clear();
|
||||||
});
|
});
|
||||||
@ -718,29 +717,29 @@ class ChatController extends State<Chat> {
|
|||||||
await showOkCancelAlertDialog(
|
await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).goToTheNewRoom,
|
title: L10n.of(context)!.goToTheNewRoom,
|
||||||
message: room
|
message: room!
|
||||||
.getState(EventTypes.RoomTombstone)
|
.getState(EventTypes.RoomTombstone)!
|
||||||
.parsedTombstoneContent
|
.parsedTombstoneContent
|
||||||
.body,
|
.body,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
)) {
|
)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.client.joinRoom(room
|
future: () => room!.client.joinRoom(room!
|
||||||
.getState(EventTypes.RoomTombstone)
|
.getState(EventTypes.RoomTombstone)!
|
||||||
.parsedTombstoneContent
|
.parsedTombstoneContent
|
||||||
.replacementRoom),
|
.replacementRoom),
|
||||||
);
|
);
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: room.leave,
|
future: room!.leave,
|
||||||
);
|
);
|
||||||
if (result.error == null) {
|
if (result.error == null) {
|
||||||
VRouter.of(context).toSegments(['rooms', result.result]);
|
VRouter.of(context).toSegments(['rooms', result.result!]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -761,12 +760,12 @@ class ChatController extends State<Chat> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int findChildIndexCallback(Key key, Map<String, int> thisEventsKeyMap) {
|
int? findChildIndexCallback(Key key, Map<String, int> thisEventsKeyMap) {
|
||||||
// this method is called very often. As such, it has to be optimized for speed.
|
// this method is called very often. As such, it has to be optimized for speed.
|
||||||
if (key is! ValueKey) {
|
if (key is! ValueKey) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final eventId = (key as ValueKey).value;
|
final eventId = key.value;
|
||||||
if (eventId is! String) {
|
if (eventId is! String) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -809,11 +808,11 @@ class ChatController extends State<Chat> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onInputBarChanged(String text) {
|
void onInputBarChanged(String text) {
|
||||||
if (text.endsWith(' ') && matrix.hasComplexBundles) {
|
if (text.endsWith(' ') && matrix!.hasComplexBundles) {
|
||||||
final clients = currentRoomBundle;
|
final clients = currentRoomBundle;
|
||||||
for (final client in clients) {
|
for (final client in clients) {
|
||||||
final prefix = client.sendPrefix;
|
final prefix = client!.sendPrefix;
|
||||||
if ((prefix?.isNotEmpty ?? false) &&
|
if ((prefix.isNotEmpty) &&
|
||||||
text.toLowerCase() == '${prefix.toLowerCase()} ') {
|
text.toLowerCase() == '${prefix.toLowerCase()} ') {
|
||||||
setSendingClient(client);
|
setSendingClient(client);
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -828,7 +827,7 @@ class ChatController extends State<Chat> {
|
|||||||
typingCoolDown = Timer(const Duration(seconds: 2), () {
|
typingCoolDown = Timer(const Duration(seconds: 2), () {
|
||||||
typingCoolDown = null;
|
typingCoolDown = null;
|
||||||
currentlyTyping = false;
|
currentlyTyping = false;
|
||||||
room.setTyping(false);
|
room!.setTyping(false);
|
||||||
});
|
});
|
||||||
typingTimeout ??= Timer(const Duration(seconds: 30), () {
|
typingTimeout ??= Timer(const Duration(seconds: 30), () {
|
||||||
typingTimeout = null;
|
typingTimeout = null;
|
||||||
@ -836,12 +835,13 @@ class ChatController extends State<Chat> {
|
|||||||
});
|
});
|
||||||
if (!currentlyTyping) {
|
if (!currentlyTyping) {
|
||||||
currentlyTyping = true;
|
currentlyTyping = true;
|
||||||
room.setTyping(true, timeout: const Duration(seconds: 30).inMilliseconds);
|
room!
|
||||||
|
.setTyping(true, timeout: const Duration(seconds: 30).inMilliseconds);
|
||||||
}
|
}
|
||||||
setState(() => inputText = text);
|
setState(() => inputText = text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showEventInfo([Event event]) =>
|
void showEventInfo([Event? event]) =>
|
||||||
(event ?? selectedEvents.single).showInfoDialog(context);
|
(event ?? selectedEvents.single).showInfoDialog(context);
|
||||||
|
|
||||||
void cancelReplyEventAction() => setState(() {
|
void cancelReplyEventAction() => setState(() {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
@ -19,41 +17,42 @@ class ChatAppBarTitle extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final room = controller.room;
|
||||||
|
if (room == null) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
if (controller.selectedEvents.isNotEmpty) {
|
if (controller.selectedEvents.isNotEmpty) {
|
||||||
return Text(controller.selectedEvents.length.toString());
|
return Text(controller.selectedEvents.length.toString());
|
||||||
}
|
}
|
||||||
final directChatMatrixID = controller.room.directChatMatrixID;
|
final directChatMatrixID = room.directChatMatrixID;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: Avatar(
|
leading: Avatar(
|
||||||
mxContent: controller.room.avatar,
|
mxContent: room.avatar,
|
||||||
name: controller.room.displayname,
|
name: room.displayname,
|
||||||
),
|
),
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
onTap: directChatMatrixID != null
|
onTap: directChatMatrixID != null
|
||||||
? () => showModalBottomSheet(
|
? () => showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => UserBottomSheet(
|
builder: (c) => UserBottomSheet(
|
||||||
user: controller.room.getUserByMXIDSync(directChatMatrixID),
|
user: room.getUserByMXIDSync(directChatMatrixID),
|
||||||
outerContext: context,
|
outerContext: context,
|
||||||
onMention: () => controller.sendController.text +=
|
onMention: () => controller.sendController.text +=
|
||||||
'${controller.room.getUserByMXIDSync(directChatMatrixID).mention} ',
|
'${room.getUserByMXIDSync(directChatMatrixID).mention} ',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: () => VRouter.of(context)
|
: () => VRouter.of(context).toSegments(['rooms', room.id, 'details']),
|
||||||
.toSegments(['rooms', controller.room.id, 'details']),
|
title: Text(room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||||
title: Text(
|
|
||||||
controller.room
|
|
||||||
.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
|
||||||
maxLines: 1),
|
maxLines: 1),
|
||||||
subtitle: StreamBuilder<Object>(
|
subtitle: StreamBuilder<Object>(
|
||||||
stream: Matrix.of(context)
|
stream: Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.onPresence
|
.onPresence
|
||||||
.stream
|
.stream
|
||||||
.where((p) => p.senderId == controller.room.directChatMatrixID)
|
.where((p) => p.senderId == room.directChatMatrixID)
|
||||||
.rateLimit(const Duration(seconds: 1)),
|
.rateLimit(const Duration(seconds: 1)),
|
||||||
builder: (context, snapshot) => Text(
|
builder: (context, snapshot) => Text(
|
||||||
controller.room.getLocalizedStatus(context),
|
room.getLocalizedStatus(context),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
//overflow: TextOverflow.ellipsis,
|
//overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
||||||
|
@ -13,7 +13,7 @@ import 'input_bar.dart';
|
|||||||
|
|
||||||
class ChatInputRow extends StatelessWidget {
|
class ChatInputRow extends StatelessWidget {
|
||||||
final ChatController controller;
|
final ChatController controller;
|
||||||
const ChatInputRow(this.controller, {Key key}) : super(key: key);
|
const ChatInputRow(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -30,14 +30,14 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(Icons.keyboard_arrow_left_outlined),
|
const Icon(Icons.keyboard_arrow_left_outlined),
|
||||||
Text(L10n.of(context).forward),
|
Text(L10n.of(context)!.forward),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
controller.selectedEvents.length == 1
|
controller.selectedEvents.length == 1
|
||||||
? controller.selectedEvents.first
|
? controller.selectedEvents.first
|
||||||
.getDisplayEvent(controller.timeline)
|
.getDisplayEvent(controller.timeline!)
|
||||||
.status
|
.status
|
||||||
.isSent
|
.isSent
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
@ -46,7 +46,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
onPressed: controller.replyAction,
|
onPressed: controller.replyAction,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(L10n.of(context).reply),
|
Text(L10n.of(context)!.reply),
|
||||||
const Icon(Icons.keyboard_arrow_right),
|
const Icon(Icons.keyboard_arrow_right),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -58,7 +58,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
onPressed: controller.sendAgainAction,
|
onPressed: controller.sendAgainAction,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(L10n.of(context).tryToSendAgain),
|
Text(L10n.of(context)!.tryToSendAgain),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
const Icon(Icons.send_outlined, size: 16),
|
const Icon(Icons.send_outlined, size: 16),
|
||||||
],
|
],
|
||||||
@ -88,7 +88,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
child: Icon(Icons.video_call_outlined),
|
child: Icon(Icons.video_call_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).videoCall),
|
title: Text(L10n.of(context)!.videoCall),
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -100,7 +100,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
child: Icon(Icons.attachment_outlined),
|
child: Icon(Icons.attachment_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).sendFile),
|
title: Text(L10n.of(context)!.sendFile),
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -112,7 +112,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
child: Icon(Icons.image_outlined),
|
child: Icon(Icons.image_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).sendImage),
|
title: Text(L10n.of(context)!.sendImage),
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -125,7 +125,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
child: Icon(Icons.camera_alt_outlined),
|
child: Icon(Icons.camera_alt_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).openCamera),
|
title: Text(L10n.of(context)!.openCamera),
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -138,11 +138,11 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
child: Icon(Icons.videocam_outlined),
|
child: Icon(Icons.videocam_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).openVideoCamera),
|
title: Text(L10n.of(context)!.openVideoCamera),
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (controller.room
|
if (controller.room!
|
||||||
.getImagePacks(ImagePackUsage.sticker)
|
.getImagePacks(ImagePackUsage.sticker)
|
||||||
.isNotEmpty)
|
.isNotEmpty)
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
@ -153,7 +153,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
child: Icon(Icons.emoji_emotions_outlined),
|
child: Icon(Icons.emoji_emotions_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).sendSticker),
|
title: Text(L10n.of(context)!.sendSticker),
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -166,7 +166,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
child: Icon(Icons.gps_fixed_outlined),
|
child: Icon(Icons.gps_fixed_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).shareLocation),
|
title: Text(L10n.of(context)!.shareLocation),
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -176,11 +176,11 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
height: 56,
|
height: 56,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: EncryptionButton(controller.room),
|
child: EncryptionButton(controller.room!),
|
||||||
),
|
),
|
||||||
if (controller.matrix.isMultiAccount &&
|
if (controller.matrix!.isMultiAccount &&
|
||||||
controller.matrix.hasComplexBundles &&
|
controller.matrix!.hasComplexBundles &&
|
||||||
controller.matrix.currentBundle.length > 1)
|
controller.matrix!.currentBundle!.length > 1)
|
||||||
Container(
|
Container(
|
||||||
height: 56,
|
height: 56,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
@ -190,7 +190,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||||
child: InputBar(
|
child: InputBar(
|
||||||
room: controller.room,
|
room: controller.room!,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
maxLines: 8,
|
maxLines: 8,
|
||||||
autofocus: !PlatformInfos.isMobile,
|
autofocus: !PlatformInfos.isMobile,
|
||||||
@ -201,7 +201,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
focusNode: controller.inputFocus,
|
focusNode: controller.inputFocus,
|
||||||
controller: controller.sendController,
|
controller: controller.sendController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: L10n.of(context).writeAMessage,
|
hintText: L10n.of(context)!.writeAMessage,
|
||||||
hintMaxLines: 1,
|
hintMaxLines: 1,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
enabledBorder: InputBorder.none,
|
enabledBorder: InputBorder.none,
|
||||||
@ -216,7 +216,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
height: 56,
|
height: 56,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: L10n.of(context).voiceMessage,
|
tooltip: L10n.of(context)!.voiceMessage,
|
||||||
icon: const Icon(Icons.mic_none_outlined),
|
icon: const Icon(Icons.mic_none_outlined),
|
||||||
onPressed: controller.voiceMessageAction,
|
onPressed: controller.voiceMessageAction,
|
||||||
),
|
),
|
||||||
@ -228,7 +228,7 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: const Icon(Icons.send_outlined),
|
icon: const Icon(Icons.send_outlined),
|
||||||
onPressed: controller.send,
|
onPressed: controller.send,
|
||||||
tooltip: L10n.of(context).send,
|
tooltip: L10n.of(context)!.send,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -239,11 +239,11 @@ class ChatInputRow extends StatelessWidget {
|
|||||||
class _ChatAccountPicker extends StatelessWidget {
|
class _ChatAccountPicker extends StatelessWidget {
|
||||||
final ChatController controller;
|
final ChatController controller;
|
||||||
|
|
||||||
const _ChatAccountPicker(this.controller, {Key key}) : super(key: key);
|
const _ChatAccountPicker(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
void _popupMenuButtonSelected(String mxid) {
|
void _popupMenuButtonSelected(String mxid) {
|
||||||
final client = controller.matrix.currentBundle
|
final client = controller.matrix!.currentBundle!
|
||||||
.firstWhere((cl) => cl.userID == mxid, orElse: () => null);
|
.firstWhere((cl) => cl!.userID == mxid, orElse: () => null);
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
Logs().w('Attempted to switch to a non-existing client $mxid');
|
Logs().w('Attempted to switch to a non-existing client $mxid');
|
||||||
return;
|
return;
|
||||||
@ -258,23 +258,23 @@ class _ChatAccountPicker extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: FutureBuilder<Profile>(
|
child: FutureBuilder<Profile>(
|
||||||
future: controller.sendingClient.ownProfile,
|
future: controller.sendingClient!.ownProfile,
|
||||||
builder: (context, snapshot) => PopupMenuButton<String>(
|
builder: (context, snapshot) => PopupMenuButton<String>(
|
||||||
onSelected: _popupMenuButtonSelected,
|
onSelected: _popupMenuButtonSelected,
|
||||||
itemBuilder: (BuildContext context) => clients
|
itemBuilder: (BuildContext context) => clients
|
||||||
.map((client) => PopupMenuItem<String>(
|
.map((client) => PopupMenuItem<String>(
|
||||||
value: client.userID,
|
value: client!.userID,
|
||||||
child: FutureBuilder<Profile>(
|
child: FutureBuilder<Profile>(
|
||||||
future: client.ownProfile,
|
future: client.ownProfile,
|
||||||
builder: (context, snapshot) => ListTile(
|
builder: (context, snapshot) => ListTile(
|
||||||
leading: Avatar(
|
leading: Avatar(
|
||||||
mxContent: snapshot.data?.avatarUrl,
|
mxContent: snapshot.data?.avatarUrl,
|
||||||
name: snapshot.data?.displayName ??
|
name: snapshot.data?.displayName ??
|
||||||
client.userID.localpart,
|
client.userID!.localpart,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
title:
|
title:
|
||||||
Text(snapshot.data?.displayName ?? client.userID),
|
Text(snapshot.data?.displayName ?? client.userID!),
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -283,7 +283,7 @@ class _ChatAccountPicker extends StatelessWidget {
|
|||||||
child: Avatar(
|
child: Avatar(
|
||||||
mxContent: snapshot.data?.avatarUrl,
|
mxContent: snapshot.data?.avatarUrl,
|
||||||
name: snapshot.data?.displayName ??
|
name: snapshot.data?.displayName ??
|
||||||
controller.matrix.client.userID.localpart,
|
controller.matrix!.client.userID!.localpart,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -34,31 +34,31 @@ enum _EventContextAction { info, report }
|
|||||||
class ChatView extends StatelessWidget {
|
class ChatView extends StatelessWidget {
|
||||||
final ChatController controller;
|
final ChatController controller;
|
||||||
|
|
||||||
const ChatView(this.controller, {Key key}) : super(key: key);
|
const ChatView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
List<Widget> _appBarActions(BuildContext context) => controller.selectMode
|
List<Widget> _appBarActions(BuildContext context) => controller.selectMode
|
||||||
? [
|
? [
|
||||||
if (controller.canEditSelectedEvents)
|
if (controller.canEditSelectedEvents)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.edit_outlined),
|
icon: const Icon(Icons.edit_outlined),
|
||||||
tooltip: L10n.of(context).edit,
|
tooltip: L10n.of(context)!.edit,
|
||||||
onPressed: controller.editSelectedEventAction,
|
onPressed: controller.editSelectedEventAction,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.copy_outlined),
|
icon: const Icon(Icons.copy_outlined),
|
||||||
tooltip: L10n.of(context).copy,
|
tooltip: L10n.of(context)!.copy,
|
||||||
onPressed: controller.copyEventsAction,
|
onPressed: controller.copyEventsAction,
|
||||||
),
|
),
|
||||||
if (controller.canSaveSelectedEvent)
|
if (controller.canSaveSelectedEvent)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.adaptive.share),
|
icon: Icon(Icons.adaptive.share),
|
||||||
tooltip: L10n.of(context).share,
|
tooltip: L10n.of(context)!.share,
|
||||||
onPressed: controller.saveSelectedEvent,
|
onPressed: controller.saveSelectedEvent,
|
||||||
),
|
),
|
||||||
if (controller.canRedactSelectedEvents)
|
if (controller.canRedactSelectedEvents)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.delete_outlined),
|
icon: const Icon(Icons.delete_outlined),
|
||||||
tooltip: L10n.of(context).redactMessage,
|
tooltip: L10n.of(context)!.redactMessage,
|
||||||
onPressed: controller.redactEventsAction,
|
onPressed: controller.redactEventsAction,
|
||||||
),
|
),
|
||||||
if (controller.selectedEvents.length == 1)
|
if (controller.selectedEvents.length == 1)
|
||||||
@ -82,7 +82,7 @@ class ChatView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.info_outlined),
|
const Icon(Icons.info_outlined),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(L10n.of(context).messageInfo),
|
Text(L10n.of(context)!.messageInfo),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -96,7 +96,7 @@ class ChatView extends StatelessWidget {
|
|||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(L10n.of(context).reportMessage),
|
Text(L10n.of(context)!.reportMessage),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -104,29 +104,30 @@ class ChatView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
ChatSettingsPopupMenu(controller.room, !controller.room.isDirectChat),
|
ChatSettingsPopupMenu(
|
||||||
|
controller.room!, !controller.room!.isDirectChat),
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
controller.matrix ??= Matrix.of(context);
|
controller.matrix ??= Matrix.of(context);
|
||||||
final client = controller.matrix.client;
|
final client = controller.matrix!.client;
|
||||||
controller.sendingClient ??= client;
|
controller.sendingClient ??= client;
|
||||||
controller.room = controller.sendingClient.getRoomById(controller.roomId);
|
controller.room = controller.sendingClient!.getRoomById(controller.roomId!);
|
||||||
if (controller.room == null) {
|
if (controller.room == null) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(L10n.of(context).oopsSomethingWentWrong),
|
title: Text(L10n.of(context)!.oopsSomethingWentWrong),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
|
child: Text(L10n.of(context)!.youAreNoLongerParticipatingInThisChat),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controller.room.membership == Membership.invite) {
|
if (controller.room!.membership == Membership.invite) {
|
||||||
showFutureLoadingDialog(
|
showFutureLoadingDialog(
|
||||||
context: context, future: () => controller.room.join());
|
context: context, future: () => controller.room!.join());
|
||||||
}
|
}
|
||||||
final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0;
|
final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0;
|
||||||
final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
|
final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
|
||||||
@ -139,7 +140,7 @@ class ChatView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: StreamBuilder(
|
child: StreamBuilder(
|
||||||
stream: controller.room.onUpdate.stream
|
stream: controller.room!.onUpdate.stream
|
||||||
.rateLimit(const Duration(milliseconds: 250)),
|
.rateLimit(const Duration(milliseconds: 250)),
|
||||||
builder: (context, snapshot) => Scaffold(
|
builder: (context, snapshot) => Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@ -152,10 +153,10 @@ class ChatView extends StatelessWidget {
|
|||||||
? IconButton(
|
? IconButton(
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: controller.clearSelectedEvents,
|
onPressed: controller.clearSelectedEvents,
|
||||||
tooltip: L10n.of(context).close,
|
tooltip: L10n.of(context)!.close,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
)
|
)
|
||||||
: UnreadBadgeBackButton(roomId: controller.roomId),
|
: UnreadBadgeBackButton(roomId: controller.roomId!),
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: ChatAppBarTitle(controller),
|
title: ChatAppBarTitle(controller),
|
||||||
actions: _appBarActions(context),
|
actions: _appBarActions(context),
|
||||||
@ -167,7 +168,7 @@ class ChatView extends StatelessWidget {
|
|||||||
child: FloatingActionButton(
|
child: FloatingActionButton(
|
||||||
onPressed: controller.scrollDown,
|
onPressed: controller.scrollDown,
|
||||||
foregroundColor:
|
foregroundColor:
|
||||||
Theme.of(context).textTheme.bodyText2.color,
|
Theme.of(context).textTheme.bodyText2!.color,
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
mini: true,
|
mini: true,
|
||||||
child: Icon(Icons.arrow_downward_outlined,
|
child: Icon(Icons.arrow_downward_outlined,
|
||||||
@ -184,7 +185,7 @@ class ChatView extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (Matrix.of(context).wallpaper != null)
|
if (Matrix.of(context).wallpaper != null)
|
||||||
Image.file(
|
Image.file(
|
||||||
Matrix.of(context).wallpaper,
|
Matrix.of(context).wallpaper!,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
@ -240,7 +241,7 @@ class ChatView extends StatelessWidget {
|
|||||||
controller.filteredEvents.length +
|
controller.filteredEvents.length +
|
||||||
1
|
1
|
||||||
? controller
|
? controller
|
||||||
.timeline.isRequestingHistory
|
.timeline!.isRequestingHistory
|
||||||
? const Center(
|
? const Center(
|
||||||
child:
|
child:
|
||||||
CircularProgressIndicator
|
CircularProgressIndicator
|
||||||
@ -259,7 +260,7 @@ class ChatView extends StatelessWidget {
|
|||||||
onPressed: controller
|
onPressed: controller
|
||||||
.requestHistory,
|
.requestHistory,
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context)
|
L10n.of(context)!
|
||||||
.loadMore),
|
.loadMore),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -337,7 +338,7 @@ class ChatView extends StatelessWidget {
|
|||||||
selected: controller
|
selected: controller
|
||||||
.selectedEvents
|
.selectedEvents
|
||||||
.any((e) => e.eventId == controller.filteredEvents[i - 1].eventId),
|
.any((e) => e.eventId == controller.filteredEvents[i - 1].eventId),
|
||||||
timeline: controller.timeline,
|
timeline: controller.timeline!,
|
||||||
nextEvent: i < controller.filteredEvents.length ? controller.filteredEvents[i] : null),
|
nextEvent: i < controller.filteredEvents.length ? controller.filteredEvents[i] : null),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -352,8 +353,8 @@ class ChatView extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
if (controller.room.canSendDefaultMessages &&
|
if (controller.room!.canSendDefaultMessages &&
|
||||||
controller.room.membership == Membership.join)
|
controller.room!.membership == Membership.join)
|
||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
bottom: bottomSheetPadding,
|
bottom: bottomSheetPadding,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
// This file is auto-generated using scripts/generate_command_hints_glue.sh.
|
// This file is auto-generated using scripts/generate_command_hints_glue.sh.
|
||||||
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
@ -12,13 +12,13 @@ import '../../widgets/matrix.dart';
|
|||||||
|
|
||||||
class EncryptionButton extends StatefulWidget {
|
class EncryptionButton extends StatefulWidget {
|
||||||
final Room room;
|
final Room room;
|
||||||
const EncryptionButton(this.room, {Key key}) : super(key: key);
|
const EncryptionButton(this.room, {Key? key}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
_EncryptionButtonState createState() => _EncryptionButtonState();
|
_EncryptionButtonState createState() => _EncryptionButtonState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EncryptionButtonState extends State<EncryptionButton> {
|
class _EncryptionButtonState extends State<EncryptionButton> {
|
||||||
StreamSubscription _onSyncSub;
|
StreamSubscription? _onSyncSub;
|
||||||
|
|
||||||
void _enableEncryptionAction() async {
|
void _enableEncryptionAction() async {
|
||||||
if (widget.room.encrypted) {
|
if (widget.room.encrypted) {
|
||||||
@ -29,20 +29,20 @@ class _EncryptionButtonState extends State<EncryptionButton> {
|
|||||||
await showOkAlertDialog(
|
await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
message: L10n.of(context).noEncryptionForPublicRooms,
|
message: L10n.of(context)!.noEncryptionForPublicRooms,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).enableEncryption,
|
title: L10n.of(context)!.enableEncryption,
|
||||||
message: widget.room.client.encryptionEnabled
|
message: widget.room.client.encryptionEnabled
|
||||||
? L10n.of(context).enableEncryptionWarning
|
? L10n.of(context)!.enableEncryptionWarning
|
||||||
: L10n.of(context).needPantalaimonWarning,
|
: L10n.of(context)!.needPantalaimonWarning,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: L10n.of(context)!.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.ok) {
|
OkCancelResult.ok) {
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
@ -50,7 +50,7 @@ class _EncryptionButtonState extends State<EncryptionButton> {
|
|||||||
future: () => widget.room.enableEncryption(),
|
future: () => widget.room.enableEncryption(),
|
||||||
);
|
);
|
||||||
// we want to enable the lock icon
|
// we want to enable the lock icon
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,22 +68,22 @@ class _EncryptionButtonState extends State<EncryptionButton> {
|
|||||||
.onSync
|
.onSync
|
||||||
.stream
|
.stream
|
||||||
.where((s) => s.deviceLists != null)
|
.where((s) => s.deviceLists != null)
|
||||||
.listen((s) => setState(() => null));
|
.listen((s) => setState(() {}));
|
||||||
}
|
}
|
||||||
return FutureBuilder<List<User>>(
|
return FutureBuilder<List<User>>(
|
||||||
future:
|
future:
|
||||||
widget.room.encrypted ? widget.room.requestParticipants() : null,
|
widget.room.encrypted ? widget.room.requestParticipants() : null,
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
Color color;
|
Color? color;
|
||||||
if (widget.room.encrypted && snapshot.hasData) {
|
if (widget.room.encrypted && snapshot.hasData) {
|
||||||
final users = snapshot.data;
|
final users = snapshot.data!;
|
||||||
users.removeWhere((u) =>
|
users.removeWhere((u) =>
|
||||||
!{Membership.invite, Membership.join}.contains(u.membership) ||
|
!{Membership.invite, Membership.join}.contains(u.membership) ||
|
||||||
!widget.room.client.userDeviceKeys.containsKey(u.id));
|
!widget.room.client.userDeviceKeys.containsKey(u.id));
|
||||||
var allUsersValid = true;
|
var allUsersValid = true;
|
||||||
var oneUserInvalid = false;
|
var oneUserInvalid = false;
|
||||||
for (final u in users) {
|
for (final u in users) {
|
||||||
final status = widget.room.client.userDeviceKeys[u.id].verified;
|
final status = widget.room.client.userDeviceKeys[u.id]!.verified;
|
||||||
if (status != UserVerifiedStatus.verified) {
|
if (status != UserVerifiedStatus.verified) {
|
||||||
allUsersValid = false;
|
allUsersValid = false;
|
||||||
}
|
}
|
||||||
@ -99,8 +99,8 @@ class _EncryptionButtonState extends State<EncryptionButton> {
|
|||||||
}
|
}
|
||||||
return IconButton(
|
return IconButton(
|
||||||
tooltip: widget.room.encrypted
|
tooltip: widget.room.encrypted
|
||||||
? L10n.of(context).encrypted
|
? L10n.of(context)!.encrypted
|
||||||
: L10n.of(context).encryptionNotEnabled,
|
: L10n.of(context)!.encryptionNotEnabled,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
widget.room.encrypted
|
widget.room.encrypted
|
||||||
? Icons.lock_outlined
|
? Icons.lock_outlined
|
||||||
|
@ -13,7 +13,7 @@ extension EventInfoDialogExtension on Event {
|
|||||||
void showInfoDialog(BuildContext context) => showModalBottomSheet(
|
void showInfoDialog(BuildContext context) => showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
EventInfoDialog(l10n: L10n.of(context), event: this),
|
EventInfoDialog(l10n: L10n.of(context)!, event: this),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,9 +21,9 @@ class EventInfoDialog extends StatelessWidget {
|
|||||||
final Event event;
|
final Event event;
|
||||||
final L10n l10n;
|
final L10n l10n;
|
||||||
const EventInfoDialog({
|
const EventInfoDialog({
|
||||||
@required this.event,
|
required this.event,
|
||||||
@required this.l10n,
|
required this.l10n,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
String get prettyJson {
|
String get prettyJson {
|
||||||
@ -37,11 +37,11 @@ class EventInfoDialog extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(L10n.of(context).messageInfo),
|
title: Text(L10n.of(context)!.messageInfo),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_downward_outlined),
|
icon: const Icon(Icons.arrow_downward_outlined),
|
||||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||||
tooltip: L10n.of(context).close,
|
tooltip: L10n.of(context)!.close,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
@ -51,19 +51,19 @@ class EventInfoDialog extends StatelessWidget {
|
|||||||
mxContent: event.sender.avatarUrl,
|
mxContent: event.sender.avatarUrl,
|
||||||
name: event.sender.calcDisplayname(),
|
name: event.sender.calcDisplayname(),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).sender),
|
title: Text(L10n.of(context)!.sender),
|
||||||
subtitle:
|
subtitle:
|
||||||
Text('${event.sender.calcDisplayname()} [${event.senderId}]'),
|
Text('${event.sender.calcDisplayname()} [${event.senderId}]'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).time),
|
title: Text(L10n.of(context)!.time),
|
||||||
subtitle: Text(event.originServerTs.localizedTime(context)),
|
subtitle: Text(event.originServerTs.localizedTime(context)),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).messageType),
|
title: Text(L10n.of(context)!.messageType),
|
||||||
subtitle: Text(event.humanreadableType),
|
subtitle: Text(event.humanreadableType),
|
||||||
),
|
),
|
||||||
ListTile(title: Text('${L10n.of(context).sourceCode}:')),
|
ListTile(title: Text('${L10n.of(context)!.sourceCode}:')),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Material(
|
child: Material(
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
@ -13,17 +13,17 @@ import '../../../utils/url_launcher.dart';
|
|||||||
|
|
||||||
class HtmlMessage extends StatelessWidget {
|
class HtmlMessage extends StatelessWidget {
|
||||||
final String html;
|
final String html;
|
||||||
final int maxLines;
|
final int? maxLines;
|
||||||
final Room room;
|
final Room room;
|
||||||
final TextStyle defaultTextStyle;
|
final TextStyle? defaultTextStyle;
|
||||||
final TextStyle linkStyle;
|
final TextStyle? linkStyle;
|
||||||
final double emoteSize;
|
final double? emoteSize;
|
||||||
|
|
||||||
const HtmlMessage({
|
const HtmlMessage({
|
||||||
Key key,
|
Key? key,
|
||||||
this.html,
|
required this.html,
|
||||||
this.maxLines,
|
this.maxLines,
|
||||||
this.room,
|
required this.room,
|
||||||
this.defaultTextStyle,
|
this.defaultTextStyle,
|
||||||
this.linkStyle,
|
this.linkStyle,
|
||||||
this.emoteSize,
|
this.emoteSize,
|
||||||
@ -52,7 +52,7 @@ class HtmlMessage extends StatelessWidget {
|
|||||||
defaultTextStyle: defaultTextStyle,
|
defaultTextStyle: defaultTextStyle,
|
||||||
emoteSize: emoteSize,
|
emoteSize: emoteSize,
|
||||||
linkStyle: linkStyle ??
|
linkStyle: linkStyle ??
|
||||||
themeData.textTheme.bodyText2.copyWith(
|
themeData.textTheme.bodyText2!.copyWith(
|
||||||
color: themeData.colorScheme.secondary,
|
color: themeData.colorScheme.secondary,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
@ -60,11 +60,11 @@ class HtmlMessage extends StatelessWidget {
|
|||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
|
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
|
||||||
onPillTap: (url) => UrlLauncher(context, url).launchUrl(),
|
onPillTap: (url) => UrlLauncher(context, url).launchUrl(),
|
||||||
getMxcUrl: (String mxc, double width, double height,
|
getMxcUrl: (String mxc, double? width, double? height,
|
||||||
{bool animated = false}) {
|
{bool? animated = false}) {
|
||||||
final ratio = MediaQuery.of(context).devicePixelRatio;
|
final ratio = MediaQuery.of(context).devicePixelRatio;
|
||||||
return Uri.parse(mxc)
|
return Uri.parse(mxc)
|
||||||
?.getThumbnail(
|
.getThumbnail(
|
||||||
matrix.client,
|
matrix.client,
|
||||||
width: (width ?? 800) * ratio,
|
width: (width ?? 800) * ratio,
|
||||||
height: (height ?? 800) * ratio,
|
height: (height ?? 800) * ratio,
|
||||||
@ -92,9 +92,6 @@ class HtmlMessage extends StatelessWidget {
|
|||||||
return await matrix.store.getItem('${SettingKeys.codeLanguage}.$key');
|
return await matrix.store.getItem('${SettingKeys.codeLanguage}.$key');
|
||||||
},
|
},
|
||||||
getPillInfo: (String url) async {
|
getPillInfo: (String url) async {
|
||||||
if (room == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final identityParts = url.parseIdentifierIntoParts();
|
final identityParts = url.parseIdentifierIntoParts();
|
||||||
final identifier = identityParts?.primaryIdentifier;
|
final identifier = identityParts?.primaryIdentifier;
|
||||||
if (identifier == null) {
|
if (identifier == null) {
|
||||||
@ -108,14 +105,11 @@ class HtmlMessage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
// there might still be a profile...
|
// there might still be a profile...
|
||||||
final profile = await room.client.getProfileFromUserId(identifier);
|
final profile = await room.client.getProfileFromUserId(identifier);
|
||||||
if (profile != null) {
|
|
||||||
return {
|
return {
|
||||||
'displayname': profile.displayName,
|
'displayname': profile.displayName,
|
||||||
'avatar_url': profile.avatarUrl.toString(),
|
'avatar_url': profile.avatarUrl.toString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (identifier.sigil == '#') {
|
if (identifier.sigil == '#') {
|
||||||
// we have an alias pill
|
// we have an alias pill
|
||||||
for (final r in room.client.rooms) {
|
for (final r in room.client.rooms) {
|
||||||
@ -128,7 +122,7 @@ class HtmlMessage extends StatelessWidget {
|
|||||||
// we have a room!
|
// we have a room!
|
||||||
return {
|
return {
|
||||||
'displayname':
|
'displayname':
|
||||||
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||||
'avatar_url': r.getState('m.room.avatar')?.content['url'],
|
'avatar_url': r.getState('m.room.avatar')?.content['url'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -143,12 +137,12 @@ class HtmlMessage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
'displayname':
|
'displayname':
|
||||||
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||||
'avatar_url': r.getState('m.room.avatar')?.content['url'],
|
'avatar_url': r.getState('m.room.avatar')?.content['url'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
} as Future<Map<String, dynamic>> Function(String)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,13 @@ class ImageBubble extends StatefulWidget {
|
|||||||
final bool tapToView;
|
final bool tapToView;
|
||||||
final BoxFit fit;
|
final BoxFit fit;
|
||||||
final bool maxSize;
|
final bool maxSize;
|
||||||
final Color backgroundColor;
|
final Color? backgroundColor;
|
||||||
final bool thumbnailOnly;
|
final bool thumbnailOnly;
|
||||||
final bool animated;
|
final bool animated;
|
||||||
final double width;
|
final double width;
|
||||||
final double height;
|
final double height;
|
||||||
final void Function() onLoaded;
|
final void Function()? onLoaded;
|
||||||
final void Function() onTap;
|
final void Function()? onTap;
|
||||||
|
|
||||||
const ImageBubble(
|
const ImageBubble(
|
||||||
this.event, {
|
this.event, {
|
||||||
@ -39,7 +39,7 @@ class ImageBubble extends StatefulWidget {
|
|||||||
this.height = 300,
|
this.height = 300,
|
||||||
this.animated = false,
|
this.animated = false,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -48,17 +48,17 @@ class ImageBubble extends StatefulWidget {
|
|||||||
|
|
||||||
class _ImageBubbleState extends State<ImageBubble> {
|
class _ImageBubbleState extends State<ImageBubble> {
|
||||||
// for plaintext: holds the http URL for the thumbnail
|
// for plaintext: holds the http URL for the thumbnail
|
||||||
String thumbnailUrl;
|
String? thumbnailUrl;
|
||||||
// for plaintext. holds the http URL for the thumbnial, without the animated flag
|
// for plaintext. holds the http URL for the thumbnial, without the animated flag
|
||||||
String thumbnailUrlNoAnimated;
|
String? thumbnailUrlNoAnimated;
|
||||||
// for plaintext: holds the http URL of the original
|
// for plaintext: holds the http URL of the original
|
||||||
String attachmentUrl;
|
String? attachmentUrl;
|
||||||
MatrixFile _file;
|
MatrixFile? _file;
|
||||||
MatrixFile _thumbnail;
|
MatrixFile? _thumbnail;
|
||||||
bool _requestedThumbnailOnFailure = false;
|
bool _requestedThumbnailOnFailure = false;
|
||||||
// In case we have animated = false, this will hold the first frame so that we make
|
// In case we have animated = false, this will hold the first frame so that we make
|
||||||
// sure that things are never animated
|
// sure that things are never animated
|
||||||
Widget _firstFrame;
|
Widget? _firstFrame;
|
||||||
|
|
||||||
// the mimetypes that we know how to render, from the flutter Image widget
|
// the mimetypes that we know how to render, from the flutter Image widget
|
||||||
final _knownMimetypes = <String>{
|
final _knownMimetypes = <String>{
|
||||||
@ -82,8 +82,8 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
? widget.event.thumbnailMimetype.toLowerCase()
|
? widget.event.thumbnailMimetype.toLowerCase()
|
||||||
: widget.event.attachmentMimetype.toLowerCase();
|
: widget.event.attachmentMimetype.toLowerCase();
|
||||||
|
|
||||||
MatrixFile get _displayFile => _file ?? _thumbnail;
|
MatrixFile? get _displayFile => _file ?? _thumbnail;
|
||||||
String get displayUrl => widget.thumbnailOnly ? thumbnailUrl : attachmentUrl;
|
String? get displayUrl => widget.thumbnailOnly ? thumbnailUrl : attachmentUrl;
|
||||||
|
|
||||||
dynamic _error;
|
dynamic _error;
|
||||||
|
|
||||||
@ -91,14 +91,14 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
try {
|
try {
|
||||||
final res = await widget.event
|
final res = await widget.event
|
||||||
.downloadAndDecryptAttachmentCached(getThumbnail: getThumbnail);
|
.downloadAndDecryptAttachmentCached(getThumbnail: getThumbnail);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
if (getThumbnail) {
|
if (getThumbnail) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _thumbnail = res);
|
setState(() => _thumbnail = res);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (widget.onLoaded != null) {
|
if (widget.onLoaded != null) {
|
||||||
widget.onLoaded();
|
widget.onLoaded!();
|
||||||
}
|
}
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _file = res);
|
setState(() => _file = res);
|
||||||
@ -106,7 +106,7 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _error = err);
|
setState(() => _error = err);
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget frameBuilder(_, Widget child, int frame, __) {
|
Widget frameBuilder(_, Widget child, int? frame, __) {
|
||||||
// as servers might return animated gifs as thumbnails and we want them to *not* play
|
// as servers might return animated gifs as thumbnails and we want them to *not* play
|
||||||
// animated, we'll have to store the first frame in a variable and display that instead
|
// animated, we'll have to store the first frame in a variable and display that instead
|
||||||
if (widget.animated) {
|
if (widget.animated) {
|
||||||
@ -135,7 +135,9 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
key: ValueKey(key),
|
key: ValueKey(key),
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
),
|
),
|
||||||
network: (String url) => SvgPicture.network(
|
network: (String? url) => url == null
|
||||||
|
? Container()
|
||||||
|
: SvgPicture.network(
|
||||||
url,
|
url,
|
||||||
key: ValueKey(url),
|
key: ValueKey(url),
|
||||||
placeholderBuilder: (context) => getPlaceholderWidget(),
|
placeholderBuilder: (context) => getPlaceholderWidget(),
|
||||||
@ -151,7 +153,9 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
getErrorWidget(context, error),
|
getErrorWidget(context, error),
|
||||||
animate: widget.animated,
|
animate: widget.animated,
|
||||||
),
|
),
|
||||||
network: (String url) => Lottie.network(
|
network: (String? url) => url == null
|
||||||
|
? Container()
|
||||||
|
: Lottie.network(
|
||||||
url,
|
url,
|
||||||
key: ValueKey(url),
|
key: ValueKey(url),
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
@ -203,7 +207,7 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
primary: Theme.of(context).textTheme.bodyText1.color,
|
primary: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
),
|
),
|
||||||
icon: const Icon(Icons.download_outlined),
|
icon: const Icon(Icons.download_outlined),
|
||||||
onPressed: () => widget.event.saveFile(context),
|
onPressed: () => widget.event.saveFile(context),
|
||||||
@ -215,7 +219,7 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
if (widget.event.sizeString != null) Text(widget.event.sizeString),
|
if (widget.event.sizeString != null) Text(widget.event.sizeString!),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text((error ?? _error).toString()),
|
Text((error ?? _error).toString()),
|
||||||
],
|
],
|
||||||
@ -223,8 +227,8 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getPlaceholderWidget({Widget child}) {
|
Widget getPlaceholderWidget({Widget? child}) {
|
||||||
Widget blurhash;
|
Widget? blurhash;
|
||||||
if (widget.event.infoMap['xyz.amorgan.blurhash'] is String) {
|
if (widget.event.infoMap['xyz.amorgan.blurhash'] is String) {
|
||||||
final ratio =
|
final ratio =
|
||||||
widget.event.infoMap['w'] is int && widget.event.infoMap['h'] is int
|
widget.event.infoMap['w'] is int && widget.event.infoMap['h'] is int
|
||||||
@ -265,16 +269,16 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
: widget.event.thumbnailMxcUrl.toString();
|
: widget.event.thumbnailMxcUrl.toString();
|
||||||
final mimetype = getMimetype(!isOriginal);
|
final mimetype = getMimetype(!isOriginal);
|
||||||
if (_contentRenderers.containsKey(mimetype)) {
|
if (_contentRenderers.containsKey(mimetype)) {
|
||||||
return _contentRenderers[mimetype].memory(_displayFile.bytes, key);
|
return _contentRenderers[mimetype]!.memory!(_displayFile!.bytes, key);
|
||||||
} else {
|
} else {
|
||||||
return Image.memory(
|
return Image.memory(
|
||||||
_displayFile.bytes,
|
_displayFile!.bytes,
|
||||||
key: ValueKey(key),
|
key: ValueKey(key),
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
errorBuilder: (context, error, stacktrace) {
|
errorBuilder: (context, error, stacktrace) {
|
||||||
if (widget.event.hasThumbnail && !_requestedThumbnailOnFailure) {
|
if (widget.event.hasThumbnail && !_requestedThumbnailOnFailure) {
|
||||||
_requestedThumbnailOnFailure = true;
|
_requestedThumbnailOnFailure = true;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_file = null;
|
_file = null;
|
||||||
_requestFile(getThumbnail: true);
|
_requestFile(getThumbnail: true);
|
||||||
@ -299,12 +303,12 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
final mimetype = getMimetype(_requestedThumbnailOnFailure);
|
final mimetype = getMimetype(_requestedThumbnailOnFailure);
|
||||||
if (displayUrl == attachmentUrl &&
|
if (displayUrl == attachmentUrl &&
|
||||||
_contentRenderers.containsKey(mimetype)) {
|
_contentRenderers.containsKey(mimetype)) {
|
||||||
return _contentRenderers[mimetype].network(displayUrl);
|
return _contentRenderers[mimetype]!.network!(displayUrl);
|
||||||
} else {
|
} else {
|
||||||
return CachedNetworkImage(
|
return CachedNetworkImage(
|
||||||
// as we change the url on-error we need a key so that the widget actually updates
|
// as we change the url on-error we need a key so that the widget actually updates
|
||||||
key: ValueKey(displayUrl),
|
key: ValueKey(displayUrl),
|
||||||
imageUrl: displayUrl,
|
imageUrl: displayUrl!,
|
||||||
placeholder: (context, url) {
|
placeholder: (context, url) {
|
||||||
if (!widget.thumbnailOnly &&
|
if (!widget.thumbnailOnly &&
|
||||||
displayUrl != thumbnailUrl &&
|
displayUrl != thumbnailUrl &&
|
||||||
@ -313,7 +317,7 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
return FutureBuilder<bool>(
|
return FutureBuilder<bool>(
|
||||||
future: (() async {
|
future: (() async {
|
||||||
return await DefaultCacheManager()
|
return await DefaultCacheManager()
|
||||||
.getFileFromCache(thumbnailUrl) !=
|
.getFileFromCache(thumbnailUrl!) !=
|
||||||
null;
|
null;
|
||||||
})(),
|
})(),
|
||||||
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
|
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
|
||||||
@ -321,8 +325,8 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
return getPlaceholderWidget();
|
return getPlaceholderWidget();
|
||||||
}
|
}
|
||||||
final effectiveUrl = snapshot.data == true
|
final effectiveUrl = snapshot.data == true
|
||||||
? thumbnailUrl
|
? thumbnailUrl!
|
||||||
: thumbnailUrlNoAnimated;
|
: thumbnailUrlNoAnimated!;
|
||||||
return CachedNetworkImage(
|
return CachedNetworkImage(
|
||||||
key: ValueKey(effectiveUrl),
|
key: ValueKey(effectiveUrl),
|
||||||
imageUrl: effectiveUrl,
|
imageUrl: effectiveUrl,
|
||||||
@ -348,7 +352,7 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
// the image failed to load but the event has a thumbnail attached....so we can
|
// the image failed to load but the event has a thumbnail attached....so we can
|
||||||
// try to load this one!
|
// try to load this one!
|
||||||
_requestedThumbnailOnFailure = true;
|
_requestedThumbnailOnFailure = true;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
thumbnailUrl = widget.event
|
thumbnailUrl = widget.event
|
||||||
.getAttachmentUrl(
|
.getAttachmentUrl(
|
||||||
@ -382,11 +386,11 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
primary: Theme.of(context).textTheme.bodyText1.color,
|
primary: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
),
|
),
|
||||||
onPressed: () => onTap(context),
|
onPressed: () => onTap(context),
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).tapToShowImage,
|
L10n.of(context)!.tapToShowImage,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@ -394,7 +398,7 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
),
|
),
|
||||||
if (widget.event.sizeString != null) ...[
|
if (widget.event.sizeString != null) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(widget.event.sizeString),
|
Text(widget.event.sizeString!),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
@ -451,7 +455,7 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
|
|
||||||
void onTap(BuildContext context) {
|
void onTap(BuildContext context) {
|
||||||
if (widget.onTap != null) {
|
if (widget.onTap != null) {
|
||||||
widget.onTap();
|
widget.onTap!();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!widget.tapToView) return;
|
if (!widget.tapToView) return;
|
||||||
@ -476,8 +480,8 @@ class _ImageBubbleState extends State<ImageBubble> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ImageBubbleContentRenderer {
|
class _ImageBubbleContentRenderer {
|
||||||
final Widget Function(Uint8List, String) memory;
|
final Widget Function(Uint8List, String)? memory;
|
||||||
final Widget Function(String) network;
|
final Widget Function(String?)? network;
|
||||||
|
|
||||||
_ImageBubbleContentRenderer({this.memory, this.network});
|
_ImageBubbleContentRenderer({this.memory, this.network});
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,13 @@ class MapBubble extends StatelessWidget {
|
|||||||
final double height;
|
final double height;
|
||||||
final double radius;
|
final double radius;
|
||||||
const MapBubble({
|
const MapBubble({
|
||||||
this.latitude,
|
required this.latitude,
|
||||||
this.longitude,
|
required this.longitude,
|
||||||
this.zoom = 14.0,
|
this.zoom = 14.0,
|
||||||
this.width = 400,
|
this.width = 400,
|
||||||
this.height = 400,
|
this.height = 400,
|
||||||
this.radius = 10.0,
|
this.radius = 10.0,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -16,14 +16,14 @@ import 'verification_request_content.dart';
|
|||||||
|
|
||||||
class Message extends StatelessWidget {
|
class Message extends StatelessWidget {
|
||||||
final Event event;
|
final Event event;
|
||||||
final Event nextEvent;
|
final Event? nextEvent;
|
||||||
final void Function(Event) onSelect;
|
final void Function(Event)? onSelect;
|
||||||
final void Function(Event) onAvatarTab;
|
final void Function(Event)? onAvatarTab;
|
||||||
final void Function(Event) onInfoTab;
|
final void Function(Event)? onInfoTab;
|
||||||
final void Function(String) scrollToEventId;
|
final void Function(String)? scrollToEventId;
|
||||||
final void Function(String) unfold;
|
final void Function(String) unfold;
|
||||||
final bool longPressSelect;
|
final bool? longPressSelect;
|
||||||
final bool selected;
|
final bool? selected;
|
||||||
final Timeline timeline;
|
final Timeline timeline;
|
||||||
|
|
||||||
const Message(this.event,
|
const Message(this.event,
|
||||||
@ -33,10 +33,10 @@ class Message extends StatelessWidget {
|
|||||||
this.onInfoTab,
|
this.onInfoTab,
|
||||||
this.onAvatarTab,
|
this.onAvatarTab,
|
||||||
this.scrollToEventId,
|
this.scrollToEventId,
|
||||||
@required this.unfold,
|
required this.unfold,
|
||||||
this.selected,
|
this.selected,
|
||||||
this.timeline,
|
required this.timeline,
|
||||||
Key key})
|
Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
/// Indicates wheither the user may use a mouse instead
|
/// Indicates wheither the user may use a mouse instead
|
||||||
@ -61,14 +61,14 @@ class Message extends StatelessWidget {
|
|||||||
var color = Theme.of(context).appBarTheme.backgroundColor;
|
var color = Theme.of(context).appBarTheme.backgroundColor;
|
||||||
final displayTime = event.type == EventTypes.RoomCreate ||
|
final displayTime = event.type == EventTypes.RoomCreate ||
|
||||||
nextEvent == null ||
|
nextEvent == null ||
|
||||||
!event.originServerTs.sameEnvironment(nextEvent.originServerTs);
|
!event.originServerTs.sameEnvironment(nextEvent!.originServerTs);
|
||||||
final sameSender = nextEvent != null &&
|
final sameSender = nextEvent != null &&
|
||||||
[
|
[
|
||||||
EventTypes.Message,
|
EventTypes.Message,
|
||||||
EventTypes.Sticker,
|
EventTypes.Sticker,
|
||||||
EventTypes.Encrypted,
|
EventTypes.Encrypted,
|
||||||
].contains(nextEvent.type)
|
].contains(nextEvent!.type)
|
||||||
? nextEvent.sender.id == event.sender.id && !displayTime
|
? nextEvent!.sender.id == event.sender.id && !displayTime
|
||||||
: false;
|
: false;
|
||||||
final textColor = ownMessage
|
final textColor = ownMessage
|
||||||
? Colors.white
|
? Colors.white
|
||||||
@ -119,7 +119,7 @@ class Message extends StatelessWidget {
|
|||||||
: Avatar(
|
: Avatar(
|
||||||
mxContent: event.sender.avatarUrl,
|
mxContent: event.sender.avatarUrl,
|
||||||
name: event.sender.calcDisplayname(),
|
name: event.sender.calcDisplayname(),
|
||||||
onTap: () => onAvatarTab(event),
|
onTap: () => onAvatarTab!(event),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -152,10 +152,11 @@ class Message extends StatelessWidget {
|
|||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onHover: (b) => useMouse = true,
|
onHover: (b) => useMouse = true,
|
||||||
onTap: !useMouse && longPressSelect
|
onTap: !useMouse && longPressSelect!
|
||||||
? () => null
|
? () {}
|
||||||
: () => onSelect(event),
|
: () => onSelect!(event),
|
||||||
onLongPress: !longPressSelect ? null : () => onSelect(event),
|
onLongPress:
|
||||||
|
!longPressSelect! ? null : () => onSelect!(event),
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -175,13 +176,13 @@ class Message extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (event.relationshipType ==
|
if (event.relationshipType ==
|
||||||
RelationshipTypes.reply)
|
RelationshipTypes.reply)
|
||||||
FutureBuilder<Event>(
|
FutureBuilder<Event?>(
|
||||||
future: event.getReplyEvent(timeline),
|
future: event.getReplyEvent(timeline),
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
final replyEvent = snapshot.hasData
|
final replyEvent = snapshot.hasData
|
||||||
? snapshot.data
|
? snapshot.data!
|
||||||
: Event(
|
: Event(
|
||||||
eventId: event.relationshipEventId,
|
eventId: event.relationshipEventId!,
|
||||||
content: {
|
content: {
|
||||||
'msgtype': 'm.text',
|
'msgtype': 'm.text',
|
||||||
'body': '...'
|
'body': '...'
|
||||||
@ -195,7 +196,7 @@ class Message extends StatelessWidget {
|
|||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (scrollToEventId != null) {
|
if (scrollToEventId != null) {
|
||||||
scrollToEventId(replyEvent.eventId);
|
scrollToEventId!(replyEvent.eventId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: AbsorbPointer(
|
child: AbsorbPointer(
|
||||||
@ -300,7 +301,7 @@ class Message extends StatelessWidget {
|
|||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
color: selected
|
color: selected!
|
||||||
? Theme.of(context).primaryColor.withAlpha(100)
|
? Theme.of(context).primaryColor.withAlpha(100)
|
||||||
: Theme.of(context).primaryColor.withAlpha(0),
|
: Theme.of(context).primaryColor.withAlpha(0),
|
||||||
constraints:
|
constraints:
|
||||||
|
@ -22,10 +22,10 @@ import 'sticker.dart';
|
|||||||
|
|
||||||
class MessageContent extends StatelessWidget {
|
class MessageContent extends StatelessWidget {
|
||||||
final Event event;
|
final Event event;
|
||||||
final Color textColor;
|
final Color? textColor;
|
||||||
final void Function(Event) onInfoTab;
|
final void Function(Event)? onInfoTab;
|
||||||
|
|
||||||
const MessageContent(this.event, {this.onInfoTab, Key key, this.textColor})
|
const MessageContent(this.event, {this.onInfoTab, Key? key, this.textColor})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
void _verifyOrRequestKey(BuildContext context) async {
|
void _verifyOrRequestKey(BuildContext context) async {
|
||||||
@ -33,15 +33,15 @@ class MessageContent extends StatelessWidget {
|
|||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
event.type == EventTypes.Encrypted
|
event.type == EventTypes.Encrypted
|
||||||
? L10n.of(context).needPantalaimonWarning
|
? L10n.of(context)!.needPantalaimonWarning
|
||||||
: event.getLocalizedBody(
|
: event.getLocalizedBody(
|
||||||
MatrixLocals(L10n.of(context)),
|
MatrixLocals(L10n.of(context)!),
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
if (client.isUnknownSession && client.encryption.crossSigning.enabled) {
|
if (client.isUnknownSession && client.encryption!.crossSigning.enabled) {
|
||||||
await BootstrapDialog(
|
await BootstrapDialog(
|
||||||
client: Matrix.of(context).client,
|
client: Matrix.of(context).client,
|
||||||
).show(context);
|
).show(context);
|
||||||
@ -55,7 +55,7 @@ class MessageContent extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: Text(L10n.of(context).requestToReadOlderMessages)));
|
content: Text(L10n.of(context)!.requestToReadOlderMessages)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,17 +83,17 @@ class MessageContent extends StatelessWidget {
|
|||||||
if (PlatformInfos.isMobile) {
|
if (PlatformInfos.isMobile) {
|
||||||
return AudioPlayerWidget(
|
return AudioPlayerWidget(
|
||||||
event,
|
event,
|
||||||
color: textColor,
|
color: textColor!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return MessageDownloadContent(event, textColor);
|
return MessageDownloadContent(event, textColor!);
|
||||||
case MessageTypes.Video:
|
case MessageTypes.Video:
|
||||||
if (PlatformInfos.isMobile || PlatformInfos.isWeb) {
|
if (PlatformInfos.isMobile || PlatformInfos.isWeb) {
|
||||||
return EventVideoPlayer(event);
|
return EventVideoPlayer(event);
|
||||||
}
|
}
|
||||||
return MessageDownloadContent(event, textColor);
|
return MessageDownloadContent(event, textColor!);
|
||||||
case MessageTypes.File:
|
case MessageTypes.File:
|
||||||
return MessageDownloadContent(event, textColor);
|
return MessageDownloadContent(event, textColor!);
|
||||||
|
|
||||||
case MessageTypes.Text:
|
case MessageTypes.Text:
|
||||||
case MessageTypes.Notice:
|
case MessageTypes.Notice:
|
||||||
@ -115,7 +115,7 @@ class MessageContent extends StatelessWidget {
|
|||||||
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
||||||
),
|
),
|
||||||
linkStyle: TextStyle(
|
linkStyle: TextStyle(
|
||||||
color: textColor.withAlpha(150),
|
color: textColor!.withAlpha(150),
|
||||||
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
@ -131,14 +131,12 @@ class MessageContent extends StatelessWidget {
|
|||||||
textColor: buttonTextColor,
|
textColor: buttonTextColor,
|
||||||
onPressed: () => _verifyOrRequestKey(context),
|
onPressed: () => _verifyOrRequestKey(context),
|
||||||
icon: const Icon(Icons.lock_outline),
|
icon: const Icon(Icons.lock_outline),
|
||||||
label: L10n.of(context).encrypted,
|
label: L10n.of(context)!.encrypted,
|
||||||
);
|
);
|
||||||
case MessageTypes.Location:
|
case MessageTypes.Location:
|
||||||
final geoUri =
|
final geoUri =
|
||||||
Uri.tryParse(event.content.tryGet<String>('geo_uri'));
|
Uri.tryParse(event.content.tryGet<String>('geo_uri')!);
|
||||||
if (geoUri != null &&
|
if (geoUri != null && geoUri.scheme == 'geo') {
|
||||||
geoUri.scheme == 'geo' &&
|
|
||||||
geoUri.path != null) {
|
|
||||||
final latlong = geoUri.path
|
final latlong = geoUri.path
|
||||||
.split(';')
|
.split(';')
|
||||||
.first
|
.first
|
||||||
@ -152,8 +150,8 @@ class MessageContent extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
MapBubble(
|
MapBubble(
|
||||||
latitude: latlong.first,
|
latitude: latlong.first!,
|
||||||
longitude: latlong.last,
|
longitude: latlong.last!,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
@ -161,7 +159,7 @@ class MessageContent extends StatelessWidget {
|
|||||||
onPressed:
|
onPressed:
|
||||||
UrlLauncher(context, geoUri.toString()).launchUrl,
|
UrlLauncher(context, geoUri.toString()).launchUrl,
|
||||||
label: Text(
|
label: Text(
|
||||||
L10n.of(context).openInMaps,
|
L10n.of(context)!.openInMaps,
|
||||||
style: TextStyle(color: textColor),
|
style: TextStyle(color: textColor),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -177,24 +175,24 @@ class MessageContent extends StatelessWidget {
|
|||||||
return _ButtonContent(
|
return _ButtonContent(
|
||||||
onPressed: () => launch(event.body),
|
onPressed: () => launch(event.body),
|
||||||
icon: const Icon(Icons.phone_outlined, color: Colors.green),
|
icon: const Icon(Icons.phone_outlined, color: Colors.green),
|
||||||
label: L10n.of(context).videoCall,
|
label: L10n.of(context)!.videoCall,
|
||||||
textColor: buttonTextColor,
|
textColor: buttonTextColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (event.redacted) {
|
if (event.redacted) {
|
||||||
return _ButtonContent(
|
return _ButtonContent(
|
||||||
label: L10n.of(context)
|
label: L10n.of(context)!
|
||||||
.redactedAnEvent(event.sender.calcDisplayname()),
|
.redactedAnEvent(event.sender.calcDisplayname()),
|
||||||
icon: const Icon(Icons.delete_outlined),
|
icon: const Icon(Icons.delete_outlined),
|
||||||
textColor: buttonTextColor,
|
textColor: buttonTextColor,
|
||||||
onPressed: () => onInfoTab(event),
|
onPressed: () => onInfoTab!(event),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final bigEmotes = event.onlyEmotes &&
|
final bigEmotes = event.onlyEmotes &&
|
||||||
event.numberEmotes > 0 &&
|
event.numberEmotes > 0 &&
|
||||||
event.numberEmotes <= 10;
|
event.numberEmotes <= 10;
|
||||||
return LinkText(
|
return LinkText(
|
||||||
text: event.getLocalizedBody(MatrixLocals(L10n.of(context)),
|
text: event.getLocalizedBody(MatrixLocals(L10n.of(context)!),
|
||||||
hideReply: true),
|
hideReply: true),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
color: textColor,
|
color: textColor,
|
||||||
@ -202,24 +200,22 @@ class MessageContent extends StatelessWidget {
|
|||||||
decoration: event.redacted ? TextDecoration.lineThrough : null,
|
decoration: event.redacted ? TextDecoration.lineThrough : null,
|
||||||
),
|
),
|
||||||
linkStyle: TextStyle(
|
linkStyle: TextStyle(
|
||||||
color: textColor.withAlpha(150),
|
color: textColor!.withAlpha(150),
|
||||||
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
|
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return _ButtonContent(
|
return _ButtonContent(
|
||||||
label: L10n.of(context)
|
label: L10n.of(context)!
|
||||||
.userSentUnknownEvent(event.sender.calcDisplayname(), event.type),
|
.userSentUnknownEvent(event.sender.calcDisplayname(), event.type),
|
||||||
icon: const Icon(Icons.info_outlined),
|
icon: const Icon(Icons.info_outlined),
|
||||||
textColor: buttonTextColor,
|
textColor: buttonTextColor,
|
||||||
onPressed: () => onInfoTab(event),
|
onPressed: () => onInfoTab!(event),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Container(); // else flutter analyze complains
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,14 +223,14 @@ class _ButtonContent extends StatelessWidget {
|
|||||||
final void Function() onPressed;
|
final void Function() onPressed;
|
||||||
final String label;
|
final String label;
|
||||||
final Icon icon;
|
final Icon icon;
|
||||||
final Color textColor;
|
final Color? textColor;
|
||||||
|
|
||||||
const _ButtonContent({
|
const _ButtonContent({
|
||||||
@required this.label,
|
required this.label,
|
||||||
@required this.icon,
|
required this.icon,
|
||||||
@required this.textColor,
|
required this.textColor,
|
||||||
@required this.onPressed,
|
required this.onPressed,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
@ -14,14 +15,14 @@ class MessageReactions extends StatelessWidget {
|
|||||||
final Event event;
|
final Event event;
|
||||||
final Timeline timeline;
|
final Timeline timeline;
|
||||||
|
|
||||||
const MessageReactions(this.event, this.timeline, {Key key})
|
const MessageReactions(this.event, this.timeline, {Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final allReactionEvents =
|
final allReactionEvents =
|
||||||
event.aggregatedEvents(timeline, RelationshipTypes.reaction);
|
event.aggregatedEvents(timeline, RelationshipTypes.reaction);
|
||||||
final reactionMap = <String, _ReactionEntry>{};
|
final reactionMap = <String?, _ReactionEntry>{};
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
|
|
||||||
for (final e in allReactionEvents) {
|
for (final e in allReactionEvents) {
|
||||||
@ -35,9 +36,9 @@ class MessageReactions extends StatelessWidget {
|
|||||||
reactors: [],
|
reactors: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
reactionMap[key].count++;
|
reactionMap[key]!.count++;
|
||||||
reactionMap[key].reactors.add(e.sender);
|
reactionMap[key]!.reactors!.add(e.sender);
|
||||||
reactionMap[key].reacted |= e.senderId == e.room.client.userID;
|
reactionMap[key]!.reacted |= e.senderId == e.room.client.userID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,11 +53,9 @@ class MessageReactions extends StatelessWidget {
|
|||||||
reacted: r.reacted,
|
reacted: r.reacted,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (r.reacted) {
|
if (r.reacted) {
|
||||||
final evt = allReactionEvents.firstWhere(
|
final evt = allReactionEvents.firstWhereOrNull((e) =>
|
||||||
(e) =>
|
|
||||||
e.senderId == e.room.client.userID &&
|
e.senderId == e.room.client.userID &&
|
||||||
e.content['m.relates_to']['key'] == r.key,
|
e.content['m.relates_to']['key'] == r.key);
|
||||||
orElse: () => null);
|
|
||||||
if (evt != null) {
|
if (evt != null) {
|
||||||
showFutureLoadingDialog(
|
showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -67,7 +66,7 @@ class MessageReactions extends StatelessWidget {
|
|||||||
showFutureLoadingDialog(
|
showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () =>
|
future: () =>
|
||||||
event.room.sendReaction(event.eventId, r.key));
|
event.room.sendReaction(event.eventId, r.key!));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () async => await _AdaptableReactorsDialog(
|
onLongPress: () async => await _AdaptableReactorsDialog(
|
||||||
@ -91,11 +90,11 @@ class MessageReactions extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _Reaction extends StatelessWidget {
|
class _Reaction extends StatelessWidget {
|
||||||
final String reactionKey;
|
final String? reactionKey;
|
||||||
final int count;
|
final int? count;
|
||||||
final bool reacted;
|
final bool? reacted;
|
||||||
final void Function() onTap;
|
final void Function()? onTap;
|
||||||
final void Function() onLongPress;
|
final void Function()? onLongPress;
|
||||||
|
|
||||||
const _Reaction({
|
const _Reaction({
|
||||||
this.reactionKey,
|
this.reactionKey,
|
||||||
@ -113,11 +112,11 @@ class _Reaction extends StatelessWidget {
|
|||||||
final color = Theme.of(context).scaffoldBackgroundColor;
|
final color = Theme.of(context).scaffoldBackgroundColor;
|
||||||
final fontSize = DefaultTextStyle.of(context).style.fontSize;
|
final fontSize = DefaultTextStyle.of(context).style.fontSize;
|
||||||
Widget content;
|
Widget content;
|
||||||
if (reactionKey.startsWith('mxc://')) {
|
if (reactionKey!.startsWith('mxc://')) {
|
||||||
final src = Uri.parse(reactionKey)?.getThumbnail(
|
final src = Uri.parse(reactionKey!).getThumbnail(
|
||||||
Matrix.of(context).client,
|
Matrix.of(context).client,
|
||||||
width: 9999,
|
width: 9999,
|
||||||
height: fontSize * MediaQuery.of(context).devicePixelRatio,
|
height: fontSize! * MediaQuery.of(context).devicePixelRatio,
|
||||||
method: ThumbnailMethod.scale,
|
method: ThumbnailMethod.scale,
|
||||||
);
|
);
|
||||||
content = Row(
|
content = Row(
|
||||||
@ -136,7 +135,7 @@ class _Reaction extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
var renderKey = Characters(reactionKey);
|
var renderKey = Characters(reactionKey!);
|
||||||
if (renderKey.length > 10) {
|
if (renderKey.length > 10) {
|
||||||
renderKey = renderKey.getRange(0, 9) + Characters('…');
|
renderKey = renderKey.getRange(0, 9) + Characters('…');
|
||||||
}
|
}
|
||||||
@ -147,13 +146,13 @@ class _Reaction extends StatelessWidget {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => onTap != null ? onTap() : null,
|
onTap: () => onTap != null ? onTap!() : null,
|
||||||
onLongPress: () => onLongPress != null ? onLongPress() : null,
|
onLongPress: () => onLongPress != null ? onLongPress!() : null,
|
||||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: color,
|
color: color,
|
||||||
border: reacted
|
border: reacted!
|
||||||
? Border.all(
|
? Border.all(
|
||||||
width: 1,
|
width: 1,
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
@ -169,25 +168,30 @@ class _Reaction extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ReactionEntry {
|
class _ReactionEntry {
|
||||||
String key;
|
String? key;
|
||||||
int count;
|
int count;
|
||||||
bool reacted;
|
bool reacted;
|
||||||
List<User> reactors;
|
List<User>? reactors;
|
||||||
|
|
||||||
_ReactionEntry({this.key, this.count, this.reacted, this.reactors});
|
_ReactionEntry({
|
||||||
|
this.key,
|
||||||
|
required this.count,
|
||||||
|
required this.reacted,
|
||||||
|
this.reactors,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AdaptableReactorsDialog extends StatelessWidget {
|
class _AdaptableReactorsDialog extends StatelessWidget {
|
||||||
final Client client;
|
final Client? client;
|
||||||
final _ReactionEntry reactionEntry;
|
final _ReactionEntry? reactionEntry;
|
||||||
|
|
||||||
const _AdaptableReactorsDialog({
|
const _AdaptableReactorsDialog({
|
||||||
Key key,
|
Key? key,
|
||||||
this.client,
|
this.client,
|
||||||
this.reactionEntry,
|
this.reactionEntry,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
Future<bool> show(BuildContext context) => PlatformInfos.isCupertinoStyle
|
Future<bool?> show(BuildContext context) => PlatformInfos.isCupertinoStyle
|
||||||
? showCupertinoDialog(
|
? showCupertinoDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => this,
|
builder: (context) => this,
|
||||||
@ -209,20 +213,20 @@ class _AdaptableReactorsDialog extends StatelessWidget {
|
|||||||
runSpacing: 4.0,
|
runSpacing: 4.0,
|
||||||
alignment: WrapAlignment.center,
|
alignment: WrapAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
for (var reactor in reactionEntry.reactors)
|
for (var reactor in reactionEntry!.reactors!)
|
||||||
Chip(
|
Chip(
|
||||||
avatar: Avatar(
|
avatar: Avatar(
|
||||||
mxContent: reactor.avatarUrl,
|
mxContent: reactor.avatarUrl,
|
||||||
name: reactor.displayName,
|
name: reactor.displayName,
|
||||||
client: client,
|
client: client,
|
||||||
),
|
),
|
||||||
label: Text(reactor.displayName),
|
label: Text(reactor.displayName!),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final title = Center(child: Text(reactionEntry.key));
|
final title = Center(child: Text(reactionEntry!.key!));
|
||||||
|
|
||||||
return PlatformInfos.isCupertinoStyle
|
return PlatformInfos.isCupertinoStyle
|
||||||
? CupertinoAlertDialog(
|
? CupertinoAlertDialog(
|
||||||
|
@ -10,21 +10,23 @@ import 'html_message.dart';
|
|||||||
class ReplyContent extends StatelessWidget {
|
class ReplyContent extends StatelessWidget {
|
||||||
final Event replyEvent;
|
final Event replyEvent;
|
||||||
final bool lightText;
|
final bool lightText;
|
||||||
final Timeline timeline;
|
final Timeline? timeline;
|
||||||
|
|
||||||
const ReplyContent(this.replyEvent,
|
const ReplyContent(
|
||||||
{this.lightText = false, Key key, this.timeline})
|
this.replyEvent, {
|
||||||
: super(key: key);
|
this.lightText = false,
|
||||||
|
Key? key,
|
||||||
|
this.timeline,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget replyBody;
|
Widget replyBody;
|
||||||
final displayEvent = replyEvent != null && timeline != null
|
final timeline = this.timeline;
|
||||||
? replyEvent.getDisplayEvent(timeline)
|
final displayEvent =
|
||||||
: replyEvent;
|
timeline != null ? replyEvent.getDisplayEvent(timeline) : replyEvent;
|
||||||
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
|
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
|
||||||
if (displayEvent != null &&
|
if (AppConfig.renderHtml &&
|
||||||
AppConfig.renderHtml &&
|
|
||||||
[EventTypes.Message, EventTypes.Encrypted]
|
[EventTypes.Message, EventTypes.Encrypted]
|
||||||
.contains(displayEvent.type) &&
|
.contains(displayEvent.type) &&
|
||||||
[MessageTypes.Text, MessageTypes.Notice, MessageTypes.Emote]
|
[MessageTypes.Text, MessageTypes.Notice, MessageTypes.Emote]
|
||||||
@ -32,16 +34,16 @@ class ReplyContent extends StatelessWidget {
|
|||||||
!displayEvent.redacted &&
|
!displayEvent.redacted &&
|
||||||
displayEvent.content['format'] == 'org.matrix.custom.html' &&
|
displayEvent.content['format'] == 'org.matrix.custom.html' &&
|
||||||
displayEvent.content['formatted_body'] is String) {
|
displayEvent.content['formatted_body'] is String) {
|
||||||
String html = displayEvent.content['formatted_body'];
|
String? html = displayEvent.content['formatted_body'];
|
||||||
if (displayEvent.messageType == MessageTypes.Emote) {
|
if (displayEvent.messageType == MessageTypes.Emote) {
|
||||||
html = '* $html';
|
html = '* $html';
|
||||||
}
|
}
|
||||||
replyBody = HtmlMessage(
|
replyBody = HtmlMessage(
|
||||||
html: html,
|
html: html!,
|
||||||
defaultTextStyle: TextStyle(
|
defaultTextStyle: TextStyle(
|
||||||
color: lightText
|
color: lightText
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: Theme.of(context).textTheme.bodyText2.color,
|
: Theme.of(context).textTheme.bodyText2!.color,
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@ -50,18 +52,17 @@ class ReplyContent extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
replyBody = Text(
|
replyBody = Text(
|
||||||
displayEvent?.getLocalizedBody(
|
displayEvent.getLocalizedBody(
|
||||||
MatrixLocals(L10n.of(context)),
|
MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: false,
|
withSenderNamePrefix: false,
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
) ??
|
),
|
||||||
'',
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: lightText
|
color: lightText
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: Theme.of(context).textTheme.bodyText2.color,
|
: Theme.of(context).textTheme.bodyText2!.color,
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -81,7 +82,7 @@ class ReplyContent extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
(displayEvent?.sender?.calcDisplayname() ?? '') + ':',
|
displayEvent.sender.calcDisplayname() + ':',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -9,16 +9,16 @@ import '../../../config/app_config.dart';
|
|||||||
class StateMessage extends StatelessWidget {
|
class StateMessage extends StatelessWidget {
|
||||||
final Event event;
|
final Event event;
|
||||||
final void Function(String) unfold;
|
final void Function(String) unfold;
|
||||||
const StateMessage(this.event, {@required this.unfold, Key key})
|
const StateMessage(this.event, {required this.unfold, Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (event.unsigned['im.fluffychat.collapsed_state_event'] == true) {
|
if (event.unsigned!['im.fluffychat.collapsed_state_event'] == true) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
final int counter =
|
final int counter =
|
||||||
event.unsigned['im.fluffychat.collapsed_state_event_count'] ?? 0;
|
event.unsigned!['im.fluffychat.collapsed_state_event_count'] ?? 0;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 8.0,
|
horizontal: 8.0,
|
||||||
@ -40,18 +40,18 @@ class StateMessage extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
event.getLocalizedBody(MatrixLocals(L10n.of(context))),
|
event.getLocalizedBody(MatrixLocals(L10n.of(context)!)),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14 * AppConfig.fontSizeFactor,
|
fontSize: 14 * AppConfig.fontSizeFactor,
|
||||||
color: Theme.of(context).textTheme.bodyText2.color,
|
color: Theme.of(context).textTheme.bodyText2!.color,
|
||||||
decoration:
|
decoration:
|
||||||
event.redacted ? TextDecoration.lineThrough : null,
|
event.redacted ? TextDecoration.lineThrough : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (counter != 0)
|
if (counter != 0)
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context).moreEvents(counter),
|
L10n.of(context)!.moreEvents(counter),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 14 * AppConfig.fontSizeFactor,
|
fontSize: 14 * AppConfig.fontSizeFactor,
|
||||||
|
@ -10,14 +10,14 @@ import 'image_bubble.dart';
|
|||||||
class Sticker extends StatefulWidget {
|
class Sticker extends StatefulWidget {
|
||||||
final Event event;
|
final Event event;
|
||||||
|
|
||||||
const Sticker(this.event, {Key key}) : super(key: key);
|
const Sticker(this.event, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_StickerState createState() => _StickerState();
|
_StickerState createState() => _StickerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _StickerState extends State<Sticker> {
|
class _StickerState extends State<Sticker> {
|
||||||
bool animated;
|
bool? animated;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -31,7 +31,7 @@ class _StickerState extends State<Sticker> {
|
|||||||
showOkAlertDialog(
|
showOkAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
message: widget.event.body,
|
message: widget.event.body,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
animated: animated ?? AppConfig.autoplayImages,
|
animated: animated ?? AppConfig.autoplayImages,
|
||||||
|
@ -9,7 +9,8 @@ class VerificationRequestContent extends StatelessWidget {
|
|||||||
final Event event;
|
final Event event;
|
||||||
final Timeline timeline;
|
final Timeline timeline;
|
||||||
|
|
||||||
const VerificationRequestContent({this.event, this.timeline, Key key})
|
const VerificationRequestContent(
|
||||||
|
{required this.event, required this.timeline, Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -50,10 +51,10 @@ class VerificationRequestContent extends StatelessWidget {
|
|||||||
Text(canceled
|
Text(canceled
|
||||||
? 'Error ${cancel.first.content.tryGet<String>('code')}: ${cancel.first.content.tryGet<String>('reason')}'
|
? 'Error ${cancel.first.content.tryGet<String>('code')}: ${cancel.first.content.tryGet<String>('reason')}'
|
||||||
: (fullyDone
|
: (fullyDone
|
||||||
? L10n.of(context).verifySuccess
|
? L10n.of(context)!.verifySuccess
|
||||||
: (started
|
: (started
|
||||||
? L10n.of(context).loadingPleaseWait
|
? L10n.of(context)!.loadingPleaseWait
|
||||||
: L10n.of(context).newVerificationRequest)))
|
: L10n.of(context)!.newVerificationRequest)))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -15,19 +15,19 @@ import 'command_hints.dart';
|
|||||||
|
|
||||||
class InputBar extends StatelessWidget {
|
class InputBar extends StatelessWidget {
|
||||||
final Room room;
|
final Room room;
|
||||||
final int minLines;
|
final int? minLines;
|
||||||
final int maxLines;
|
final int? maxLines;
|
||||||
final TextInputType keyboardType;
|
final TextInputType? keyboardType;
|
||||||
final TextInputAction textInputAction;
|
final TextInputAction? textInputAction;
|
||||||
final ValueChanged<String> onSubmitted;
|
final ValueChanged<String>? onSubmitted;
|
||||||
final FocusNode focusNode;
|
final FocusNode? focusNode;
|
||||||
final TextEditingController controller;
|
final TextEditingController? controller;
|
||||||
final InputDecoration decoration;
|
final InputDecoration? decoration;
|
||||||
final ValueChanged<String> onChanged;
|
final ValueChanged<String>? onChanged;
|
||||||
final bool autofocus;
|
final bool? autofocus;
|
||||||
|
|
||||||
const InputBar({
|
const InputBar({
|
||||||
this.room,
|
required this.room,
|
||||||
this.minLines,
|
this.minLines,
|
||||||
this.maxLines,
|
this.maxLines,
|
||||||
this.keyboardType,
|
this.keyboardType,
|
||||||
@ -38,22 +38,23 @@ class InputBar extends StatelessWidget {
|
|||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.autofocus,
|
this.autofocus,
|
||||||
this.textInputAction,
|
this.textInputAction,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
List<Map<String, String>> getSuggestions(String text) {
|
List<Map<String, String?>> getSuggestions(String text) {
|
||||||
if (controller.selection.baseOffset != controller.selection.extentOffset ||
|
if (controller!.selection.baseOffset !=
|
||||||
controller.selection.baseOffset < 0) {
|
controller!.selection.extentOffset ||
|
||||||
|
controller!.selection.baseOffset < 0) {
|
||||||
return []; // no entries if there is selected text
|
return []; // no entries if there is selected text
|
||||||
}
|
}
|
||||||
final searchText =
|
final searchText =
|
||||||
controller.text.substring(0, controller.selection.baseOffset);
|
controller!.text.substring(0, controller!.selection.baseOffset);
|
||||||
final ret = <Map<String, String>>[];
|
final List<Map<String, String?>> ret = <Map<String, String>>[];
|
||||||
const maxResults = 30;
|
const maxResults = 30;
|
||||||
|
|
||||||
final commandMatch = RegExp(r'^\/([\w]*)$').firstMatch(searchText);
|
final commandMatch = RegExp(r'^\/([\w]*)$').firstMatch(searchText);
|
||||||
if (commandMatch != null) {
|
if (commandMatch != null) {
|
||||||
final commandSearch = commandMatch[1].toLowerCase();
|
final commandSearch = commandMatch[1]!.toLowerCase();
|
||||||
for (final command in room.client.commands.keys) {
|
for (final command in room.client.commands.keys) {
|
||||||
if (command.contains(commandSearch)) {
|
if (command.contains(commandSearch)) {
|
||||||
ret.add({
|
ret.add({
|
||||||
@ -69,7 +70,7 @@ class InputBar extends StatelessWidget {
|
|||||||
RegExp(r'(?:\s|^):(?:([-\w]+)~)?([-\w]+)$').firstMatch(searchText);
|
RegExp(r'(?:\s|^):(?:([-\w]+)~)?([-\w]+)$').firstMatch(searchText);
|
||||||
if (emojiMatch != null) {
|
if (emojiMatch != null) {
|
||||||
final packSearch = emojiMatch[1];
|
final packSearch = emojiMatch[1];
|
||||||
final emoteSearch = emojiMatch[2].toLowerCase();
|
final emoteSearch = emojiMatch[2]!.toLowerCase();
|
||||||
final emotePacks = room.getImagePacks(ImagePackUsage.emoticon);
|
final emotePacks = room.getImagePacks(ImagePackUsage.emoticon);
|
||||||
if (packSearch == null || packSearch.isEmpty) {
|
if (packSearch == null || packSearch.isEmpty) {
|
||||||
for (final pack in emotePacks.entries) {
|
for (final pack in emotePacks.entries) {
|
||||||
@ -93,16 +94,16 @@ class InputBar extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (emotePacks[packSearch] != null) {
|
} else if (emotePacks[packSearch] != null) {
|
||||||
for (final emote in emotePacks[packSearch].images.entries) {
|
for (final emote in emotePacks[packSearch]!.images.entries) {
|
||||||
if (emote.key.toLowerCase().contains(emoteSearch)) {
|
if (emote.key.toLowerCase().contains(emoteSearch)) {
|
||||||
ret.add({
|
ret.add({
|
||||||
'type': 'emote',
|
'type': 'emote',
|
||||||
'name': emote.key,
|
'name': emote.key,
|
||||||
'pack': packSearch,
|
'pack': packSearch,
|
||||||
'pack_avatar_url':
|
'pack_avatar_url':
|
||||||
emotePacks[packSearch].pack.avatarUrl?.toString(),
|
emotePacks[packSearch]!.pack.avatarUrl?.toString(),
|
||||||
'pack_display_name':
|
'pack_display_name':
|
||||||
emotePacks[packSearch].pack.displayName ?? packSearch,
|
emotePacks[packSearch]!.pack.displayName ?? packSearch,
|
||||||
'mxc': emote.value.url.toString(),
|
'mxc': emote.value.url.toString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -114,11 +115,11 @@ class InputBar extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
final userMatch = RegExp(r'(?:\s|^)@([-\w]+)$').firstMatch(searchText);
|
final userMatch = RegExp(r'(?:\s|^)@([-\w]+)$').firstMatch(searchText);
|
||||||
if (userMatch != null) {
|
if (userMatch != null) {
|
||||||
final userSearch = userMatch[1].toLowerCase();
|
final userSearch = userMatch[1]!.toLowerCase();
|
||||||
for (final user in room.getParticipants()) {
|
for (final user in room.getParticipants()) {
|
||||||
if ((user.displayName != null &&
|
if ((user.displayName != null &&
|
||||||
(user.displayName.toLowerCase().contains(userSearch) ||
|
(user.displayName!.toLowerCase().contains(userSearch) ||
|
||||||
slugify(user.displayName.toLowerCase())
|
slugify(user.displayName!.toLowerCase())
|
||||||
.contains(userSearch))) ||
|
.contains(userSearch))) ||
|
||||||
user.id.split(':')[0].toLowerCase().contains(userSearch)) {
|
user.id.split(':')[0].toLowerCase().contains(userSearch)) {
|
||||||
ret.add({
|
ret.add({
|
||||||
@ -136,7 +137,7 @@ class InputBar extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
final roomMatch = RegExp(r'(?:\s|^)#([-\w]+)$').firstMatch(searchText);
|
final roomMatch = RegExp(r'(?:\s|^)#([-\w]+)$').firstMatch(searchText);
|
||||||
if (roomMatch != null) {
|
if (roomMatch != null) {
|
||||||
final roomSearch = roomMatch[1].toLowerCase();
|
final roomSearch = roomMatch[1]!.toLowerCase();
|
||||||
for (final r in room.client.rooms) {
|
for (final r in room.client.rooms) {
|
||||||
if (r.getState(EventTypes.RoomTombstone) != null) {
|
if (r.getState(EventTypes.RoomTombstone) != null) {
|
||||||
continue; // we don't care about tombstoned rooms
|
continue; // we don't care about tombstoned rooms
|
||||||
@ -155,12 +156,10 @@ class InputBar extends StatelessWidget {
|
|||||||
.split(':')[0]
|
.split(':')[0]
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.contains(roomSearch))))) ||
|
.contains(roomSearch))))) ||
|
||||||
(r.name != null && r.name.toLowerCase().contains(roomSearch))) {
|
(r.name.toLowerCase().contains(roomSearch))) {
|
||||||
ret.add({
|
ret.add({
|
||||||
'type': 'room',
|
'type': 'room',
|
||||||
'mxid': (r.canonicalAlias != null && r.canonicalAlias.isNotEmpty)
|
'mxid': (r.canonicalAlias.isNotEmpty) ? r.canonicalAlias : r.id,
|
||||||
? r.canonicalAlias
|
|
||||||
: r.id,
|
|
||||||
'displayname': r.displayname,
|
'displayname': r.displayname,
|
||||||
'avatar_url': r.avatar?.toString(),
|
'avatar_url': r.avatar?.toString(),
|
||||||
});
|
});
|
||||||
@ -175,14 +174,14 @@ class InputBar extends StatelessWidget {
|
|||||||
|
|
||||||
Widget buildSuggestion(
|
Widget buildSuggestion(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Map<String, String> suggestion,
|
Map<String, String?> suggestion,
|
||||||
Client client,
|
Client? client,
|
||||||
) {
|
) {
|
||||||
const size = 30.0;
|
const size = 30.0;
|
||||||
const padding = EdgeInsets.all(4.0);
|
const padding = EdgeInsets.all(4.0);
|
||||||
if (suggestion['type'] == 'command') {
|
if (suggestion['type'] == 'command') {
|
||||||
final command = suggestion['name'];
|
final command = suggestion['name']!;
|
||||||
final hint = commandHint(L10n.of(context), command);
|
final hint = commandHint(L10n.of(context)!, command);
|
||||||
return Tooltip(
|
return Tooltip(
|
||||||
message: hint,
|
message: hint,
|
||||||
waitDuration: const Duration(days: 1), // don't show on hover
|
waitDuration: const Duration(days: 1), // don't show on hover
|
||||||
@ -206,7 +205,7 @@ class InputBar extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
if (suggestion['type'] == 'emote') {
|
if (suggestion['type'] == 'emote') {
|
||||||
final ratio = MediaQuery.of(context).devicePixelRatio;
|
final ratio = MediaQuery.of(context).devicePixelRatio;
|
||||||
final url = Uri.parse(suggestion['mxc'] ?? '')?.getThumbnail(
|
final url = Uri.parse(suggestion['mxc'] ?? '').getThumbnail(
|
||||||
room.client,
|
room.client,
|
||||||
width: size * ratio,
|
width: size * ratio,
|
||||||
height: size * ratio,
|
height: size * ratio,
|
||||||
@ -224,7 +223,7 @@ class InputBar extends StatelessWidget {
|
|||||||
height: size,
|
height: size,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
Text(suggestion['name']),
|
Text(suggestion['name']!),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
@ -239,7 +238,7 @@ class InputBar extends StatelessWidget {
|
|||||||
size: size * 0.9,
|
size: size * 0.9,
|
||||||
client: client,
|
client: client,
|
||||||
)
|
)
|
||||||
: Text(suggestion['pack_display_name']),
|
: Text(suggestion['pack_display_name']!),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -262,7 +261,7 @@ class InputBar extends StatelessWidget {
|
|||||||
client: client,
|
client: client,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
Text(suggestion['displayname'] ?? suggestion['mxid']),
|
Text(suggestion['displayname'] ?? suggestion['mxid']!),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -270,16 +269,16 @@ class InputBar extends StatelessWidget {
|
|||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
|
||||||
void insertSuggestion(_, Map<String, String> suggestion) {
|
void insertSuggestion(_, Map<String, String?> suggestion) {
|
||||||
final replaceText =
|
final replaceText =
|
||||||
controller.text.substring(0, controller.selection.baseOffset);
|
controller!.text.substring(0, controller!.selection.baseOffset);
|
||||||
var startText = '';
|
var startText = '';
|
||||||
final afterText = replaceText == controller.text
|
final afterText = replaceText == controller!.text
|
||||||
? ''
|
? ''
|
||||||
: controller.text.substring(controller.selection.baseOffset + 1);
|
: controller!.text.substring(controller!.selection.baseOffset + 1);
|
||||||
var insertText = '';
|
var insertText = '';
|
||||||
if (suggestion['type'] == 'command') {
|
if (suggestion['type'] == 'command') {
|
||||||
insertText = suggestion['name'] + ' ';
|
insertText = suggestion['name']! + ' ';
|
||||||
startText = replaceText.replaceAllMapped(
|
startText = replaceText.replaceAllMapped(
|
||||||
RegExp(r'^(\/[\w]*)$'),
|
RegExp(r'^(\/[\w]*)$'),
|
||||||
(Match m) => '/' + insertText,
|
(Match m) => '/' + insertText,
|
||||||
@ -304,29 +303,29 @@ class InputBar extends StatelessWidget {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
insertText = ':${isUnique ? '' : insertPack + '~'}$insertEmote: ';
|
insertText = ':${isUnique ? '' : insertPack! + '~'}$insertEmote: ';
|
||||||
startText = replaceText.replaceAllMapped(
|
startText = replaceText.replaceAllMapped(
|
||||||
RegExp(r'(\s|^)(:(?:[-\w]+~)?[-\w]+)$'),
|
RegExp(r'(\s|^)(:(?:[-\w]+~)?[-\w]+)$'),
|
||||||
(Match m) => '${m[1]}$insertText',
|
(Match m) => '${m[1]}$insertText',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (suggestion['type'] == 'user') {
|
if (suggestion['type'] == 'user') {
|
||||||
insertText = suggestion['mention'] + ' ';
|
insertText = suggestion['mention']! + ' ';
|
||||||
startText = replaceText.replaceAllMapped(
|
startText = replaceText.replaceAllMapped(
|
||||||
RegExp(r'(\s|^)(@[-\w]+)$'),
|
RegExp(r'(\s|^)(@[-\w]+)$'),
|
||||||
(Match m) => '${m[1]}$insertText',
|
(Match m) => '${m[1]}$insertText',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (suggestion['type'] == 'room') {
|
if (suggestion['type'] == 'room') {
|
||||||
insertText = suggestion['mxid'] + ' ';
|
insertText = suggestion['mxid']! + ' ';
|
||||||
startText = replaceText.replaceAllMapped(
|
startText = replaceText.replaceAllMapped(
|
||||||
RegExp(r'(\s|^)(#[-\w]+)$'),
|
RegExp(r'(\s|^)(#[-\w]+)$'),
|
||||||
(Match m) => '${m[1]}$insertText',
|
(Match m) => '${m[1]}$insertText',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (insertText.isNotEmpty && startText.isNotEmpty) {
|
if (insertText.isNotEmpty && startText.isNotEmpty) {
|
||||||
controller.text = startText + afterText;
|
controller!.text = startText + afterText;
|
||||||
controller.selection = TextSelection(
|
controller!.selection = TextSelection(
|
||||||
baseOffset: startText.length,
|
baseOffset: startText.length,
|
||||||
extentOffset: startText.length,
|
extentOffset: startText.length,
|
||||||
);
|
);
|
||||||
@ -351,13 +350,13 @@ class InputBar extends StatelessWidget {
|
|||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
NewLineIntent: CallbackAction(onInvoke: (i) {
|
NewLineIntent: CallbackAction(onInvoke: (i) {
|
||||||
final val = controller.value;
|
final val = controller!.value;
|
||||||
final selection = val.selection.start;
|
final selection = val.selection.start;
|
||||||
final messageWithoutNewLine =
|
final messageWithoutNewLine =
|
||||||
controller.text.substring(0, val.selection.start) +
|
controller!.text.substring(0, val.selection.start) +
|
||||||
'\n' +
|
'\n' +
|
||||||
controller.text.substring(val.selection.end);
|
controller!.text.substring(val.selection.end);
|
||||||
controller.value = TextEditingValue(
|
controller!.value = TextEditingValue(
|
||||||
text: messageWithoutNewLine,
|
text: messageWithoutNewLine,
|
||||||
selection: TextSelection.fromPosition(
|
selection: TextSelection.fromPosition(
|
||||||
TextPosition(offset: selection + 1),
|
TextPosition(offset: selection + 1),
|
||||||
@ -366,11 +365,11 @@ class InputBar extends StatelessWidget {
|
|||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
SubmitLineIntent: CallbackAction(onInvoke: (i) {
|
SubmitLineIntent: CallbackAction(onInvoke: (i) {
|
||||||
onSubmitted(controller.text);
|
onSubmitted!(controller!.text);
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
child: TypeAheadField<Map<String, String>>(
|
child: TypeAheadField<Map<String, String?>>(
|
||||||
direction: AxisDirection.up,
|
direction: AxisDirection.up,
|
||||||
hideOnEmpty: true,
|
hideOnEmpty: true,
|
||||||
hideOnLoading: true,
|
hideOnLoading: true,
|
||||||
@ -381,31 +380,31 @@ class InputBar extends StatelessWidget {
|
|||||||
textFieldConfiguration: TextFieldConfiguration(
|
textFieldConfiguration: TextFieldConfiguration(
|
||||||
minLines: minLines,
|
minLines: minLines,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType!,
|
||||||
textInputAction: textInputAction,
|
textInputAction: textInputAction,
|
||||||
autofocus: autofocus,
|
autofocus: autofocus!,
|
||||||
onSubmitted: (text) {
|
onSubmitted: (text) {
|
||||||
// fix for library for now
|
// fix for library for now
|
||||||
// it sets the types for the callback incorrectly
|
// it sets the types for the callback incorrectly
|
||||||
onSubmitted(text);
|
onSubmitted!(text);
|
||||||
},
|
},
|
||||||
//focusNode: focusNode,
|
//focusNode: focusNode,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
decoration: decoration,
|
decoration: decoration!,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
onChanged: (text) {
|
onChanged: (text) {
|
||||||
// fix for the library for now
|
// fix for the library for now
|
||||||
// it sets the types for the callback incorrectly
|
// it sets the types for the callback incorrectly
|
||||||
onChanged(text);
|
onChanged!(text);
|
||||||
},
|
},
|
||||||
textCapitalization: TextCapitalization.sentences,
|
textCapitalization: TextCapitalization.sentences,
|
||||||
),
|
),
|
||||||
suggestionsCallback: getSuggestions,
|
suggestionsCallback: getSuggestions,
|
||||||
itemBuilder: (c, s) =>
|
itemBuilder: (c, s) =>
|
||||||
buildSuggestion(c, s, Matrix.of(context).client),
|
buildSuggestion(c, s, Matrix.of(context).client),
|
||||||
onSuggestionSelected: (Map<String, String> suggestion) =>
|
onSuggestionSelected: (Map<String, String?> suggestion) =>
|
||||||
insertSuggestion(context, suggestion),
|
insertSuggestion(context, suggestion),
|
||||||
errorBuilder: (BuildContext context, Object error) => Container(),
|
errorBuilder: (BuildContext context, Object? error) => Container(),
|
||||||
loadingBuilder: (BuildContext context) =>
|
loadingBuilder: (BuildContext context) =>
|
||||||
Container(), // fix loading briefly flickering a dark box
|
Container(), // fix loading briefly flickering a dark box
|
||||||
noItemsFoundBuilder: (BuildContext context) =>
|
noItemsFoundBuilder: (BuildContext context) =>
|
||||||
|
@ -8,14 +8,14 @@ import 'package:fluffychat/pages/chat/chat.dart';
|
|||||||
|
|
||||||
class ReactionsPicker extends StatelessWidget {
|
class ReactionsPicker extends StatelessWidget {
|
||||||
final ChatController controller;
|
final ChatController controller;
|
||||||
const ReactionsPicker(this.controller, {Key key}) : super(key: key);
|
const ReactionsPicker(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (controller.showEmojiPicker) return Container();
|
if (controller.showEmojiPicker) return Container();
|
||||||
final display = controller.editEvent == null &&
|
final display = controller.editEvent == null &&
|
||||||
controller.replyEvent == null &&
|
controller.replyEvent == null &&
|
||||||
controller.room.canSendDefaultMessages &&
|
controller.room!.canSendDefaultMessages &&
|
||||||
controller.selectedEvents.isNotEmpty;
|
controller.selectedEvents.isNotEmpty;
|
||||||
return AnimatedContainer(
|
return AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
@ -28,8 +28,9 @@ class ReactionsPicker extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
final emojis = List<String>.from(AppEmojis.emojis);
|
final emojis = List<String>.from(AppEmojis.emojis);
|
||||||
final allReactionEvents = controller.selectedEvents.first
|
final allReactionEvents = controller.selectedEvents.first
|
||||||
.aggregatedEvents(controller.timeline, RelationshipTypes.reaction)
|
.aggregatedEvents(
|
||||||
?.where((event) =>
|
controller.timeline!, RelationshipTypes.reaction)
|
||||||
|
.where((event) =>
|
||||||
event.senderId == event.room.client.userID &&
|
event.senderId == event.room.client.userID &&
|
||||||
event.type == 'm.reaction');
|
event.type == 'm.reaction');
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
@ -9,7 +9,7 @@ import 'events/reply_content.dart';
|
|||||||
|
|
||||||
class ReplyDisplay extends StatelessWidget {
|
class ReplyDisplay extends StatelessWidget {
|
||||||
final ChatController controller;
|
final ChatController controller;
|
||||||
const ReplyDisplay(this.controller, {Key key}) : super(key: key);
|
const ReplyDisplay(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -23,16 +23,16 @@ class ReplyDisplay extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: L10n.of(context).close,
|
tooltip: L10n.of(context)!.close,
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: controller.cancelReplyEventAction,
|
onPressed: controller.cancelReplyEventAction,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: controller.replyEvent != null
|
child: controller.replyEvent != null
|
||||||
? ReplyContent(controller.replyEvent,
|
? ReplyContent(controller.replyEvent!,
|
||||||
timeline: controller.timeline)
|
timeline: controller.timeline!)
|
||||||
: _EditContent(controller.editEvent
|
: _EditContent(controller.editEvent
|
||||||
?.getDisplayEvent(controller.timeline)),
|
?.getDisplayEvent(controller.timeline!)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -42,7 +42,7 @@ class ReplyDisplay extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _EditContent extends StatelessWidget {
|
class _EditContent extends StatelessWidget {
|
||||||
final Event event;
|
final Event? event;
|
||||||
|
|
||||||
const _EditContent(this.event);
|
const _EditContent(this.event);
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ class _EditContent extends StatelessWidget {
|
|||||||
Container(width: 15.0),
|
Container(width: 15.0),
|
||||||
Text(
|
Text(
|
||||||
event?.getLocalizedBody(
|
event?.getLocalizedBody(
|
||||||
MatrixLocals(L10n.of(context)),
|
MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: false,
|
withSenderNamePrefix: false,
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
) ??
|
) ??
|
||||||
@ -68,7 +68,7 @@ class _EditContent extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).textTheme.bodyText2.color,
|
color: Theme.of(context).textTheme.bodyText2!.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -8,12 +8,12 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
|
|
||||||
class SeenByRow extends StatelessWidget {
|
class SeenByRow extends StatelessWidget {
|
||||||
final ChatController controller;
|
final ChatController controller;
|
||||||
const SeenByRow(this.controller, {Key key}) : super(key: key);
|
const SeenByRow(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final seenByUsers = controller.room.getSeenByUsers(
|
final seenByUsers = controller.room!.getSeenByUsers(
|
||||||
controller.timeline,
|
controller.timeline!,
|
||||||
controller.filteredEvents,
|
controller.filteredEvents,
|
||||||
controller.unfolded,
|
controller.unfolded,
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
@ -15,8 +15,8 @@ class SendLocationDialog extends StatefulWidget {
|
|||||||
final Room room;
|
final Room room;
|
||||||
|
|
||||||
const SendLocationDialog({
|
const SendLocationDialog({
|
||||||
this.room,
|
required this.room,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -27,8 +27,8 @@ class _SendLocationDialogState extends State<SendLocationDialog> {
|
|||||||
bool disabled = false;
|
bool disabled = false;
|
||||||
bool denied = false;
|
bool denied = false;
|
||||||
bool isSending = false;
|
bool isSending = false;
|
||||||
Position position;
|
Position? position;
|
||||||
Error error;
|
Object? error;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -75,9 +75,9 @@ class _SendLocationDialogState extends State<SendLocationDialog> {
|
|||||||
void sendAction() async {
|
void sendAction() async {
|
||||||
setState(() => isSending = true);
|
setState(() => isSending = true);
|
||||||
final body =
|
final body =
|
||||||
'https://www.openstreetmap.org/?mlat=${position.latitude}&mlon=${position.longitude}#map=16/${position.latitude}/${position.longitude}';
|
'https://www.openstreetmap.org/?mlat=${position!.latitude}&mlon=${position!.longitude}#map=16/${position!.latitude}/${position!.longitude}';
|
||||||
final uri =
|
final uri =
|
||||||
'geo:${position.latitude},${position.longitude};u=${position.accuracy}';
|
'geo:${position!.latitude},${position!.longitude};u=${position!.accuracy}';
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => widget.room.sendLocation(body, uri),
|
future: () => widget.room.sendLocation(body, uri),
|
||||||
@ -90,16 +90,16 @@ class _SendLocationDialogState extends State<SendLocationDialog> {
|
|||||||
Widget contentWidget;
|
Widget contentWidget;
|
||||||
if (position != null) {
|
if (position != null) {
|
||||||
contentWidget = MapBubble(
|
contentWidget = MapBubble(
|
||||||
latitude: position.latitude,
|
latitude: position!.latitude,
|
||||||
longitude: position.longitude,
|
longitude: position!.longitude,
|
||||||
);
|
);
|
||||||
} else if (disabled) {
|
} else if (disabled) {
|
||||||
contentWidget = Text(L10n.of(context).locationDisabledNotice);
|
contentWidget = Text(L10n.of(context)!.locationDisabledNotice);
|
||||||
} else if (denied) {
|
} else if (denied) {
|
||||||
contentWidget = Text(L10n.of(context).locationPermissionDeniedNotice);
|
contentWidget = Text(L10n.of(context)!.locationPermissionDeniedNotice);
|
||||||
} else if (error != null) {
|
} else if (error != null) {
|
||||||
contentWidget =
|
contentWidget =
|
||||||
Text(L10n.of(context).errorObtainingLocation(error.toString()));
|
Text(L10n.of(context)!.errorObtainingLocation(error.toString()));
|
||||||
} else {
|
} else {
|
||||||
contentWidget = Row(
|
contentWidget = Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -107,38 +107,38 @@ class _SendLocationDialogState extends State<SendLocationDialog> {
|
|||||||
children: [
|
children: [
|
||||||
const CupertinoActivityIndicator(),
|
const CupertinoActivityIndicator(),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(L10n.of(context).obtainingLocation),
|
Text(L10n.of(context)!.obtainingLocation),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (PlatformInfos.isCupertinoStyle) {
|
if (PlatformInfos.isCupertinoStyle) {
|
||||||
return CupertinoAlertDialog(
|
return CupertinoAlertDialog(
|
||||||
title: Text(L10n.of(context).shareLocation),
|
title: Text(L10n.of(context)!.shareLocation),
|
||||||
content: contentWidget,
|
content: contentWidget,
|
||||||
actions: [
|
actions: [
|
||||||
CupertinoDialogAction(
|
CupertinoDialogAction(
|
||||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||||
child: Text(L10n.of(context).cancel),
|
child: Text(L10n.of(context)!.cancel),
|
||||||
),
|
),
|
||||||
CupertinoDialogAction(
|
CupertinoDialogAction(
|
||||||
onPressed: isSending ? null : sendAction,
|
onPressed: isSending ? null : sendAction,
|
||||||
child: Text(L10n.of(context).send),
|
child: Text(L10n.of(context)!.send),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(L10n.of(context).shareLocation),
|
title: Text(L10n.of(context)!.shareLocation),
|
||||||
content: contentWidget,
|
content: contentWidget,
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||||
child: Text(L10n.of(context).cancel),
|
child: Text(L10n.of(context)!.cancel),
|
||||||
),
|
),
|
||||||
if (position != null)
|
if (position != null)
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: isSending ? null : sendAction,
|
onPressed: isSending ? null : sendAction,
|
||||||
child: Text(L10n.of(context).send),
|
child: Text(L10n.of(context)!.send),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -10,14 +10,14 @@ import 'events/image_bubble.dart';
|
|||||||
class StickerPickerDialog extends StatefulWidget {
|
class StickerPickerDialog extends StatefulWidget {
|
||||||
final Room room;
|
final Room room;
|
||||||
|
|
||||||
const StickerPickerDialog({this.room, Key key}) : super(key: key);
|
const StickerPickerDialog({required this.room, Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
StickerPickerDialogState createState() => StickerPickerDialogState();
|
StickerPickerDialogState createState() => StickerPickerDialogState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class StickerPickerDialogState extends State<StickerPickerDialog> {
|
class StickerPickerDialogState extends State<StickerPickerDialog> {
|
||||||
String searchFilter;
|
String? searchFilter;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -26,14 +26,14 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||||||
|
|
||||||
// ignore: prefer_function_declarations_over_variables
|
// ignore: prefer_function_declarations_over_variables
|
||||||
final _packBuilder = (BuildContext context, int packIndex) {
|
final _packBuilder = (BuildContext context, int packIndex) {
|
||||||
final pack = stickerPacks[packSlugs[packIndex]];
|
final pack = stickerPacks[packSlugs[packIndex]]!;
|
||||||
final filteredImagePackImageEntried = pack.images.entries.toList();
|
final filteredImagePackImageEntried = pack.images.entries.toList();
|
||||||
if (searchFilter?.isNotEmpty ?? false) {
|
if (searchFilter?.isNotEmpty ?? false) {
|
||||||
filteredImagePackImageEntried.removeWhere((e) =>
|
filteredImagePackImageEntried.removeWhere((e) =>
|
||||||
!(e.key.toLowerCase().contains(searchFilter.toLowerCase()) ||
|
!(e.key.toLowerCase().contains(searchFilter!.toLowerCase()) ||
|
||||||
(e.value.body
|
(e.value.body
|
||||||
?.toLowerCase()
|
?.toLowerCase()
|
||||||
?.contains(searchFilter.toLowerCase()) ??
|
.contains(searchFilter!.toLowerCase()) ??
|
||||||
false)));
|
false)));
|
||||||
}
|
}
|
||||||
final imageKeys =
|
final imageKeys =
|
||||||
@ -62,7 +62,7 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (BuildContext context, int imageIndex) {
|
itemBuilder: (BuildContext context, int imageIndex) {
|
||||||
final image = pack.images[imageKeys[imageIndex]];
|
final image = pack.images[imageKeys[imageIndex]]!;
|
||||||
final fakeEvent = Event.fromJson(<String, dynamic>{
|
final fakeEvent = Event.fromJson(<String, dynamic>{
|
||||||
'type': EventTypes.Sticker,
|
'type': EventTypes.Sticker,
|
||||||
'content': <String, dynamic>{
|
'content': <String, dynamic>{
|
||||||
@ -116,7 +116,7 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
|
|||||||
),
|
),
|
||||||
title: DefaultAppBarSearchField(
|
title: DefaultAppBarSearchField(
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
hintText: L10n.of(context).search,
|
hintText: L10n.of(context)!.search,
|
||||||
suffix: const Icon(Icons.search_outlined),
|
suffix: const Icon(Icons.search_outlined),
|
||||||
onChanged: (s) => setState(() => searchFilter = s),
|
onChanged: (s) => setState(() => searchFilter = s),
|
||||||
),
|
),
|
||||||
|
@ -7,11 +7,11 @@ import 'chat.dart';
|
|||||||
|
|
||||||
class TombstoneDisplay extends StatelessWidget {
|
class TombstoneDisplay extends StatelessWidget {
|
||||||
final ChatController controller;
|
final ChatController controller;
|
||||||
const TombstoneDisplay(this.controller, {Key key}) : super(key: key);
|
const TombstoneDisplay(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (controller.room.getState(EventTypes.RoomTombstone) == null) {
|
if (controller.room!.getState(EventTypes.RoomTombstone) == null) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
@ -26,14 +26,14 @@ class TombstoneDisplay extends StatelessWidget {
|
|||||||
child: const Icon(Icons.upgrade_outlined),
|
child: const Icon(Icons.upgrade_outlined),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
controller.room
|
controller.room!
|
||||||
.getState(EventTypes.RoomTombstone)
|
.getState(EventTypes.RoomTombstone)!
|
||||||
.parsedTombstoneContent
|
.parsedTombstoneContent
|
||||||
.body,
|
.body,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
subtitle: Text(L10n.of(context).goToTheNewRoom),
|
subtitle: Text(L10n.of(context)!.goToTheNewRoom),
|
||||||
onTap: controller.goToNewRoomAction,
|
onTap: controller.goToNewRoomAction,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -8,11 +8,11 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
|
|
||||||
class TypingIndicators extends StatelessWidget {
|
class TypingIndicators extends StatelessWidget {
|
||||||
final ChatController controller;
|
final ChatController controller;
|
||||||
const TypingIndicators(this.controller, {Key key}) : super(key: key);
|
const TypingIndicators(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final typingUsers = controller.room.typingUsers
|
final typingUsers = controller.room!.typingUsers
|
||||||
..removeWhere((u) => u.stateKey == Matrix.of(context).client.userID);
|
..removeWhere((u) => u.stateKey == Matrix.of(context).client.userID);
|
||||||
const topPadding = 20.0;
|
const topPadding = 20.0;
|
||||||
const bottomPadding = 4.0;
|
const bottomPadding = 4.0;
|
||||||
|
@ -18,34 +18,34 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
enum AliasActions { copy, delete, setCanonical }
|
enum AliasActions { copy, delete, setCanonical }
|
||||||
|
|
||||||
class ChatDetails extends StatefulWidget {
|
class ChatDetails extends StatefulWidget {
|
||||||
const ChatDetails({Key key}) : super(key: key);
|
const ChatDetails({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ChatDetailsController createState() => ChatDetailsController();
|
ChatDetailsController createState() => ChatDetailsController();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatDetailsController extends State<ChatDetails> {
|
class ChatDetailsController extends State<ChatDetails> {
|
||||||
List<User> members;
|
List<User>? members;
|
||||||
bool displaySettings = false;
|
bool displaySettings = false;
|
||||||
|
|
||||||
void toggleDisplaySettings() =>
|
void toggleDisplaySettings() =>
|
||||||
setState(() => displaySettings = !displaySettings);
|
setState(() => displaySettings = !displaySettings);
|
||||||
|
|
||||||
String get roomId => VRouter.of(context).pathParameters['roomid'];
|
String? get roomId => VRouter.of(context).pathParameters['roomid'];
|
||||||
|
|
||||||
void setDisplaynameAction() async {
|
void setDisplaynameAction() async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).changeTheNameOfTheGroup,
|
title: L10n.of(context)!.changeTheNameOfTheGroup,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
initialText: room.getLocalizedDisplayname(
|
initialText: room.getLocalizedDisplayname(
|
||||||
MatrixLocals(
|
MatrixLocals(
|
||||||
L10n.of(context),
|
L10n.of(context)!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -58,12 +58,12 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).displaynameHasBeenChanged)));
|
SnackBar(content: Text(L10n.of(context)!.displaynameHasBeenChanged)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void editAliases() async {
|
void editAliases() async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!);
|
||||||
|
|
||||||
// The current endpoint doesnt seem to be implemented in Synapse. This may
|
// The current endpoint doesnt seem to be implemented in Synapse. This may
|
||||||
// change in the future and then we just need to switch to this api call:
|
// change in the future and then we just need to switch to this api call:
|
||||||
@ -76,7 +76,7 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
// While this is not working we use the unstable api:
|
// While this is not working we use the unstable api:
|
||||||
final aliases = await showFutureLoadingDialog(
|
final aliases = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.client
|
future: () => room!.client
|
||||||
.request(
|
.request(
|
||||||
RequestType.GET,
|
RequestType.GET,
|
||||||
'/client/unstable/org.matrix.msc2432/rooms/${Uri.encodeComponent(room.id)}/aliases',
|
'/client/unstable/org.matrix.msc2432/rooms/${Uri.encodeComponent(room.id)}/aliases',
|
||||||
@ -86,21 +86,21 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
// Switch to the stable api once it is implemented.
|
// Switch to the stable api once it is implemented.
|
||||||
|
|
||||||
if (aliases.error != null) return;
|
if (aliases.error != null) return;
|
||||||
final adminMode = room.canSendEvent('m.room.canonical_alias');
|
final adminMode = room!.canSendEvent('m.room.canonical_alias');
|
||||||
if (aliases.result.isEmpty && (room.canonicalAlias?.isNotEmpty ?? false)) {
|
if (aliases.result!.isEmpty && (room.canonicalAlias.isNotEmpty)) {
|
||||||
aliases.result.add(room.canonicalAlias);
|
aliases.result!.add(room.canonicalAlias);
|
||||||
}
|
}
|
||||||
if (aliases.result.isEmpty && adminMode) {
|
if (aliases.result!.isEmpty && adminMode) {
|
||||||
return setAliasAction();
|
return setAliasAction();
|
||||||
}
|
}
|
||||||
final select = await showConfirmationDialog(
|
final select = await showConfirmationDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).editRoomAliases,
|
title: L10n.of(context)!.editRoomAliases,
|
||||||
actions: [
|
actions: [
|
||||||
if (adminMode)
|
if (adminMode)
|
||||||
AlertDialogAction(label: L10n.of(context).create, key: 'new'),
|
AlertDialogAction(label: L10n.of(context)!.create, key: 'new'),
|
||||||
...aliases.result
|
...aliases.result!
|
||||||
.map((alias) => AlertDialogAction(key: alias, label: alias))
|
.map((alias) => AlertDialogAction(key: alias, label: alias))
|
||||||
.toList(),
|
.toList(),
|
||||||
],
|
],
|
||||||
@ -114,29 +114,30 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
title: select,
|
title: select,
|
||||||
actions: [
|
actions: [
|
||||||
AlertDialogAction(
|
AlertDialogAction(
|
||||||
label: L10n.of(context).copyToClipboard,
|
label: L10n.of(context)!.copyToClipboard,
|
||||||
key: AliasActions.copy,
|
key: AliasActions.copy,
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
),
|
),
|
||||||
if (adminMode) ...{
|
if (adminMode) ...{
|
||||||
AlertDialogAction(
|
AlertDialogAction(
|
||||||
label: L10n.of(context).setAsCanonicalAlias,
|
label: L10n.of(context)!.setAsCanonicalAlias,
|
||||||
key: AliasActions.setCanonical,
|
key: AliasActions.setCanonical,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
),
|
),
|
||||||
AlertDialogAction(
|
AlertDialogAction(
|
||||||
label: L10n.of(context).delete,
|
label: L10n.of(context)!.delete,
|
||||||
key: AliasActions.delete,
|
key: AliasActions.delete,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
if (option == null) return;
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case AliasActions.copy:
|
case AliasActions.copy:
|
||||||
await Clipboard.setData(ClipboardData(text: select));
|
await Clipboard.setData(ClipboardData(text: select));
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).copiedToClipboard)),
|
SnackBar(content: Text(L10n.of(context)!.copiedToClipboard)),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AliasActions.delete:
|
case AliasActions.delete:
|
||||||
@ -162,21 +163,21 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setAliasAction() async {
|
void setAliasAction() async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||||
final domain = room.client.userID.domain;
|
final domain = room.client.userID!.domain;
|
||||||
|
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).setInvitationLink,
|
title: L10n.of(context)!.setInvitationLink,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
prefixText: '#',
|
prefixText: '#',
|
||||||
suffixText: domain,
|
suffixText: domain,
|
||||||
hintText: L10n.of(context).alias,
|
hintText: L10n.of(context)!.alias,
|
||||||
initialText: room.canonicalAlias?.localpart,
|
initialText: room.canonicalAlias.localpart,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -184,21 +185,21 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () =>
|
future: () =>
|
||||||
room.client.setRoomAlias('#' + input.single + ':' + domain, room.id),
|
room.client.setRoomAlias('#' + input.single + ':' + domain!, room.id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTopicAction() async {
|
void setTopicAction() async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).setGroupDescription,
|
title: L10n.of(context)!.setGroupDescription,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
hintText: L10n.of(context).setGroupDescription,
|
hintText: L10n.of(context)!.setGroupDescription,
|
||||||
initialText: room.topic,
|
initialText: room.topic,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
maxLines: 4,
|
maxLines: 4,
|
||||||
@ -212,7 +213,7 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: Text(L10n.of(context).groupDescriptionHasBeenChanged)));
|
content: Text(L10n.of(context)!.groupDescriptionHasBeenChanged)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +221,7 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context)
|
future: () => Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.getRoomById(roomId)
|
.getRoomById(roomId!)!
|
||||||
.setGuestAccess(guestAccess),
|
.setGuestAccess(guestAccess),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context)
|
future: () => Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.getRoomById(roomId)
|
.getRoomById(roomId!)!
|
||||||
.setHistoryVisibility(historyVisibility),
|
.setHistoryVisibility(historyVisibility),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -237,12 +238,12 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context)
|
future: () => Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.getRoomById(roomId)
|
.getRoomById(roomId!)!
|
||||||
.setJoinRules(joinRule),
|
.setJoinRules(joinRule),
|
||||||
);
|
);
|
||||||
|
|
||||||
void goToEmoteSettings() async {
|
void goToEmoteSettings() async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||||
// okay, we need to test if there are any emote state events other than the default one
|
// okay, we need to test if there are any emote state events other than the default one
|
||||||
// if so, we need to be directed to a selection screen for which pack we want to look at
|
// if so, we need to be directed to a selection screen for which pack we want to look at
|
||||||
// otherwise, we just open the normal one.
|
// otherwise, we just open the normal one.
|
||||||
@ -256,24 +257,24 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setAvatarAction() async {
|
void setAvatarAction() async {
|
||||||
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)
|
||||||
SheetAction(
|
SheetAction(
|
||||||
key: AvatarAction.camera,
|
key: AvatarAction.camera,
|
||||||
label: L10n.of(context).openCamera,
|
label: L10n.of(context)!.openCamera,
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
icon: Icons.camera_alt_outlined,
|
icon: Icons.camera_alt_outlined,
|
||||||
),
|
),
|
||||||
SheetAction(
|
SheetAction(
|
||||||
key: AvatarAction.file,
|
key: AvatarAction.file,
|
||||||
label: L10n.of(context).openGallery,
|
label: L10n.of(context)!.openGallery,
|
||||||
icon: Icons.photo_outlined,
|
icon: Icons.photo_outlined,
|
||||||
),
|
),
|
||||||
if (room?.avatar != null)
|
if (room?.avatar != null)
|
||||||
SheetAction(
|
SheetAction(
|
||||||
key: AvatarAction.remove,
|
key: AvatarAction.remove,
|
||||||
label: L10n.of(context).delete,
|
label: L10n.of(context)!.delete,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
icon: Icons.delete_outlined,
|
icon: Icons.delete_outlined,
|
||||||
),
|
),
|
||||||
@ -282,14 +283,14 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
? actions.single
|
? actions.single
|
||||||
: await showModalActionSheet<AvatarAction>(
|
: await showModalActionSheet<AvatarAction>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).editRoomAvatar,
|
title: L10n.of(context)!.editRoomAvatar,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
if (action == null) return;
|
if (action == null) return;
|
||||||
if (action == AvatarAction.remove) {
|
if (action == AvatarAction.remove) {
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.setAvatar(null),
|
future: () => room!.setAvatar(null),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -309,22 +310,22 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
} else {
|
} else {
|
||||||
final result =
|
final result =
|
||||||
await FilePickerCross.importFromStorage(type: FileTypeCross.image);
|
await FilePickerCross.importFromStorage(type: FileTypeCross.image);
|
||||||
if (result == null) return;
|
if (result.fileName == null) return;
|
||||||
file = MatrixFile(
|
file = MatrixFile(
|
||||||
bytes: result.toUint8List(),
|
bytes: result.toUint8List(),
|
||||||
name: result.fileName,
|
name: result.fileName!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.setAvatar(file),
|
future: () => room!.setAvatar(file),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestMoreMembersAction() async {
|
void requestMoreMembersAction() async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!);
|
||||||
final participants = await showFutureLoadingDialog(
|
final participants = await showFutureLoadingDialog(
|
||||||
context: context, future: () => room.requestParticipants());
|
context: context, future: () => room!.requestParticipants());
|
||||||
if (participants.error == null) {
|
if (participants.error == null) {
|
||||||
setState(() => members = participants.result);
|
setState(() => members = participants.result);
|
||||||
}
|
}
|
||||||
@ -334,7 +335,8 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
members ??= Matrix.of(context).client.getRoomById(roomId).getParticipants();
|
members ??=
|
||||||
|
Matrix.of(context).client.getRoomById(roomId!)!.getParticipants();
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: fixedWidth,
|
width: fixedWidth,
|
||||||
child: ChatDetailsView(this),
|
child: ChatDetailsView(this),
|
||||||
|
@ -20,28 +20,28 @@ import '../../utils/url_launcher.dart';
|
|||||||
class ChatDetailsView extends StatelessWidget {
|
class ChatDetailsView extends StatelessWidget {
|
||||||
final ChatDetailsController controller;
|
final ChatDetailsController controller;
|
||||||
|
|
||||||
const ChatDetailsView(this.controller, {Key key}) : super(key: key);
|
const ChatDetailsView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final room = Matrix.of(context).client.getRoomById(controller.roomId);
|
final room = Matrix.of(context).client.getRoomById(controller.roomId!);
|
||||||
if (room == null) {
|
if (room == null) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(L10n.of(context).oopsSomethingWentWrong),
|
title: Text(L10n.of(context)!.oopsSomethingWentWrong),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
|
child: Text(L10n.of(context)!.youAreNoLongerParticipatingInThisChat),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.members.removeWhere((u) => u.membership == Membership.leave);
|
controller.members!.removeWhere((u) => u.membership == Membership.leave);
|
||||||
final actualMembersCount = (room.summary?.mInvitedMemberCount ?? 0) +
|
final actualMembersCount = (room.summary.mInvitedMemberCount ?? 0) +
|
||||||
(room.summary?.mJoinedMemberCount ?? 0);
|
(room.summary.mJoinedMemberCount ?? 0);
|
||||||
final canRequestMoreMembers =
|
final canRequestMoreMembers =
|
||||||
controller.members.length < actualMembersCount;
|
controller.members!.length < actualMembersCount;
|
||||||
final iconColor = Theme.of(context).textTheme.bodyText1.color;
|
final iconColor = Theme.of(context).textTheme.bodyText1!.color;
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
stream: room.onUpdate.stream,
|
stream: room.onUpdate.stream,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -56,16 +56,16 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
VRouter.of(context).path.startsWith('/spaces/')
|
VRouter.of(context).path.startsWith('/spaces/')
|
||||||
? VRouter.of(context).pop()
|
? VRouter.of(context).pop()
|
||||||
: VRouter.of(context)
|
: VRouter.of(context)
|
||||||
.toSegments(['rooms', controller.roomId]),
|
.toSegments(['rooms', controller.roomId!]),
|
||||||
),
|
),
|
||||||
elevation: Theme.of(context).appBarTheme.elevation,
|
elevation: Theme.of(context).appBarTheme.elevation,
|
||||||
expandedHeight: 300.0,
|
expandedHeight: 300.0,
|
||||||
floating: true,
|
floating: true,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
if (room.canonicalAlias?.isNotEmpty ?? false)
|
if (room.canonicalAlias.isNotEmpty)
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: L10n.of(context).share,
|
tooltip: L10n.of(context)!.share,
|
||||||
icon: Icon(Icons.adaptive.share_outlined),
|
icon: Icon(Icons.adaptive.share_outlined),
|
||||||
onPressed: () => FluffyShare.share(
|
onPressed: () => FluffyShare.share(
|
||||||
AppConfig.inviteLinkPrefix + room.canonicalAlias,
|
AppConfig.inviteLinkPrefix + room.canonicalAlias,
|
||||||
@ -75,16 +75,17 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
title: Text(
|
title: Text(
|
||||||
room.getLocalizedDisplayname(
|
room.getLocalizedDisplayname(
|
||||||
MatrixLocals(L10n.of(context))),
|
MatrixLocals(L10n.of(context)!)),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.appBarTheme
|
.appBarTheme
|
||||||
.titleTextStyle
|
.titleTextStyle!
|
||||||
.color)),
|
.color)),
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).appBarTheme.backgroundColor,
|
Theme.of(context).appBarTheme.backgroundColor,
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
background: ContentBanner(room.avatar,
|
background: ContentBanner(
|
||||||
|
mxContent: room.avatar,
|
||||||
onEdit: room.canSendEvent('m.room.avatar')
|
onEdit: room.canSendEvent('m.room.avatar')
|
||||||
? controller.setAvatarAction
|
? controller.setAvatarAction
|
||||||
: null),
|
: null),
|
||||||
@ -93,7 +94,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
body: MaxWidthBody(
|
body: MaxWidthBody(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: controller.members.length +
|
itemCount: controller.members!.length +
|
||||||
1 +
|
1 +
|
||||||
(canRequestMoreMembers ? 1 : 0),
|
(canRequestMoreMembers ? 1 : 0),
|
||||||
itemBuilder: (BuildContext context, int i) => i == 0
|
itemBuilder: (BuildContext context, int i) => i == 0
|
||||||
@ -111,15 +112,15 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
title: Text(
|
title: Text(
|
||||||
'${L10n.of(context).groupDescription}:',
|
'${L10n.of(context)!.groupDescription}:',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.secondary,
|
.secondary,
|
||||||
fontWeight: FontWeight.bold)),
|
fontWeight: FontWeight.bold)),
|
||||||
subtitle: LinkText(
|
subtitle: LinkText(
|
||||||
text: room.topic?.isEmpty ?? true
|
text: room.topic.isEmpty
|
||||||
? L10n.of(context).addGroupDescription
|
? L10n.of(context)!.addGroupDescription
|
||||||
: room.topic,
|
: room.topic,
|
||||||
linkStyle:
|
linkStyle:
|
||||||
const TextStyle(color: Colors.blueAccent),
|
const TextStyle(color: Colors.blueAccent),
|
||||||
@ -127,7 +128,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyText2
|
.bodyText2!
|
||||||
.color,
|
.color,
|
||||||
),
|
),
|
||||||
onLinkTap: (url) =>
|
onLinkTap: (url) =>
|
||||||
@ -141,7 +142,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).settings,
|
L10n.of(context)!.settings,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color:
|
||||||
Theme.of(context).colorScheme.secondary,
|
Theme.of(context).colorScheme.secondary,
|
||||||
@ -163,10 +164,10 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
child: const Icon(
|
child: const Icon(
|
||||||
Icons.people_outline_outlined),
|
Icons.people_outline_outlined),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(L10n.of(context)!
|
||||||
L10n.of(context).changeTheNameOfTheGroup),
|
.changeTheNameOfTheGroup),
|
||||||
subtitle: Text(room.getLocalizedDisplayname(
|
subtitle: Text(room.getLocalizedDisplayname(
|
||||||
MatrixLocals(L10n.of(context)))),
|
MatrixLocals(L10n.of(context)!))),
|
||||||
onTap: controller.setDisplaynameAction,
|
onTap: controller.setDisplaynameAction,
|
||||||
),
|
),
|
||||||
if (room.joinRules == JoinRules.public)
|
if (room.joinRules == JoinRules.public)
|
||||||
@ -178,11 +179,12 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
child: const Icon(Icons.link_outlined),
|
child: const Icon(Icons.link_outlined),
|
||||||
),
|
),
|
||||||
onTap: controller.editAliases,
|
onTap: controller.editAliases,
|
||||||
title: Text(L10n.of(context).editRoomAliases),
|
title:
|
||||||
|
Text(L10n.of(context)!.editRoomAliases),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
(room.canonicalAlias?.isNotEmpty ?? false)
|
(room.canonicalAlias.isNotEmpty)
|
||||||
? room.canonicalAlias
|
? room.canonicalAlias
|
||||||
: L10n.of(context).none),
|
: L10n.of(context)!.none),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
@ -192,9 +194,9 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
child: const Icon(
|
child: const Icon(
|
||||||
Icons.insert_emoticon_outlined),
|
Icons.insert_emoticon_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).emoteSettings),
|
title: Text(L10n.of(context)!.emoteSettings),
|
||||||
subtitle:
|
subtitle:
|
||||||
Text(L10n.of(context).setCustomEmotes),
|
Text(L10n.of(context)!.setCustomEmotes),
|
||||||
onTap: controller.goToEmoteSettings,
|
onTap: controller.goToEmoteSettings,
|
||||||
),
|
),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
@ -206,14 +208,14 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
value: JoinRules.public,
|
value: JoinRules.public,
|
||||||
child: Text(JoinRules.public
|
child: Text(JoinRules.public
|
||||||
.getLocalizedString(
|
.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context)))),
|
MatrixLocals(L10n.of(context)!))),
|
||||||
),
|
),
|
||||||
if (room.canChangeJoinRules)
|
if (room.canChangeJoinRules)
|
||||||
PopupMenuItem<JoinRules>(
|
PopupMenuItem<JoinRules>(
|
||||||
value: JoinRules.invite,
|
value: JoinRules.invite,
|
||||||
child: Text(JoinRules.invite
|
child: Text(JoinRules.invite
|
||||||
.getLocalizedString(
|
.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context)))),
|
MatrixLocals(L10n.of(context)!))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@ -222,11 +224,11 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
.scaffoldBackgroundColor,
|
.scaffoldBackgroundColor,
|
||||||
foregroundColor: iconColor,
|
foregroundColor: iconColor,
|
||||||
child: const Icon(Icons.shield_outlined)),
|
child: const Icon(Icons.shield_outlined)),
|
||||||
title: Text(L10n.of(context)
|
title: Text(L10n.of(context)!
|
||||||
.whoIsAllowedToJoinThisGroup),
|
.whoIsAllowedToJoinThisGroup),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
room.joinRules.getLocalizedString(
|
room.joinRules!.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context))),
|
MatrixLocals(L10n.of(context)!)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -240,21 +242,21 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
value: HistoryVisibility.invited,
|
value: HistoryVisibility.invited,
|
||||||
child: Text(HistoryVisibility.invited
|
child: Text(HistoryVisibility.invited
|
||||||
.getLocalizedString(
|
.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context)))),
|
MatrixLocals(L10n.of(context)!))),
|
||||||
),
|
),
|
||||||
if (room.canChangeHistoryVisibility)
|
if (room.canChangeHistoryVisibility)
|
||||||
PopupMenuItem<HistoryVisibility>(
|
PopupMenuItem<HistoryVisibility>(
|
||||||
value: HistoryVisibility.joined,
|
value: HistoryVisibility.joined,
|
||||||
child: Text(HistoryVisibility.joined
|
child: Text(HistoryVisibility.joined
|
||||||
.getLocalizedString(
|
.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context)))),
|
MatrixLocals(L10n.of(context)!))),
|
||||||
),
|
),
|
||||||
if (room.canChangeHistoryVisibility)
|
if (room.canChangeHistoryVisibility)
|
||||||
PopupMenuItem<HistoryVisibility>(
|
PopupMenuItem<HistoryVisibility>(
|
||||||
value: HistoryVisibility.shared,
|
value: HistoryVisibility.shared,
|
||||||
child: Text(HistoryVisibility.shared
|
child: Text(HistoryVisibility.shared
|
||||||
.getLocalizedString(
|
.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context)))),
|
MatrixLocals(L10n.of(context)!))),
|
||||||
),
|
),
|
||||||
if (room.canChangeHistoryVisibility)
|
if (room.canChangeHistoryVisibility)
|
||||||
PopupMenuItem<HistoryVisibility>(
|
PopupMenuItem<HistoryVisibility>(
|
||||||
@ -262,7 +264,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
child: Text(HistoryVisibility
|
child: Text(HistoryVisibility
|
||||||
.worldReadable
|
.worldReadable
|
||||||
.getLocalizedString(
|
.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context)))),
|
MatrixLocals(L10n.of(context)!))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@ -273,12 +275,11 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
child:
|
child:
|
||||||
const Icon(Icons.visibility_outlined),
|
const Icon(Icons.visibility_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context)
|
title: Text(L10n.of(context)!
|
||||||
.visibilityOfTheChatHistory),
|
.visibilityOfTheChatHistory),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
room.historyVisibility.getLocalizedString(
|
room.historyVisibility!.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context))) ??
|
MatrixLocals(L10n.of(context)!)),
|
||||||
'',
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -293,7 +294,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
GuestAccess.canJoin
|
GuestAccess.canJoin
|
||||||
.getLocalizedString(MatrixLocals(
|
.getLocalizedString(MatrixLocals(
|
||||||
L10n.of(context))),
|
L10n.of(context)!)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (room.canChangeGuestAccess)
|
if (room.canChangeGuestAccess)
|
||||||
@ -302,7 +303,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
GuestAccess.forbidden
|
GuestAccess.forbidden
|
||||||
.getLocalizedString(MatrixLocals(
|
.getLocalizedString(MatrixLocals(
|
||||||
L10n.of(context))),
|
L10n.of(context)!)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -314,19 +315,19 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
child: const Icon(
|
child: const Icon(
|
||||||
Icons.person_add_alt_1_outlined),
|
Icons.person_add_alt_1_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context)
|
title: Text(L10n.of(context)!
|
||||||
.areGuestsAllowedToJoin),
|
.areGuestsAllowedToJoin),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
room.guestAccess.getLocalizedString(
|
room.guestAccess.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context))),
|
MatrixLocals(L10n.of(context)!)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title:
|
title:
|
||||||
Text(L10n.of(context).editChatPermissions),
|
Text(L10n.of(context)!.editChatPermissions),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
L10n.of(context).whoCanPerformWhichAction),
|
L10n.of(context)!.whoCanPerformWhichAction),
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
@ -342,9 +343,9 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
actualMembersCount > 1
|
actualMembersCount > 1
|
||||||
? L10n.of(context).countParticipants(
|
? L10n.of(context)!.countParticipants(
|
||||||
actualMembersCount.toString())
|
actualMembersCount.toString())
|
||||||
: L10n.of(context).emptyChat,
|
: L10n.of(context)!.emptyChat,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color:
|
||||||
Theme.of(context).colorScheme.secondary,
|
Theme.of(context).colorScheme.secondary,
|
||||||
@ -354,7 +355,8 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
room.canInvite
|
room.canInvite
|
||||||
? ListTile(
|
? ListTile(
|
||||||
title: Text(L10n.of(context).inviteContact),
|
title:
|
||||||
|
Text(L10n.of(context)!.inviteContact),
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).primaryColor,
|
Theme.of(context).primaryColor,
|
||||||
@ -368,13 +370,13 @@ class ChatDetailsView extends StatelessWidget {
|
|||||||
: Container(),
|
: Container(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: i < controller.members.length + 1
|
: i < controller.members!.length + 1
|
||||||
? ParticipantListItem(controller.members[i - 1])
|
? ParticipantListItem(controller.members![i - 1])
|
||||||
: ListTile(
|
: ListTile(
|
||||||
title: Text(L10n.of(context)
|
title: Text(L10n.of(context)!
|
||||||
.loadCountMoreParticipants(
|
.loadCountMoreParticipants(
|
||||||
(actualMembersCount -
|
(actualMembersCount -
|
||||||
controller.members.length)
|
controller.members!.length)
|
||||||
.toString())),
|
.toString())),
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
|
@ -9,20 +9,20 @@ import '../user_bottom_sheet/user_bottom_sheet.dart';
|
|||||||
class ParticipantListItem extends StatelessWidget {
|
class ParticipantListItem extends StatelessWidget {
|
||||||
final User user;
|
final User user;
|
||||||
|
|
||||||
const ParticipantListItem(this.user, {Key key}) : super(key: key);
|
const ParticipantListItem(this.user, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final membershipBatch = <Membership, String>{
|
final membershipBatch = <Membership, String>{
|
||||||
Membership.join: '',
|
Membership.join: '',
|
||||||
Membership.ban: L10n.of(context).banned,
|
Membership.ban: L10n.of(context)!.banned,
|
||||||
Membership.invite: L10n.of(context).invited,
|
Membership.invite: L10n.of(context)!.invited,
|
||||||
Membership.leave: L10n.of(context).leftTheChat,
|
Membership.leave: L10n.of(context)!.leftTheChat,
|
||||||
};
|
};
|
||||||
final permissionBatch = user.powerLevel == 100
|
final permissionBatch = user.powerLevel == 100
|
||||||
? L10n.of(context).admin
|
? L10n.of(context)!.admin
|
||||||
: user.powerLevel >= 50
|
: user.powerLevel >= 50
|
||||||
? L10n.of(context).moderator
|
? L10n.of(context)!.moderator
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
return Opacity(
|
return Opacity(
|
||||||
@ -49,7 +49,7 @@ class ParticipantListItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Center(child: Text(permissionBatch)),
|
child: Center(child: Text(permissionBatch)),
|
||||||
),
|
),
|
||||||
membershipBatch[user.membership].isEmpty
|
membershipBatch[user.membership]!.isEmpty
|
||||||
? Container()
|
? Container()
|
||||||
: Container(
|
: Container(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
@ -59,7 +59,7 @@ class ParticipantListItem extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child:
|
child:
|
||||||
Center(child: Text(membershipBatch[user.membership])),
|
Center(child: Text(membershipBatch[user.membership]!)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -9,7 +9,7 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
import '../key_verification/key_verification_dialog.dart';
|
import '../key_verification/key_verification_dialog.dart';
|
||||||
|
|
||||||
class ChatEncryptionSettings extends StatefulWidget {
|
class ChatEncryptionSettings extends StatefulWidget {
|
||||||
const ChatEncryptionSettings({Key key}) : super(key: key);
|
const ChatEncryptionSettings({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ChatEncryptionSettingsController createState() =>
|
ChatEncryptionSettingsController createState() =>
|
||||||
@ -17,7 +17,7 @@ class ChatEncryptionSettings extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
|
class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
|
||||||
String get roomId => VRouter.of(context).pathParameters['roomid'];
|
String? get roomId => VRouter.of(context).pathParameters['roomid'];
|
||||||
|
|
||||||
Future<void> unblock(DeviceKeys key) async {
|
Future<void> unblock(DeviceKeys key) async {
|
||||||
if (key.blocked) {
|
if (key.blocked) {
|
||||||
@ -27,14 +27,14 @@ class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
|
|||||||
|
|
||||||
Future<void> onSelected(
|
Future<void> onSelected(
|
||||||
BuildContext context, String action, DeviceKeys key) async {
|
BuildContext context, String action, DeviceKeys key) async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'verify':
|
case 'verify':
|
||||||
await unblock(key);
|
await unblock(key);
|
||||||
final req = key.startVerification();
|
final req = key.startVerification();
|
||||||
req.onUpdate = () {
|
req.onUpdate = () {
|
||||||
if (req.state == KeyVerificationState.done) {
|
if (req.state == KeyVerificationState.done) {
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
await KeyVerificationDialog(request: req).show(context);
|
await KeyVerificationDialog(request: req).show(context);
|
||||||
@ -42,10 +42,10 @@ class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
|
|||||||
case 'verify_user':
|
case 'verify_user':
|
||||||
await unblock(key);
|
await unblock(key);
|
||||||
final req =
|
final req =
|
||||||
await room.client.userDeviceKeys[key.userId].startVerification();
|
await room!.client.userDeviceKeys[key.userId]!.startVerification();
|
||||||
req.onUpdate = () {
|
req.onUpdate = () {
|
||||||
if (req.state == KeyVerificationState.done) {
|
if (req.state == KeyVerificationState.done) {
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
await KeyVerificationDialog(request: req).show(context);
|
await KeyVerificationDialog(request: req).show(context);
|
||||||
@ -55,11 +55,11 @@ class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
|
|||||||
await key.setVerified(false);
|
await key.setVerified(false);
|
||||||
}
|
}
|
||||||
await key.setBlocked(true);
|
await key.setBlocked(true);
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
break;
|
break;
|
||||||
case 'unblock':
|
case 'unblock':
|
||||||
await unblock(key);
|
await unblock(key);
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,21 +13,21 @@ import '../../utils/matrix_sdk_extensions.dart/device_extension.dart';
|
|||||||
class ChatEncryptionSettingsView extends StatelessWidget {
|
class ChatEncryptionSettingsView extends StatelessWidget {
|
||||||
final ChatEncryptionSettingsController controller;
|
final ChatEncryptionSettingsController controller;
|
||||||
|
|
||||||
const ChatEncryptionSettingsView(this.controller, {Key key})
|
const ChatEncryptionSettingsView(this.controller, {Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final room = Matrix.of(context).client.getRoomById(controller.roomId);
|
final room = Matrix.of(context).client.getRoomById(controller.roomId!)!;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.close_outlined),
|
icon: const Icon(Icons.close_outlined),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
VRouter.of(context).toSegments(['rooms', controller.roomId]),
|
VRouter.of(context).toSegments(['rooms', controller.roomId!]),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).tapOnDeviceToVerify),
|
title: Text(L10n.of(context)!.tapOnDeviceToVerify),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
body: MaxWidthBody(
|
body: MaxWidthBody(
|
||||||
@ -36,7 +36,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).deviceVerifyDescription),
|
title: Text(L10n.of(context)!.deviceVerifyDescription),
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
||||||
foregroundColor: Theme.of(context).colorScheme.secondary,
|
foregroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
@ -52,7 +52,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(L10n.of(context).oopsSomethingWentWrong +
|
child: Text(L10n.of(context)!.oopsSomethingWentWrong +
|
||||||
': ' +
|
': ' +
|
||||||
snapshot.error.toString()),
|
snapshot.error.toString()),
|
||||||
);
|
);
|
||||||
@ -62,7 +62,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||||||
child: CircularProgressIndicator.adaptive(
|
child: CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 2));
|
strokeWidth: 2));
|
||||||
}
|
}
|
||||||
final deviceKeys = snapshot.data;
|
final deviceKeys = snapshot.data!;
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
@ -75,18 +75,18 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||||||
deviceKeys[i - 1].userId) ...{
|
deviceKeys[i - 1].userId) ...{
|
||||||
const Divider(height: 1, thickness: 1),
|
const Divider(height: 1, thickness: 1),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
onSelected: (action) => controller.onSelected(
|
onSelected: (dynamic action) => controller
|
||||||
context, action, deviceKeys[i]),
|
.onSelected(context, action, deviceKeys[i]),
|
||||||
itemBuilder: (c) {
|
itemBuilder: (c) {
|
||||||
final items = <PopupMenuEntry<String>>[];
|
final items = <PopupMenuEntry<String>>[];
|
||||||
if (room
|
if (room
|
||||||
.client
|
.client
|
||||||
.userDeviceKeys[deviceKeys[i].userId]
|
.userDeviceKeys[deviceKeys[i].userId]!
|
||||||
.verified ==
|
.verified ==
|
||||||
UserVerifiedStatus.unknown) {
|
UserVerifiedStatus.unknown) {
|
||||||
items.add(PopupMenuItem(
|
items.add(PopupMenuItem(
|
||||||
value: 'verify_user',
|
value: 'verify_user',
|
||||||
child: Text(L10n.of(context).verifyUser),
|
child: Text(L10n.of(context)!.verifyUser),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
@ -114,8 +114,8 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
onSelected: (action) => controller.onSelected(
|
onSelected: (dynamic action) => controller
|
||||||
context, action, deviceKeys[i]),
|
.onSelected(context, action, deviceKeys[i]),
|
||||||
itemBuilder: (c) {
|
itemBuilder: (c) {
|
||||||
final items = <PopupMenuEntry<String>>[];
|
final items = <PopupMenuEntry<String>>[];
|
||||||
if (deviceKeys[i].blocked ||
|
if (deviceKeys[i].blocked ||
|
||||||
@ -125,19 +125,20 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||||||
room.client.userID
|
room.client.userID
|
||||||
? 'verify'
|
? 'verify'
|
||||||
: 'verify_user',
|
: 'verify_user',
|
||||||
child: Text(L10n.of(context).verifyStart),
|
child: Text(L10n.of(context)!.verifyStart),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if (deviceKeys[i].blocked) {
|
if (deviceKeys[i].blocked) {
|
||||||
items.add(PopupMenuItem(
|
items.add(PopupMenuItem(
|
||||||
value: 'unblock',
|
value: 'unblock',
|
||||||
child: Text(L10n.of(context).unblockDevice),
|
child:
|
||||||
|
Text(L10n.of(context)!.unblockDevice),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if (!deviceKeys[i].blocked) {
|
if (!deviceKeys[i].blocked) {
|
||||||
items.add(PopupMenuItem(
|
items.add(PopupMenuItem(
|
||||||
value: 'block',
|
value: 'block',
|
||||||
child: Text(L10n.of(context).blockDevice),
|
child: Text(L10n.of(context)!.blockDevice),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
@ -156,17 +157,17 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||||||
subtitle: Row(
|
subtitle: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
deviceKeys[i].deviceId,
|
deviceKeys[i].deviceId!,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.w300),
|
fontWeight: FontWeight.w300),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
Text(
|
||||||
deviceKeys[i].blocked
|
deviceKeys[i].blocked
|
||||||
? L10n.of(context).blocked
|
? L10n.of(context)!.blocked
|
||||||
: deviceKeys[i].verified
|
: deviceKeys[i].verified
|
||||||
? L10n.of(context).verified
|
? L10n.of(context)!.verified
|
||||||
: L10n.of(context).unverified,
|
: L10n.of(context)!.unverified,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: deviceKeys[i].color,
|
color: deviceKeys[i].color,
|
||||||
|
@ -34,25 +34,28 @@ enum PopupMenuAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ChatList extends StatefulWidget {
|
class ChatList extends StatefulWidget {
|
||||||
const ChatList({Key key}) : super(key: key);
|
const ChatList({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ChatListController createState() => ChatListController();
|
ChatListController createState() => ChatListController();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatListController extends State<ChatList> {
|
class ChatListController extends State<ChatList> {
|
||||||
StreamSubscription _intentDataStreamSubscription;
|
StreamSubscription? _intentDataStreamSubscription;
|
||||||
|
|
||||||
StreamSubscription _intentFileStreamSubscription;
|
StreamSubscription? _intentFileStreamSubscription;
|
||||||
|
|
||||||
StreamSubscription _intentUriStreamSubscription;
|
StreamSubscription? _intentUriStreamSubscription;
|
||||||
|
|
||||||
String _activeSpaceId;
|
String? _activeSpaceId;
|
||||||
|
|
||||||
String get activeSpaceId =>
|
String? get activeSpaceId {
|
||||||
Matrix.of(context).client.getRoomById(_activeSpaceId) == null
|
final id = _activeSpaceId;
|
||||||
|
return id != null && Matrix.of(context).client.getRoomById(id) == null
|
||||||
? null
|
? null
|
||||||
: _activeSpaceId;
|
: _activeSpaceId;
|
||||||
|
}
|
||||||
|
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
bool scrolledToTop = true;
|
bool scrolledToTop = true;
|
||||||
|
|
||||||
@ -69,12 +72,12 @@ class ChatListController extends State<ChatList> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setActiveSpaceId(BuildContext context, String spaceId) {
|
void setActiveSpaceId(BuildContext context, String? spaceId) {
|
||||||
setState(() => _activeSpaceId = spaceId);
|
setState(() => _activeSpaceId = spaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void editSpace(BuildContext context, String spaceId) async {
|
void editSpace(BuildContext context, String spaceId) async {
|
||||||
await Matrix.of(context).client.getRoomById(spaceId).postLoad();
|
await Matrix.of(context).client.getRoomById(spaceId)!.postLoad();
|
||||||
VRouter.of(context).toSegments(['spaces', spaceId]);
|
VRouter.of(context).toSegments(['spaces', spaceId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +85,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
Matrix.of(context).client.rooms.where((r) => r.isSpace).toList();
|
Matrix.of(context).client.rooms.where((r) => r.isSpace).toList();
|
||||||
|
|
||||||
final selectedRoomIds = <String>{};
|
final selectedRoomIds = <String>{};
|
||||||
bool crossSigningCached;
|
bool? crossSigningCached;
|
||||||
bool showChatBackupBanner = false;
|
bool showChatBackupBanner = false;
|
||||||
|
|
||||||
void firstRunBootstrapAction() async {
|
void firstRunBootstrapAction() async {
|
||||||
@ -96,7 +99,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
VRouter.of(context).to('/rooms');
|
VRouter.of(context).to('/rooms');
|
||||||
}
|
}
|
||||||
|
|
||||||
String get activeChat => VRouter.of(context).pathParameters['roomid'];
|
String? get activeChat => VRouter.of(context).pathParameters['roomid'];
|
||||||
|
|
||||||
SelectMode get selectMode => Matrix.of(context).shareContent != null
|
SelectMode get selectMode => Matrix.of(context).shareContent != null
|
||||||
? SelectMode.share
|
? SelectMode.share
|
||||||
@ -105,7 +108,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
: SelectMode.select;
|
: SelectMode.select;
|
||||||
|
|
||||||
void _processIncomingSharedFiles(List<SharedMediaFile> files) {
|
void _processIncomingSharedFiles(List<SharedMediaFile> files) {
|
||||||
if (files?.isEmpty ?? true) return;
|
if (files.isEmpty) return;
|
||||||
VRouter.of(context).to('/rooms');
|
VRouter.of(context).to('/rooms');
|
||||||
final file = File(files.first.path);
|
final file = File(files.first.path);
|
||||||
|
|
||||||
@ -118,7 +121,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void _processIncomingSharedText(String text) {
|
void _processIncomingSharedText(String? text) {
|
||||||
if (text == null) return;
|
if (text == null) return;
|
||||||
VRouter.of(context).to('/rooms');
|
VRouter.of(context).to('/rooms');
|
||||||
if (text.toLowerCase().startsWith(AppConfig.deepLinkPrefix) ||
|
if (text.toLowerCase().startsWith(AppConfig.deepLinkPrefix) ||
|
||||||
@ -133,10 +136,10 @@ class ChatListController extends State<ChatList> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void _processIncomingUris(String text) async {
|
void _processIncomingUris(String? text) async {
|
||||||
if (text == null) return;
|
if (text == null) return;
|
||||||
VRouter.of(context).to('/rooms');
|
VRouter.of(context).to('/rooms');
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
UrlLauncher(context, text).openMatrixToUrl();
|
UrlLauncher(context, text).openMatrixToUrl();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -180,10 +183,10 @@ class ChatListController extends State<ChatList> {
|
|||||||
await Matrix.of(context).client.accountDataLoading;
|
await Matrix.of(context).client.accountDataLoading;
|
||||||
await Matrix.of(context).client.userDeviceKeysLoading;
|
await Matrix.of(context).client.userDeviceKeysLoading;
|
||||||
final crossSigning =
|
final crossSigning =
|
||||||
await Matrix.of(context).client.encryption?.crossSigning?.isCached() ??
|
await Matrix.of(context).client.encryption?.crossSigning.isCached() ??
|
||||||
false;
|
false;
|
||||||
final needsBootstrap =
|
final needsBootstrap =
|
||||||
Matrix.of(context).client.encryption?.crossSigning?.enabled == false ||
|
Matrix.of(context).client.encryption?.crossSigning.enabled == false ||
|
||||||
crossSigning == false;
|
crossSigning == false;
|
||||||
final isUnknownSession = Matrix.of(context).client.isUnknownSession;
|
final isUnknownSession = Matrix.of(context).client.isUnknownSession;
|
||||||
if (needsBootstrap || isUnknownSession) {
|
if (needsBootstrap || isUnknownSession) {
|
||||||
@ -206,23 +209,21 @@ class ChatListController extends State<ChatList> {
|
|||||||
if (room.isSpace && room.membership == Membership.join && !room.isUnread) {
|
if (room.isSpace && room.membership == Membership.join && !room.isUnread) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (room.getState(EventTypes.RoomCreate)?.content?.tryGet<String>('type') ==
|
if (room.getState(EventTypes.RoomCreate)?.content.tryGet<String>('type') ==
|
||||||
ClientStoriesExtension.storiesRoomType) {
|
ClientStoriesExtension.storiesRoomType) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (activeSpaceId != null) {
|
if (activeSpaceId != null) {
|
||||||
final space = Matrix.of(context).client.getRoomById(activeSpaceId);
|
final space = Matrix.of(context).client.getRoomById(activeSpaceId!)!;
|
||||||
if (space.spaceChildren?.any((child) => child.roomId == room.id) ??
|
if (space.spaceChildren.any((child) => child.roomId == room.id)) {
|
||||||
false) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (room.spaceParents?.any((parent) => parent.roomId == activeSpaceId) ??
|
if (room.spaceParents.any((parent) => parent.roomId == activeSpaceId)) {
|
||||||
false) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (room.isDirectChat &&
|
if (room.isDirectChat &&
|
||||||
room.summary?.mHeroes != null &&
|
room.summary.mHeroes != null &&
|
||||||
room.summary.mHeroes.any((userId) {
|
room.summary.mHeroes!.any((userId) {
|
||||||
final user = space.getState(EventTypes.RoomMember, userId)?.asUser;
|
final user = space.getState(EventTypes.RoomMember, userId)?.asUser;
|
||||||
return user != null && user.membership == Membership.join;
|
return user != null && user.membership == Membership.join;
|
||||||
})) {
|
})) {
|
||||||
@ -246,9 +247,9 @@ class ChatListController extends State<ChatList> {
|
|||||||
final markUnread = anySelectedRoomNotMarkedUnread;
|
final markUnread = anySelectedRoomNotMarkedUnread;
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
for (final roomId in selectedRoomIds) {
|
for (final roomId in selectedRoomIds) {
|
||||||
final room = client.getRoomById(roomId);
|
final room = client.getRoomById(roomId)!;
|
||||||
if (room.markedUnread == markUnread) continue;
|
if (room.markedUnread == markUnread) continue;
|
||||||
await client.getRoomById(roomId).markUnread(markUnread);
|
await client.getRoomById(roomId)!.markUnread(markUnread);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -262,9 +263,9 @@ class ChatListController extends State<ChatList> {
|
|||||||
final makeFavorite = anySelectedRoomNotFavorite;
|
final makeFavorite = anySelectedRoomNotFavorite;
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
for (final roomId in selectedRoomIds) {
|
for (final roomId in selectedRoomIds) {
|
||||||
final room = client.getRoomById(roomId);
|
final room = client.getRoomById(roomId)!;
|
||||||
if (room.isFavourite == makeFavorite) continue;
|
if (room.isFavourite == makeFavorite) continue;
|
||||||
await client.getRoomById(roomId).setFavourite(makeFavorite);
|
await client.getRoomById(roomId)!.setFavourite(makeFavorite);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -280,9 +281,9 @@ class ChatListController extends State<ChatList> {
|
|||||||
: PushRuleState.notify;
|
: PushRuleState.notify;
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
for (final roomId in selectedRoomIds) {
|
for (final roomId in selectedRoomIds) {
|
||||||
final room = client.getRoomById(roomId);
|
final room = client.getRoomById(roomId)!;
|
||||||
if (room.pushRuleState == newState) continue;
|
if (room.pushRuleState == newState) continue;
|
||||||
await client.getRoomById(roomId).setPushRuleState(newState);
|
await client.getRoomById(roomId)!.setPushRuleState(newState);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -293,9 +294,9 @@ class ChatListController extends State<ChatList> {
|
|||||||
final confirmed = await showOkCancelAlertDialog(
|
final confirmed = await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: L10n.of(context)!.areYouSure,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: L10n.of(context)!.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.ok;
|
OkCancelResult.ok;
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
@ -303,26 +304,26 @@ class ChatListController extends State<ChatList> {
|
|||||||
context: context,
|
context: context,
|
||||||
future: () => _archiveSelectedRooms(),
|
future: () => _archiveSelectedRooms(),
|
||||||
);
|
);
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void setStatus() async {
|
void setStatus() async {
|
||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).setStatus,
|
title: L10n.of(context)!.setStatus,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
hintText: L10n.of(context).statusExampleMessage,
|
hintText: L10n.of(context)!.statusExampleMessage,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
if (input == null) return;
|
if (input == null) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context).client.setPresence(
|
future: () => Matrix.of(context).client.setPresence(
|
||||||
Matrix.of(context).client.userID,
|
Matrix.of(context).client.userID!,
|
||||||
PresenceType.online,
|
PresenceType.online,
|
||||||
statusMsg: input.single,
|
statusMsg: input.single,
|
||||||
),
|
),
|
||||||
@ -339,7 +340,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
break;
|
break;
|
||||||
case PopupMenuAction.invite:
|
case PopupMenuAction.invite:
|
||||||
FluffyShare.share(
|
FluffyShare.share(
|
||||||
L10n.of(context).inviteText(Matrix.of(context).client.userID,
|
L10n.of(context)!.inviteText(Matrix.of(context).client.userID!,
|
||||||
'https://matrix.to/#/${Matrix.of(context).client.userID}?client=im.fluffychat'),
|
'https://matrix.to/#/${Matrix.of(context).client.userID}?client=im.fluffychat'),
|
||||||
context);
|
context);
|
||||||
break;
|
break;
|
||||||
@ -360,7 +361,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
while (selectedRoomIds.isNotEmpty) {
|
while (selectedRoomIds.isNotEmpty) {
|
||||||
final roomId = selectedRoomIds.first;
|
final roomId = selectedRoomIds.first;
|
||||||
try {
|
try {
|
||||||
await client.getRoomById(roomId).leave();
|
await client.getRoomById(roomId)!.leave();
|
||||||
} finally {
|
} finally {
|
||||||
toggleSelection(roomId);
|
toggleSelection(roomId);
|
||||||
}
|
}
|
||||||
@ -371,36 +372,36 @@ class ChatListController extends State<ChatList> {
|
|||||||
if (activeSpaceId != null) {
|
if (activeSpaceId != null) {
|
||||||
final consent = await showOkCancelAlertDialog(
|
final consent = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).removeFromSpace,
|
title: L10n.of(context)!.removeFromSpace,
|
||||||
message: L10n.of(context).removeFromSpaceDescription,
|
message: L10n.of(context)!.removeFromSpaceDescription,
|
||||||
okLabel: L10n.of(context).remove,
|
okLabel: L10n.of(context)!.remove,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
fullyCapitalizedForMaterial: false,
|
fullyCapitalizedForMaterial: false,
|
||||||
);
|
);
|
||||||
if (consent != OkCancelResult.ok) return;
|
if (consent != OkCancelResult.ok) return;
|
||||||
|
|
||||||
final space = Matrix.of(context).client.getRoomById(activeSpaceId);
|
final space = Matrix.of(context).client.getRoomById(activeSpaceId!);
|
||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () async {
|
future: () async {
|
||||||
for (final roomId in selectedRoomIds) {
|
for (final roomId in selectedRoomIds) {
|
||||||
await space.removeSpaceChild(roomId);
|
await space!.removeSpaceChild(roomId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (result.error == null) {
|
if (result.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(L10n.of(context).chatHasBeenRemovedFromThisSpace),
|
content: Text(L10n.of(context)!.chatHasBeenRemovedFromThisSpace),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final selectedSpace = await showConfirmationDialog<String>(
|
final selectedSpace = await showConfirmationDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).addToSpace,
|
title: L10n.of(context)!.addToSpace,
|
||||||
message: L10n.of(context).addToSpaceDescription,
|
message: L10n.of(context)!.addToSpaceDescription,
|
||||||
fullyCapitalizedForMaterial: false,
|
fullyCapitalizedForMaterial: false,
|
||||||
actions: Matrix.of(context)
|
actions: Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
@ -417,7 +418,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
final result = await showFutureLoadingDialog(
|
final result = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () async {
|
future: () async {
|
||||||
final space = Matrix.of(context).client.getRoomById(selectedSpace);
|
final space = Matrix.of(context).client.getRoomById(selectedSpace)!;
|
||||||
if (space.canSendDefaultStates) {
|
if (space.canSendDefaultStates) {
|
||||||
for (final roomId in selectedRoomIds) {
|
for (final roomId in selectedRoomIds) {
|
||||||
await space.setSpaceChild(roomId);
|
await space.setSpaceChild(roomId);
|
||||||
@ -428,7 +429,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
if (result.error == null) {
|
if (result.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(L10n.of(context).chatHasBeenAddedToThisSpace),
|
content: Text(L10n.of(context)!.chatHasBeenAddedToThisSpace),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -437,13 +438,13 @@ class ChatListController extends State<ChatList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool get anySelectedRoomNotMarkedUnread => selectedRoomIds.any(
|
bool get anySelectedRoomNotMarkedUnread => selectedRoomIds.any(
|
||||||
(roomId) => !Matrix.of(context).client.getRoomById(roomId).markedUnread);
|
(roomId) => !Matrix.of(context).client.getRoomById(roomId)!.markedUnread);
|
||||||
|
|
||||||
bool get anySelectedRoomNotFavorite => selectedRoomIds.any(
|
bool get anySelectedRoomNotFavorite => selectedRoomIds.any(
|
||||||
(roomId) => !Matrix.of(context).client.getRoomById(roomId).isFavourite);
|
(roomId) => !Matrix.of(context).client.getRoomById(roomId)!.isFavourite);
|
||||||
|
|
||||||
bool get anySelectedRoomNotMuted => selectedRoomIds.any((roomId) =>
|
bool get anySelectedRoomNotMuted => selectedRoomIds.any((roomId) =>
|
||||||
Matrix.of(context).client.getRoomById(roomId).pushRuleState ==
|
Matrix.of(context).client.getRoomById(roomId)!.pushRuleState ==
|
||||||
PushRuleState.notify);
|
PushRuleState.notify);
|
||||||
|
|
||||||
bool waitForFirstSync = false;
|
bool waitForFirstSync = false;
|
||||||
@ -457,10 +458,10 @@ class ChatListController extends State<ChatList> {
|
|||||||
}
|
}
|
||||||
// Load space members to display DM rooms
|
// Load space members to display DM rooms
|
||||||
if (activeSpaceId != null) {
|
if (activeSpaceId != null) {
|
||||||
final space = client.getRoomById(activeSpaceId);
|
final space = client.getRoomById(activeSpaceId!)!;
|
||||||
final localMembers = space.getParticipants().length;
|
final localMembers = space.getParticipants().length;
|
||||||
final actualMembersCount = (space.summary?.mInvitedMemberCount ?? 0) +
|
final actualMembersCount = (space.summary.mInvitedMemberCount ?? 0) +
|
||||||
(space.summary?.mJoinedMemberCount ?? 0);
|
(space.summary.mJoinedMemberCount ?? 0);
|
||||||
if (localMembers < actualMembersCount) {
|
if (localMembers < actualMembersCount) {
|
||||||
await space.requestParticipants();
|
await space.requestParticipants();
|
||||||
}
|
}
|
||||||
@ -468,7 +469,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
waitForFirstSync = true;
|
waitForFirstSync = true;
|
||||||
});
|
});
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => checkBootstrap());
|
WidgetsBinding.instance!.addPostFrameCallback((_) => checkBootstrap());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,7 +482,6 @@ class ChatListController extends State<ChatList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setActiveClient(Client client) {
|
void setActiveClient(Client client) {
|
||||||
if (client == null) return;
|
|
||||||
VRouter.of(context).to('/rooms');
|
VRouter.of(context).to('/rooms');
|
||||||
setState(() {
|
setState(() {
|
||||||
_activeSpaceId = null;
|
_activeSpaceId = null;
|
||||||
@ -498,30 +498,30 @@ class ChatListController extends State<ChatList> {
|
|||||||
selectedRoomIds.clear();
|
selectedRoomIds.clear();
|
||||||
Matrix.of(context).activeBundle = bundle;
|
Matrix.of(context).activeBundle = bundle;
|
||||||
if (!Matrix.of(context)
|
if (!Matrix.of(context)
|
||||||
.currentBundle
|
.currentBundle!
|
||||||
.any((client) => client == Matrix.of(context).client)) {
|
.any((client) => client == Matrix.of(context).client)) {
|
||||||
Matrix.of(context)
|
Matrix.of(context)
|
||||||
.setActiveClient(Matrix.of(context).currentBundle.first);
|
.setActiveClient(Matrix.of(context).currentBundle!.first);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void editBundlesForAccount(String userId, String activeBundle) async {
|
void editBundlesForAccount(String? userId, String? activeBundle) async {
|
||||||
final client = Matrix.of(context)
|
final client = Matrix.of(context)
|
||||||
.widget
|
.widget
|
||||||
.clients[Matrix.of(context).getClientIndexByMatrixId(userId)];
|
.clients[Matrix.of(context).getClientIndexByMatrixId(userId!)];
|
||||||
final action = await showConfirmationDialog<EditBundleAction>(
|
final action = await showConfirmationDialog<EditBundleAction>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).editBundlesForAccount,
|
title: L10n.of(context)!.editBundlesForAccount,
|
||||||
actions: [
|
actions: [
|
||||||
AlertDialogAction(
|
AlertDialogAction(
|
||||||
key: EditBundleAction.addToBundle,
|
key: EditBundleAction.addToBundle,
|
||||||
label: L10n.of(context).addToBundle,
|
label: L10n.of(context)!.addToBundle,
|
||||||
),
|
),
|
||||||
if (activeBundle != client.userID)
|
if (activeBundle != client.userID)
|
||||||
AlertDialogAction(
|
AlertDialogAction(
|
||||||
key: EditBundleAction.removeFromBundle,
|
key: EditBundleAction.removeFromBundle,
|
||||||
label: L10n.of(context).removeFromBundle,
|
label: L10n.of(context)!.removeFromBundle,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -530,9 +530,9 @@ class ChatListController extends State<ChatList> {
|
|||||||
case EditBundleAction.addToBundle:
|
case EditBundleAction.addToBundle:
|
||||||
final bundle = await showTextInputDialog(
|
final bundle = await showTextInputDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).bundleName,
|
title: L10n.of(context)!.bundleName,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(hintText: L10n.of(context).bundleName)
|
DialogTextField(hintText: L10n.of(context)!.bundleName)
|
||||||
]);
|
]);
|
||||||
if (bundle == null || bundle.isEmpty || bundle.single.isEmpty) return;
|
if (bundle == null || bundle.isEmpty || bundle.single.isEmpty) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
@ -543,7 +543,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
case EditBundleAction.removeFromBundle:
|
case EditBundleAction.removeFromBundle:
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => client.removeFromAccountBundle(activeBundle),
|
future: () => client.removeFromAccountBundle(activeBundle!),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -552,7 +552,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
Matrix.of(context).hasComplexBundles &&
|
Matrix.of(context).hasComplexBundles &&
|
||||||
Matrix.of(context).accountBundles.keys.length > 1;
|
Matrix.of(context).accountBundles.keys.length > 1;
|
||||||
|
|
||||||
String get secureActiveBundle {
|
String? get secureActiveBundle {
|
||||||
if (Matrix.of(context).activeBundle == null ||
|
if (Matrix.of(context).activeBundle == null ||
|
||||||
!Matrix.of(context)
|
!Matrix.of(context)
|
||||||
.accountBundles
|
.accountBundles
|
||||||
@ -564,7 +564,7 @@ class ChatListController extends State<ChatList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void resetActiveBundle() {
|
void resetActiveBundle() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
|
||||||
setState(() {
|
setState(() {
|
||||||
Matrix.of(context).activeBundle = null;
|
Matrix.of(context).activeBundle = null;
|
||||||
});
|
});
|
||||||
|
@ -21,9 +21,9 @@ class ChatListItem extends StatelessWidget {
|
|||||||
final Room room;
|
final Room room;
|
||||||
final bool activeChat;
|
final bool activeChat;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final Function onForget;
|
final Function? onForget;
|
||||||
final Function onTap;
|
final Function? onTap;
|
||||||
final Function onLongPress;
|
final Function? onLongPress;
|
||||||
|
|
||||||
const ChatListItem(
|
const ChatListItem(
|
||||||
this.room, {
|
this.room, {
|
||||||
@ -32,11 +32,11 @@ class ChatListItem extends StatelessWidget {
|
|||||||
this.onTap,
|
this.onTap,
|
||||||
this.onLongPress,
|
this.onLongPress,
|
||||||
this.onForget,
|
this.onForget,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
dynamic clickAction(BuildContext context) async {
|
dynamic clickAction(BuildContext context) async {
|
||||||
if (onTap != null) return onTap();
|
if (onTap != null) return onTap!();
|
||||||
if (!activeChat) {
|
if (!activeChat) {
|
||||||
if (room.membership == Membership.invite &&
|
if (room.membership == Membership.invite &&
|
||||||
(await showFutureLoadingDialog(
|
(await showFutureLoadingDialog(
|
||||||
@ -57,7 +57,7 @@ class ChatListItem extends StatelessWidget {
|
|||||||
if (room.membership == Membership.ban) {
|
if (room.membership == Membership.ban) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(L10n.of(context).youHaveBeenBannedFromThisChat),
|
content: Text(L10n.of(context)!.youHaveBeenBannedFromThisChat),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -66,15 +66,15 @@ class ChatListItem extends StatelessWidget {
|
|||||||
if (room.membership == Membership.leave) {
|
if (room.membership == Membership.leave) {
|
||||||
final action = await showModalActionSheet<ArchivedRoomAction>(
|
final action = await showModalActionSheet<ArchivedRoomAction>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).archivedRoom,
|
title: L10n.of(context)!.archivedRoom,
|
||||||
message: L10n.of(context).thisRoomHasBeenArchived,
|
message: L10n.of(context)!.thisRoomHasBeenArchived,
|
||||||
actions: [
|
actions: [
|
||||||
SheetAction(
|
SheetAction(
|
||||||
label: L10n.of(context).rejoin,
|
label: L10n.of(context)!.rejoin,
|
||||||
key: ArchivedRoomAction.rejoin,
|
key: ArchivedRoomAction.rejoin,
|
||||||
),
|
),
|
||||||
SheetAction(
|
SheetAction(
|
||||||
label: L10n.of(context).delete,
|
label: L10n.of(context)!.delete,
|
||||||
key: ArchivedRoomAction.delete,
|
key: ArchivedRoomAction.delete,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
),
|
),
|
||||||
@ -97,18 +97,18 @@ class ChatListItem extends StatelessWidget {
|
|||||||
|
|
||||||
if (room.membership == Membership.join) {
|
if (room.membership == Membership.join) {
|
||||||
if (Matrix.of(context).shareContent != null) {
|
if (Matrix.of(context).shareContent != null) {
|
||||||
if (Matrix.of(context).shareContent['msgtype'] ==
|
if (Matrix.of(context).shareContent!['msgtype'] ==
|
||||||
'chat.fluffy.shared_file') {
|
'chat.fluffy.shared_file') {
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
builder: (c) => SendFileDialog(
|
builder: (c) => SendFileDialog(
|
||||||
file: Matrix.of(context).shareContent['file'],
|
file: Matrix.of(context).shareContent!['file'],
|
||||||
room: room,
|
room: room,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
unawaited(room.sendEvent(Matrix.of(context).shareContent));
|
unawaited(room.sendEvent(Matrix.of(context).shareContent!));
|
||||||
}
|
}
|
||||||
Matrix.of(context).shareContent = null;
|
Matrix.of(context).shareContent = null;
|
||||||
}
|
}
|
||||||
@ -125,16 +125,16 @@ class ChatListItem extends StatelessWidget {
|
|||||||
future: () => room.forget(),
|
future: () => room.forget(),
|
||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
if (onForget != null) onForget();
|
if (onForget != null) onForget!();
|
||||||
}
|
}
|
||||||
return success;
|
return;
|
||||||
}
|
}
|
||||||
final confirmed = await showOkCancelAlertDialog(
|
final confirmed = await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: L10n.of(context)!.areYouSure,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: L10n.of(context)!.yes,
|
||||||
cancelLabel: L10n.of(context).no,
|
cancelLabel: L10n.of(context)!.no,
|
||||||
);
|
);
|
||||||
if (confirmed == OkCancelResult.cancel) return;
|
if (confirmed == OkCancelResult.cancel) return;
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
@ -160,7 +160,7 @@ class ChatListItem extends StatelessWidget {
|
|||||||
selectedTileColor: selected
|
selectedTileColor: selected
|
||||||
? Theme.of(context).primaryColor.withAlpha(100)
|
? Theme.of(context).primaryColor.withAlpha(100)
|
||||||
: Theme.of(context).secondaryHeaderColor,
|
: Theme.of(context).secondaryHeaderColor,
|
||||||
onLongPress: onLongPress,
|
onLongPress: onLongPress as void Function()?,
|
||||||
leading: selected
|
leading: selected
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
width: Avatar.defaultSize,
|
width: Avatar.defaultSize,
|
||||||
@ -174,13 +174,13 @@ class ChatListItem extends StatelessWidget {
|
|||||||
: Avatar(
|
: Avatar(
|
||||||
mxContent: room.avatar,
|
mxContent: room.avatar,
|
||||||
name: room.displayname,
|
name: room.displayname,
|
||||||
onTap: onLongPress,
|
onTap: onLongPress as void Function()?,
|
||||||
),
|
),
|
||||||
title: Row(
|
title: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
@ -188,7 +188,7 @@ class ChatListItem extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: unread
|
color: unread
|
||||||
? Theme.of(context).colorScheme.secondary
|
? Theme.of(context).colorScheme.secondary
|
||||||
: Theme.of(context).textTheme.bodyText1.color,
|
: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -218,7 +218,7 @@ class ChatListItem extends StatelessWidget {
|
|||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: unread
|
color: unread
|
||||||
? Theme.of(context).colorScheme.secondary
|
? Theme.of(context).colorScheme.secondary
|
||||||
: Theme.of(context).textTheme.bodyText2.color,
|
: Theme.of(context).textTheme.bodyText2!.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -229,7 +229,7 @@ class ChatListItem extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (typingText.isEmpty &&
|
if (typingText.isEmpty &&
|
||||||
ownMessage &&
|
ownMessage &&
|
||||||
room.lastEvent.status.isSending) ...[
|
room.lastEvent!.status.isSending) ...[
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
@ -261,9 +261,9 @@ class ChatListItem extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
: Text(
|
: Text(
|
||||||
room.membership == Membership.invite
|
room.membership == Membership.invite
|
||||||
? L10n.of(context).youAreInvitedToThisChat
|
? L10n.of(context)!.youAreInvitedToThisChat
|
||||||
: room.lastEvent?.getLocalizedBody(
|
: room.lastEvent?.getLocalizedBody(
|
||||||
MatrixLocals(L10n.of(context)),
|
MatrixLocals(L10n.of(context)!),
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
hideEdit: true,
|
hideEdit: true,
|
||||||
plaintextBody: true,
|
plaintextBody: true,
|
||||||
@ -271,14 +271,14 @@ class ChatListItem extends StatelessWidget {
|
|||||||
room.directChatMatrixID !=
|
room.directChatMatrixID !=
|
||||||
room.lastEvent?.senderId,
|
room.lastEvent?.senderId,
|
||||||
) ??
|
) ??
|
||||||
L10n.of(context).emptyChat,
|
L10n.of(context)!.emptyChat,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: unread
|
color: unread
|
||||||
? Theme.of(context).colorScheme.secondary
|
? Theme.of(context).colorScheme.secondary
|
||||||
: Theme.of(context).textTheme.bodyText2.color,
|
: Theme.of(context).textTheme.bodyText2!.color,
|
||||||
decoration: room.lastEvent?.redacted == true
|
decoration: room.lastEvent?.redacted == true
|
||||||
? TextDecoration.lineThrough
|
? TextDecoration.lineThrough
|
||||||
: null,
|
: null,
|
||||||
|
@ -21,11 +21,11 @@ import '../../widgets/matrix.dart';
|
|||||||
class ChatListView extends StatelessWidget {
|
class ChatListView extends StatelessWidget {
|
||||||
final ChatListController controller;
|
final ChatListController controller;
|
||||||
|
|
||||||
const ChatListView(this.controller, {Key key}) : super(key: key);
|
const ChatListView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return StreamBuilder<Object>(
|
return StreamBuilder<Object?>(
|
||||||
stream: Matrix.of(context).onShareContentChanged.stream,
|
stream: Matrix.of(context).onShareContentChanged.stream,
|
||||||
builder: (_, __) {
|
builder: (_, __) {
|
||||||
final selectMode = controller.selectMode;
|
final selectMode = controller.selectMode;
|
||||||
@ -48,7 +48,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
? ClientChooserButton(controller)
|
? ClientChooserButton(controller)
|
||||||
: null
|
: null
|
||||||
: IconButton(
|
: IconButton(
|
||||||
tooltip: L10n.of(context).cancel,
|
tooltip: L10n.of(context)!.cancel,
|
||||||
icon: const Icon(Icons.close_outlined),
|
icon: const Icon(Icons.close_outlined),
|
||||||
onPressed: controller.cancelAction,
|
onPressed: controller.cancelAction,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
@ -60,12 +60,12 @@ class ChatListView extends StatelessWidget {
|
|||||||
? [
|
? [
|
||||||
if (controller.spaces.isNotEmpty)
|
if (controller.spaces.isNotEmpty)
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: L10n.of(context).addToSpace,
|
tooltip: L10n.of(context)!.addToSpace,
|
||||||
icon: const Icon(Icons.group_work_outlined),
|
icon: const Icon(Icons.group_work_outlined),
|
||||||
onPressed: controller.addOrRemoveToSpace,
|
onPressed: controller.addOrRemoveToSpace,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: L10n.of(context).toggleUnread,
|
tooltip: L10n.of(context)!.toggleUnread,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
controller.anySelectedRoomNotMarkedUnread
|
controller.anySelectedRoomNotMarkedUnread
|
||||||
? Icons.mark_chat_read_outlined
|
? Icons.mark_chat_read_outlined
|
||||||
@ -73,7 +73,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
onPressed: controller.toggleUnread,
|
onPressed: controller.toggleUnread,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: L10n.of(context).toggleFavorite,
|
tooltip: L10n.of(context)!.toggleFavorite,
|
||||||
icon: Icon(controller.anySelectedRoomNotFavorite
|
icon: Icon(controller.anySelectedRoomNotFavorite
|
||||||
? Icons.push_pin_outlined
|
? Icons.push_pin_outlined
|
||||||
: Icons.push_pin),
|
: Icons.push_pin),
|
||||||
@ -83,19 +83,19 @@ class ChatListView extends StatelessWidget {
|
|||||||
icon: Icon(controller.anySelectedRoomNotMuted
|
icon: Icon(controller.anySelectedRoomNotMuted
|
||||||
? Icons.notifications_off_outlined
|
? Icons.notifications_off_outlined
|
||||||
: Icons.notifications_outlined),
|
: Icons.notifications_outlined),
|
||||||
tooltip: L10n.of(context).toggleMuted,
|
tooltip: L10n.of(context)!.toggleMuted,
|
||||||
onPressed: controller.toggleMuted,
|
onPressed: controller.toggleMuted,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.delete_outlined),
|
icon: const Icon(Icons.delete_outlined),
|
||||||
tooltip: L10n.of(context).archive,
|
tooltip: L10n.of(context)!.archive,
|
||||||
onPressed: controller.archiveAction,
|
onPressed: controller.archiveAction,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.search_outlined),
|
icon: const Icon(Icons.search_outlined),
|
||||||
tooltip: L10n.of(context).search,
|
tooltip: L10n.of(context)!.search,
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
VRouter.of(context).to('/search'),
|
VRouter.of(context).to('/search'),
|
||||||
),
|
),
|
||||||
@ -109,7 +109,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.edit_outlined),
|
const Icon(Icons.edit_outlined),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(L10n.of(context).setStatus),
|
Text(L10n.of(context)!.setStatus),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -120,7 +120,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.group_add_outlined),
|
const Icon(Icons.group_add_outlined),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(L10n.of(context).createNewGroup),
|
Text(L10n.of(context)!.createNewGroup),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -131,7 +131,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.group_work_outlined),
|
const Icon(Icons.group_work_outlined),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(L10n.of(context).createNewSpace),
|
Text(L10n.of(context)!.createNewSpace),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -142,7 +142,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.share_outlined),
|
const Icon(Icons.share_outlined),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(L10n.of(context).inviteContact),
|
Text(L10n.of(context)!.inviteContact),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -153,7 +153,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.archive_outlined),
|
const Icon(Icons.archive_outlined),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(L10n.of(context).archive),
|
Text(L10n.of(context)!.archive),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -164,7 +164,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.settings_outlined),
|
const Icon(Icons.settings_outlined),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(L10n.of(context).settings),
|
Text(L10n.of(context)!.settings),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -172,14 +172,14 @@ class ChatListView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
title: Text(selectMode == SelectMode.share
|
title: Text(selectMode == SelectMode.share
|
||||||
? L10n.of(context).share
|
? L10n.of(context)!.share
|
||||||
: selectMode == SelectMode.select
|
: selectMode == SelectMode.select
|
||||||
? controller.selectedRoomIds.length.toString()
|
? controller.selectedRoomIds.length.toString()
|
||||||
: controller.activeSpaceId == null
|
: controller.activeSpaceId == null
|
||||||
? AppConfig.applicationName
|
? AppConfig.applicationName
|
||||||
: Matrix.of(context)
|
: Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.getRoomById(controller.activeSpaceId)
|
.getRoomById(controller.activeSpaceId!)!
|
||||||
.displayname),
|
.displayname),
|
||||||
),
|
),
|
||||||
body: Column(children: [
|
body: Column(children: [
|
||||||
@ -197,7 +197,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
width: 44,
|
width: 44,
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).setupChatBackupNow),
|
title: Text(L10n.of(context)!.setupChatBackupNow),
|
||||||
trailing: const Icon(Icons.chevron_right_outlined),
|
trailing: const Icon(Icons.chevron_right_outlined),
|
||||||
onTap: controller.firstRunBootstrapAction,
|
onTap: controller.firstRunBootstrapAction,
|
||||||
),
|
),
|
||||||
@ -211,7 +211,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
VRouter.of(context).to('/newprivatechat'),
|
VRouter.of(context).to('/newprivatechat'),
|
||||||
icon: const Icon(CupertinoIcons.chat_bubble),
|
icon: const Icon(CupertinoIcons.chat_bubble),
|
||||||
label: Text(L10n.of(context).newChat),
|
label: Text(L10n.of(context)!.newChat),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
bottomNavigationBar: Column(
|
bottomNavigationBar: Column(
|
||||||
@ -232,7 +232,7 @@ class ChatListView extends StatelessWidget {
|
|||||||
class _ChatListViewBody extends StatefulWidget {
|
class _ChatListViewBody extends StatefulWidget {
|
||||||
final ChatListController controller;
|
final ChatListController controller;
|
||||||
|
|
||||||
const _ChatListViewBody(this.controller, {Key key}) : super(key: key);
|
const _ChatListViewBody(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_ChatListViewBody> createState() => _ChatListViewBodyState();
|
State<_ChatListViewBody> createState() => _ChatListViewBodyState();
|
||||||
@ -240,12 +240,12 @@ class _ChatListViewBody extends StatefulWidget {
|
|||||||
|
|
||||||
class _ChatListViewBodyState extends State<_ChatListViewBody> {
|
class _ChatListViewBodyState extends State<_ChatListViewBody> {
|
||||||
// the matrix sync stream
|
// the matrix sync stream
|
||||||
StreamSubscription _subscription;
|
late StreamSubscription _subscription;
|
||||||
StreamSubscription _clientSubscription;
|
late StreamSubscription _clientSubscription;
|
||||||
|
|
||||||
// used to check the animation direction
|
// used to check the animation direction
|
||||||
String _lastUserId;
|
String? _lastUserId;
|
||||||
String _lastSpaceId;
|
String? _lastSpaceId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -285,7 +285,7 @@ class _ChatListViewBodyState extends State<_ChatListViewBody> {
|
|||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).startYourFirstChat,
|
L10n.of(context)!.startYourFirstChat,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
@ -324,9 +324,9 @@ class _ChatListViewBodyState extends State<_ChatListViewBody> {
|
|||||||
} else {
|
} else {
|
||||||
const dummyChatCount = 8;
|
const dummyChatCount = 8;
|
||||||
final titleColor =
|
final titleColor =
|
||||||
Theme.of(context).textTheme.bodyText1.color.withAlpha(100);
|
Theme.of(context).textTheme.bodyText1!.color!.withAlpha(100);
|
||||||
final subtitleColor =
|
final subtitleColor =
|
||||||
Theme.of(context).textTheme.bodyText1.color.withAlpha(50);
|
Theme.of(context).textTheme.bodyText1!.color!.withAlpha(50);
|
||||||
child = ListView.builder(
|
child = ListView.builder(
|
||||||
itemCount: dummyChatCount,
|
itemCount: dummyChatCount,
|
||||||
itemBuilder: (context, i) => Opacity(
|
itemBuilder: (context, i) => Opacity(
|
||||||
@ -336,7 +336,7 @@ class _ChatListViewBodyState extends State<_ChatListViewBody> {
|
|||||||
backgroundColor: titleColor,
|
backgroundColor: titleColor,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
color: Theme.of(context).textTheme.bodyText1.color,
|
color: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: Row(
|
title: Row(
|
||||||
@ -417,11 +417,11 @@ class _ChatListViewBodyState extends State<_ChatListViewBody> {
|
|||||||
final newClient = Matrix.of(context).client;
|
final newClient = Matrix.of(context).client;
|
||||||
if (_lastUserId != newClient.userID) {
|
if (_lastUserId != newClient.userID) {
|
||||||
reversed = Matrix.of(context)
|
reversed = Matrix.of(context)
|
||||||
.currentBundle
|
.currentBundle!
|
||||||
.indexWhere((element) => element.userID == _lastUserId) <
|
.indexWhere((element) => element!.userID == _lastUserId) <
|
||||||
Matrix.of(context)
|
Matrix.of(context)
|
||||||
.currentBundle
|
.currentBundle!
|
||||||
.indexWhere((element) => element.userID == newClient.userID);
|
.indexWhere((element) => element!.userID == newClient.userID);
|
||||||
}
|
}
|
||||||
// otherwise, the space changed...
|
// otherwise, the space changed...
|
||||||
else {
|
else {
|
||||||
|
@ -8,20 +8,20 @@ import 'chat_list.dart';
|
|||||||
|
|
||||||
class ClientChooserButton extends StatelessWidget {
|
class ClientChooserButton extends StatelessWidget {
|
||||||
final ChatListController controller;
|
final ChatListController controller;
|
||||||
const ClientChooserButton(this.controller, {Key key}) : super(key: key);
|
const ClientChooserButton(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
List<PopupMenuEntry<Object>> _bundleMenuItems(BuildContext context) {
|
List<PopupMenuEntry<Object>> _bundleMenuItems(BuildContext context) {
|
||||||
final matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
final bundles = matrix.accountBundles.keys.toList()
|
final bundles = matrix.accountBundles.keys.toList()
|
||||||
..sort((a, b) => a.isValidMatrixId == b.isValidMatrixId
|
..sort((a, b) => a!.isValidMatrixId == b!.isValidMatrixId
|
||||||
? 0
|
? 0
|
||||||
: a.isValidMatrixId && !b.isValidMatrixId
|
: a.isValidMatrixId && !b.isValidMatrixId
|
||||||
? -1
|
? -1
|
||||||
: 1);
|
: 1);
|
||||||
return <PopupMenuEntry<Object>>[
|
return <PopupMenuEntry<Object>>[
|
||||||
for (final bundle in bundles) ...[
|
for (final bundle in bundles) ...[
|
||||||
if (matrix.accountBundles[bundle].length != 1 ||
|
if (matrix.accountBundles[bundle]!.length != 1 ||
|
||||||
matrix.accountBundles[bundle].single.userID != bundle)
|
matrix.accountBundles[bundle]!.single!.userID != bundle)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: null,
|
value: null,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -29,9 +29,9 @@ class ClientChooserButton extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
bundle,
|
bundle!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).textTheme.subtitle1.color,
|
color: Theme.of(context).textTheme.subtitle1!.color,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -39,25 +39,26 @@ class ClientChooserButton extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
...matrix.accountBundles[bundle]
|
...matrix.accountBundles[bundle]!
|
||||||
.map(
|
.map(
|
||||||
(client) => PopupMenuItem(
|
(client) => PopupMenuItem(
|
||||||
value: client,
|
value: client,
|
||||||
child: FutureBuilder<Profile>(
|
child: FutureBuilder<Profile>(
|
||||||
future: client.ownProfile,
|
future: client!.ownProfile,
|
||||||
builder: (context, snapshot) => Row(
|
builder: (context, snapshot) => Row(
|
||||||
children: [
|
children: [
|
||||||
Avatar(
|
Avatar(
|
||||||
mxContent: snapshot.data?.avatarUrl,
|
mxContent: snapshot.data?.avatarUrl,
|
||||||
name: snapshot.data?.displayName ??
|
name: snapshot.data?.displayName ??
|
||||||
client.userID.localpart,
|
client.userID!.localpart,
|
||||||
size: 28,
|
size: 28,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
snapshot.data?.displayName ?? client.userID.localpart,
|
snapshot.data?.displayName ??
|
||||||
|
client.userID!.localpart!,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -86,7 +87,7 @@ class ClientChooserButton extends StatelessWidget {
|
|||||||
builder: (context, snapshot) => PopupMenuButton<Object>(
|
builder: (context, snapshot) => PopupMenuButton<Object>(
|
||||||
child: Avatar(
|
child: Avatar(
|
||||||
mxContent: snapshot.data?.avatarUrl,
|
mxContent: snapshot.data?.avatarUrl,
|
||||||
name: snapshot.data?.displayName ?? matrix.client.userID.localpart,
|
name: snapshot.data?.displayName ?? matrix.client.userID!.localpart,
|
||||||
size: 28,
|
size: 28,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
|
@ -10,7 +10,7 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
|
|
||||||
class SpacesBottomBar extends StatelessWidget {
|
class SpacesBottomBar extends StatelessWidget {
|
||||||
final ChatListController controller;
|
final ChatListController controller;
|
||||||
const SpacesBottomBar(this.controller, {Key key}) : super(key: key);
|
const SpacesBottomBar(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -25,8 +25,9 @@ class SpacesBottomBar extends StatelessWidget {
|
|||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: StreamBuilder<Object>(
|
child: StreamBuilder<Object>(
|
||||||
stream: Matrix.of(context).client.onSync.stream.where((sync) =>
|
stream: Matrix.of(context).client.onSync.stream.where((sync) =>
|
||||||
(sync.rooms?.join?.values?.any((r) =>
|
(sync.rooms?.join?.values.any((r) =>
|
||||||
r.state?.any((s) => s.type.startsWith('m.space'))) ??
|
r.state?.any((s) => s.type.startsWith('m.space')) ??
|
||||||
|
false) ??
|
||||||
false) ||
|
false) ||
|
||||||
(sync.rooms?.leave?.isNotEmpty ?? false)),
|
(sync.rooms?.leave?.isNotEmpty ?? false)),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -48,7 +49,7 @@ class SpacesBottomBar extends StatelessWidget {
|
|||||||
icon: const Icon(CupertinoIcons.chat_bubble_2),
|
icon: const Icon(CupertinoIcons.chat_bubble_2),
|
||||||
activeIcon:
|
activeIcon:
|
||||||
const Icon(CupertinoIcons.chat_bubble_2_fill),
|
const Icon(CupertinoIcons.chat_bubble_2_fill),
|
||||||
title: Text(L10n.of(context).allChats),
|
title: Text(L10n.of(context)!.allChats),
|
||||||
),
|
),
|
||||||
...controller.spaces
|
...controller.spaces
|
||||||
.map((space) => SalomonBottomBarItem(
|
.map((space) => SalomonBottomBarItem(
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
@ -13,7 +13,7 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
import 'package:fluffychat/widgets/permission_slider_dialog.dart';
|
import 'package:fluffychat/widgets/permission_slider_dialog.dart';
|
||||||
|
|
||||||
class ChatPermissionsSettings extends StatefulWidget {
|
class ChatPermissionsSettings extends StatefulWidget {
|
||||||
const ChatPermissionsSettings({Key key}) : super(key: key);
|
const ChatPermissionsSettings({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ChatPermissionsSettingsController createState() =>
|
ChatPermissionsSettingsController createState() =>
|
||||||
@ -21,13 +21,13 @@ class ChatPermissionsSettings extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
||||||
String get roomId => VRouter.of(context).pathParameters['roomid'];
|
String? get roomId => VRouter.of(context).pathParameters['roomid'];
|
||||||
void editPowerLevel(BuildContext context, String key, int currentLevel,
|
void editPowerLevel(BuildContext context, String key, int currentLevel,
|
||||||
{String category}) async {
|
{String? category}) async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||||
if (!room.canSendEvent(EventTypes.RoomPowerLevels)) {
|
if (!room.canSendEvent(EventTypes.RoomPowerLevels)) {
|
||||||
ScaffoldMessenger.of(context)
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
.showSnackBar(SnackBar(content: Text(L10n.of(context).noPermission)));
|
SnackBar(content: Text(L10n.of(context)!.noPermission)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final newLevel =
|
final newLevel =
|
||||||
@ -35,7 +35,7 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
|||||||
.show(context);
|
.show(context);
|
||||||
if (newLevel == null) return;
|
if (newLevel == null) return;
|
||||||
final content = Map<String, dynamic>.from(
|
final content = Map<String, dynamic>.from(
|
||||||
room.getState(EventTypes.RoomPowerLevels).content);
|
room.getState(EventTypes.RoomPowerLevels)!.content);
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
if (!content.containsKey(category)) {
|
if (!content.containsKey(category)) {
|
||||||
content[category] = <String, dynamic>{};
|
content[category] = <String, dynamic>{};
|
||||||
@ -58,20 +58,20 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
|||||||
|
|
||||||
Stream get onChanged => Matrix.of(context).client.onSync.stream.where(
|
Stream get onChanged => Matrix.of(context).client.onSync.stream.where(
|
||||||
(e) =>
|
(e) =>
|
||||||
(e?.rooms?.join?.containsKey(roomId) ?? false) &&
|
(e.rooms?.join?.containsKey(roomId) ?? false) &&
|
||||||
(e.rooms.join[roomId]?.timeline?.events
|
(e.rooms!.join![roomId!]?.timeline?.events
|
||||||
?.any((s) => s.type == EventTypes.RoomPowerLevels) ??
|
?.any((s) => s.type == EventTypes.RoomPowerLevels) ??
|
||||||
false),
|
false),
|
||||||
);
|
);
|
||||||
|
|
||||||
void updateRoomAction(Capabilities capabilities) async {
|
void updateRoomAction(Capabilities capabilities) async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||||
final String roomVersion =
|
final String roomVersion =
|
||||||
room.getState(EventTypes.RoomCreate).content['room_version'] ?? '1';
|
room.getState(EventTypes.RoomCreate)!.content['room_version'] ?? '1';
|
||||||
final newVersion = await showConfirmationDialog<String>(
|
final newVersion = await showConfirmationDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).replaceRoomWithNewerVersion,
|
title: L10n.of(context)!.replaceRoomWithNewerVersion,
|
||||||
actions: capabilities.mRoomVersions.available.entries
|
actions: capabilities.mRoomVersions!.available.entries
|
||||||
.where((r) => r.key != roomVersion)
|
.where((r) => r.key != roomVersion)
|
||||||
.map((version) => AlertDialogAction(
|
.map((version) => AlertDialogAction(
|
||||||
key: version.key,
|
key: version.key,
|
||||||
@ -84,15 +84,15 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
|||||||
await showOkCancelAlertDialog(
|
await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: L10n.of(context)!.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
title: L10n.of(context).areYouSure,
|
title: L10n.of(context)!.areYouSure,
|
||||||
)) {
|
)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.client.upgradeRoom(roomId, newVersion),
|
future: () => room.client.upgradeRoom(roomId!, newVersion),
|
||||||
).then((_) => VRouter.of(context).pop());
|
).then((_) => VRouter.of(context).pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
class ChatPermissionsSettingsView extends StatelessWidget {
|
class ChatPermissionsSettingsView extends StatelessWidget {
|
||||||
final ChatPermissionsSettingsController controller;
|
final ChatPermissionsSettingsController controller;
|
||||||
|
|
||||||
const ChatPermissionsSettingsView(this.controller, {Key key})
|
const ChatPermissionsSettingsView(this.controller, {Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -24,19 +24,24 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||||||
: IconButton(
|
: IconButton(
|
||||||
icon: const Icon(Icons.close_outlined),
|
icon: const Icon(Icons.close_outlined),
|
||||||
onPressed: () => VRouter.of(context)
|
onPressed: () => VRouter.of(context)
|
||||||
.toSegments(['rooms', controller.roomId]),
|
.toSegments(['rooms', controller.roomId!]),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).editChatPermissions),
|
title: Text(L10n.of(context)!.editChatPermissions),
|
||||||
),
|
),
|
||||||
body: MaxWidthBody(
|
body: MaxWidthBody(
|
||||||
withScrolling: true,
|
withScrolling: true,
|
||||||
child: StreamBuilder(
|
child: StreamBuilder(
|
||||||
stream: controller.onChanged,
|
stream: controller.onChanged,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
final room =
|
final roomId = controller.roomId;
|
||||||
Matrix.of(context).client.getRoomById(controller.roomId);
|
final room = roomId == null
|
||||||
|
? null
|
||||||
|
: Matrix.of(context).client.getRoomById(roomId);
|
||||||
|
if (room == null) {
|
||||||
|
return Center(child: Text(L10n.of(context)!.noRoomsFound));
|
||||||
|
}
|
||||||
final powerLevelsContent = Map<String, dynamic>.from(
|
final powerLevelsContent = Map<String, dynamic>.from(
|
||||||
room.getState(EventTypes.RoomPowerLevels).content);
|
room.getState(EventTypes.RoomPowerLevels)!.content);
|
||||||
final powerLevels = Map<String, dynamic>.from(powerLevelsContent)
|
final powerLevels = Map<String, dynamic>.from(powerLevelsContent)
|
||||||
..removeWhere((k, v) => v is! int);
|
..removeWhere((k, v) => v is! int);
|
||||||
final eventsPowerLevels =
|
final eventsPowerLevels =
|
||||||
@ -57,7 +62,7 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||||||
const Divider(thickness: 1),
|
const Divider(thickness: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).notifications,
|
L10n.of(context)!.notifications,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -82,14 +87,13 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||||||
const Divider(thickness: 1),
|
const Divider(thickness: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).configureChat,
|
L10n.of(context)!.configureChat,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (eventsPowerLevels != null)
|
|
||||||
for (var entry in eventsPowerLevels.entries)
|
for (var entry in eventsPowerLevels.entries)
|
||||||
PermissionsListTile(
|
PermissionsListTile(
|
||||||
permissionKey: entry.key,
|
permissionKey: entry.key,
|
||||||
@ -110,15 +114,15 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||||||
strokeWidth: 2));
|
strokeWidth: 2));
|
||||||
}
|
}
|
||||||
final String roomVersion = room
|
final String roomVersion = room
|
||||||
.getState(EventTypes.RoomCreate)
|
.getState(EventTypes.RoomCreate)!
|
||||||
.content['room_version'] ??
|
.content['room_version'] ??
|
||||||
'1';
|
'1';
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
'${L10n.of(context).roomVersion}: $roomVersion'),
|
'${L10n.of(context)!.roomVersion}: $roomVersion'),
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
controller.updateRoomAction(snapshot.data),
|
controller.updateRoomAction(snapshot.data!),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -6,13 +6,13 @@ import 'package:matrix/matrix.dart';
|
|||||||
class PermissionsListTile extends StatelessWidget {
|
class PermissionsListTile extends StatelessWidget {
|
||||||
final String permissionKey;
|
final String permissionKey;
|
||||||
final int permission;
|
final int permission;
|
||||||
final String category;
|
final String? category;
|
||||||
final void Function() onTap;
|
final void Function()? onTap;
|
||||||
|
|
||||||
const PermissionsListTile({
|
const PermissionsListTile({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.permissionKey,
|
required this.permissionKey,
|
||||||
@required this.permission,
|
required this.permission,
|
||||||
this.category,
|
this.category,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@ -21,43 +21,43 @@ class PermissionsListTile extends StatelessWidget {
|
|||||||
if (category == null) {
|
if (category == null) {
|
||||||
switch (permissionKey) {
|
switch (permissionKey) {
|
||||||
case 'users_default':
|
case 'users_default':
|
||||||
return L10n.of(context).defaultPermissionLevel;
|
return L10n.of(context)!.defaultPermissionLevel;
|
||||||
case 'events_default':
|
case 'events_default':
|
||||||
return L10n.of(context).sendMessages;
|
return L10n.of(context)!.sendMessages;
|
||||||
case 'state_default':
|
case 'state_default':
|
||||||
return L10n.of(context).configureChat;
|
return L10n.of(context)!.configureChat;
|
||||||
case 'ban':
|
case 'ban':
|
||||||
return L10n.of(context).banFromChat;
|
return L10n.of(context)!.banFromChat;
|
||||||
case 'kick':
|
case 'kick':
|
||||||
return L10n.of(context).kickFromChat;
|
return L10n.of(context)!.kickFromChat;
|
||||||
case 'redact':
|
case 'redact':
|
||||||
return L10n.of(context).deleteMessage;
|
return L10n.of(context)!.deleteMessage;
|
||||||
case 'invite':
|
case 'invite':
|
||||||
return L10n.of(context).inviteContact;
|
return L10n.of(context)!.inviteContact;
|
||||||
}
|
}
|
||||||
} else if (category == 'notifications') {
|
} else if (category == 'notifications') {
|
||||||
switch (permissionKey) {
|
switch (permissionKey) {
|
||||||
case 'rooms':
|
case 'rooms':
|
||||||
return L10n.of(context).notifications;
|
return L10n.of(context)!.notifications;
|
||||||
}
|
}
|
||||||
} else if (category == 'events') {
|
} else if (category == 'events') {
|
||||||
switch (permissionKey) {
|
switch (permissionKey) {
|
||||||
case EventTypes.RoomName:
|
case EventTypes.RoomName:
|
||||||
return L10n.of(context).changeTheNameOfTheGroup;
|
return L10n.of(context)!.changeTheNameOfTheGroup;
|
||||||
case EventTypes.RoomPowerLevels:
|
case EventTypes.RoomPowerLevels:
|
||||||
return L10n.of(context).editChatPermissions;
|
return L10n.of(context)!.editChatPermissions;
|
||||||
case EventTypes.HistoryVisibility:
|
case EventTypes.HistoryVisibility:
|
||||||
return L10n.of(context).visibilityOfTheChatHistory;
|
return L10n.of(context)!.visibilityOfTheChatHistory;
|
||||||
case EventTypes.RoomCanonicalAlias:
|
case EventTypes.RoomCanonicalAlias:
|
||||||
return L10n.of(context).setInvitationLink;
|
return L10n.of(context)!.setInvitationLink;
|
||||||
case EventTypes.RoomAvatar:
|
case EventTypes.RoomAvatar:
|
||||||
return L10n.of(context).editRoomAvatar;
|
return L10n.of(context)!.editRoomAvatar;
|
||||||
case EventTypes.RoomTombstone:
|
case EventTypes.RoomTombstone:
|
||||||
return L10n.of(context).replaceRoomWithNewerVersion;
|
return L10n.of(context)!.replaceRoomWithNewerVersion;
|
||||||
case EventTypes.Encryption:
|
case EventTypes.Encryption:
|
||||||
return L10n.of(context).enableEncryption;
|
return L10n.of(context)!.enableEncryption;
|
||||||
case 'm.room.server_acl':
|
case 'm.room.server_acl':
|
||||||
return L10n.of(context).editBlockedServers;
|
return L10n.of(context)!.editBlockedServers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return permissionKey;
|
return permissionKey;
|
||||||
@ -96,9 +96,9 @@ class PermissionsListTile extends StatelessWidget {
|
|||||||
extension on int {
|
extension on int {
|
||||||
String toLocalizedPowerLevelString(BuildContext context) {
|
String toLocalizedPowerLevelString(BuildContext context) {
|
||||||
return this == 100
|
return this == 100
|
||||||
? L10n.of(context).admin
|
? L10n.of(context)!.admin
|
||||||
: this >= 50
|
: this >= 50
|
||||||
? L10n.of(context).moderator
|
? L10n.of(context)!.moderator
|
||||||
: L10n.of(context).participant;
|
: L10n.of(context)!.participant;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:matrix/encryption/utils/key_verification.dart';
|
import 'package:matrix/encryption/utils/key_verification.dart';
|
||||||
@ -11,14 +12,14 @@ import 'package:fluffychat/pages/key_verification/key_verification_dialog.dart';
|
|||||||
import '../../widgets/matrix.dart';
|
import '../../widgets/matrix.dart';
|
||||||
|
|
||||||
class DevicesSettings extends StatefulWidget {
|
class DevicesSettings extends StatefulWidget {
|
||||||
const DevicesSettings({Key key}) : super(key: key);
|
const DevicesSettings({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DevicesSettingsController createState() => DevicesSettingsController();
|
DevicesSettingsController createState() => DevicesSettingsController();
|
||||||
}
|
}
|
||||||
|
|
||||||
class DevicesSettingsController extends State<DevicesSettings> {
|
class DevicesSettingsController extends State<DevicesSettings> {
|
||||||
List<Device> devices;
|
List<Device>? devices;
|
||||||
Future<bool> loadUserDevices(BuildContext context) async {
|
Future<bool> loadUserDevices(BuildContext context) async {
|
||||||
if (devices != null) return true;
|
if (devices != null) return true;
|
||||||
devices = await Matrix.of(context).client.getDevices();
|
devices = await Matrix.of(context).client.getDevices();
|
||||||
@ -28,15 +29,15 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
|||||||
void reload() => setState(() => devices = null);
|
void reload() => setState(() => devices = null);
|
||||||
|
|
||||||
bool loadingDeletingDevices = false;
|
bool loadingDeletingDevices = false;
|
||||||
String errorDeletingDevices;
|
String? errorDeletingDevices;
|
||||||
|
|
||||||
void removeDevicesAction(List<Device> devices) async {
|
void removeDevicesAction(List<Device> devices) async {
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: L10n.of(context)!.areYouSure,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: L10n.of(context)!.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.cancel) return;
|
OkCancelResult.cancel) return;
|
||||||
final matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
@ -69,9 +70,9 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
|||||||
final displayName = await showTextInputDialog(
|
final displayName = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).changeDeviceName,
|
title: L10n.of(context)!.changeDeviceName,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
hintText: device.displayName,
|
hintText: device.displayName,
|
||||||
@ -93,13 +94,13 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
|||||||
void verifyDeviceAction(Device device) async {
|
void verifyDeviceAction(Device device) async {
|
||||||
final req = Matrix.of(context)
|
final req = Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.userDeviceKeys[Matrix.of(context).client.userID]
|
.userDeviceKeys[Matrix.of(context).client.userID!]!
|
||||||
.deviceKeys[device.deviceId]
|
.deviceKeys[device.deviceId]!
|
||||||
.startVerification();
|
.startVerification();
|
||||||
req.onUpdate = () {
|
req.onUpdate = () {
|
||||||
if ({KeyVerificationState.error, KeyVerificationState.done}
|
if ({KeyVerificationState.error, KeyVerificationState.done}
|
||||||
.contains(req.state)) {
|
.contains(req.state)) {
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
await KeyVerificationDialog(request: req).show(context);
|
await KeyVerificationDialog(request: req).show(context);
|
||||||
@ -108,33 +109,32 @@ class DevicesSettingsController extends State<DevicesSettings> {
|
|||||||
void blockDeviceAction(Device device) async {
|
void blockDeviceAction(Device device) async {
|
||||||
final key = Matrix.of(context)
|
final key = Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.userDeviceKeys[Matrix.of(context).client.userID]
|
.userDeviceKeys[Matrix.of(context).client.userID!]!
|
||||||
.deviceKeys[device.deviceId];
|
.deviceKeys[device.deviceId]!;
|
||||||
if (key.directVerified) {
|
if (key.directVerified) {
|
||||||
await key.setVerified(false);
|
await key.setVerified(false);
|
||||||
}
|
}
|
||||||
await key.setBlocked(true);
|
await key.setBlocked(true);
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void unblockDeviceAction(Device device) async {
|
void unblockDeviceAction(Device device) async {
|
||||||
final key = Matrix.of(context)
|
final key = Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.userDeviceKeys[Matrix.of(context).client.userID]
|
.userDeviceKeys[Matrix.of(context).client.userID!]!
|
||||||
.deviceKeys[device.deviceId];
|
.deviceKeys[device.deviceId]!;
|
||||||
await key.setBlocked(false);
|
await key.setBlocked(false);
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isOwnDevice(Device userDevice) =>
|
bool _isOwnDevice(Device userDevice) =>
|
||||||
userDevice.deviceId == Matrix.of(context).client.deviceID;
|
userDevice.deviceId == Matrix.of(context).client.deviceID;
|
||||||
|
|
||||||
Device get thisDevice => devices.firstWhere(
|
Device? get thisDevice => devices!.firstWhereOrNull(
|
||||||
_isOwnDevice,
|
_isOwnDevice,
|
||||||
orElse: () => null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Device> get notThisDevice => List<Device>.from(devices)
|
List<Device> get notThisDevice => List<Device>.from(devices!)
|
||||||
..removeWhere(_isOwnDevice)
|
..removeWhere(_isOwnDevice)
|
||||||
..sort((a, b) => (b.lastSeenTs ?? 0).compareTo(a.lastSeenTs ?? 0));
|
..sort((a, b) => (b.lastSeenTs ?? 0).compareTo(a.lastSeenTs ?? 0));
|
||||||
|
|
||||||
|
@ -9,14 +9,14 @@ import 'user_device_list_item.dart';
|
|||||||
class DevicesSettingsView extends StatelessWidget {
|
class DevicesSettingsView extends StatelessWidget {
|
||||||
final DevicesSettingsController controller;
|
final DevicesSettingsController controller;
|
||||||
|
|
||||||
const DevicesSettingsView(this.controller, {Key key}) : super(key: key);
|
const DevicesSettingsView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
title: Text(L10n.of(context).devices),
|
title: Text(L10n.of(context)!.devices),
|
||||||
),
|
),
|
||||||
body: MaxWidthBody(
|
body: MaxWidthBody(
|
||||||
child: FutureBuilder<bool>(
|
child: FutureBuilder<bool>(
|
||||||
@ -41,7 +41,7 @@ class DevicesSettingsView extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (controller.thisDevice != null)
|
if (controller.thisDevice != null)
|
||||||
UserDeviceListItem(
|
UserDeviceListItem(
|
||||||
controller.thisDevice,
|
controller.thisDevice!,
|
||||||
rename: controller.renameDeviceAction,
|
rename: controller.renameDeviceAction,
|
||||||
remove: (d) => controller.removeDevicesAction([d]),
|
remove: (d) => controller.removeDevicesAction([d]),
|
||||||
verify: controller.verifyDeviceAction,
|
verify: controller.verifyDeviceAction,
|
||||||
@ -53,7 +53,7 @@ class DevicesSettingsView extends StatelessWidget {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
controller.errorDeletingDevices ??
|
controller.errorDeletingDevices ??
|
||||||
L10n.of(context).removeAllOtherDevices,
|
L10n.of(context)!.removeAllOtherDevices,
|
||||||
style: const TextStyle(color: Colors.red),
|
style: const TextStyle(color: Colors.red),
|
||||||
),
|
),
|
||||||
trailing: controller.loadingDeletingDevices
|
trailing: controller.loadingDeletingDevices
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
@ -21,7 +21,7 @@ import '../../main.dart';
|
|||||||
import '../../utils/localized_exception_extension.dart';
|
import '../../utils/localized_exception_extension.dart';
|
||||||
|
|
||||||
class HomeserverPicker extends StatefulWidget {
|
class HomeserverPicker extends StatefulWidget {
|
||||||
const HomeserverPicker({Key key}) : super(key: key);
|
const HomeserverPicker({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
HomeserverPickerController createState() => HomeserverPickerController();
|
HomeserverPickerController createState() => HomeserverPickerController();
|
||||||
@ -32,9 +32,9 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
String domain = AppConfig.defaultHomeserver;
|
String domain = AppConfig.defaultHomeserver;
|
||||||
final TextEditingController homeserverController =
|
final TextEditingController homeserverController =
|
||||||
TextEditingController(text: AppConfig.defaultHomeserver);
|
TextEditingController(text: AppConfig.defaultHomeserver);
|
||||||
StreamSubscription _intentDataStreamSubscription;
|
StreamSubscription? _intentDataStreamSubscription;
|
||||||
String error;
|
String? error;
|
||||||
Timer _coolDown;
|
Timer? _coolDown;
|
||||||
|
|
||||||
void setDomain(String domain) {
|
void setDomain(String domain) {
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
@ -46,7 +46,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _loginWithToken(String token) {
|
void _loginWithToken(String token) {
|
||||||
if (token?.isEmpty ?? true) return;
|
if (token.isEmpty) return;
|
||||||
|
|
||||||
showFutureLoadingDialog(
|
showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -66,7 +66,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _processIncomingUris(String text) async {
|
void _processIncomingUris(String? text) async {
|
||||||
if (text == null || !text.startsWith(AppConfig.appOpenUrlScheme)) return;
|
if (text == null || !text.startsWith(AppConfig.appOpenUrlScheme)) return;
|
||||||
await browser?.close();
|
await browser?.close();
|
||||||
VRouter.of(context).to('/home');
|
VRouter.of(context).to('/home');
|
||||||
@ -89,8 +89,8 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
_initReceiveUri();
|
_initReceiveUri();
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
final token = Matrix.of(context).widget.queryParameters['loginToken'];
|
final token = Matrix.of(context).widget.queryParameters!['loginToken'];
|
||||||
if (token != null) _loginWithToken(token);
|
if (token != null) _loginWithToken(token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
_intentDataStreamSubscription?.cancel();
|
_intentDataStreamSubscription?.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
String _lastCheckedHomeserver;
|
String? _lastCheckedHomeserver;
|
||||||
|
|
||||||
/// Starts an analysis of the given homeserver. It uses the current domain and
|
/// Starts an analysis of the given homeserver. It uses the current domain and
|
||||||
/// makes sure that it is prefixed with https. Then it searches for the
|
/// makes sure that it is prefixed with https. Then it searches for the
|
||||||
@ -112,7 +112,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
Future<void> checkHomeserverAction() async {
|
Future<void> checkHomeserverAction() async {
|
||||||
_coolDown?.cancel();
|
_coolDown?.cancel();
|
||||||
if (_lastCheckedHomeserver == domain) return;
|
if (_lastCheckedHomeserver == domain) return;
|
||||||
if (domain.isEmpty) throw L10n.of(context).changeTheHomeserver;
|
if (domain.isEmpty) throw L10n.of(context)!.changeTheHomeserver;
|
||||||
var homeserver = domain;
|
var homeserver = domain;
|
||||||
|
|
||||||
if (!homeserver.startsWith('https://')) {
|
if (!homeserver.startsWith('https://')) {
|
||||||
@ -129,7 +129,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
await Matrix.of(context).getLoginClient().checkHomeserver(homeserver);
|
await Matrix.of(context).getLoginClient().checkHomeserver(homeserver);
|
||||||
|
|
||||||
var jitsi = wellKnown?.additionalProperties
|
var jitsi = wellKnown?.additionalProperties
|
||||||
?.tryGet<Map<String, dynamic>>('im.vector.riot.jitsi')
|
.tryGet<Map<String, dynamic>>('im.vector.riot.jitsi')
|
||||||
?.tryGet<String>('preferredDomain');
|
?.tryGet<String>('preferredDomain');
|
||||||
if (jitsi != null) {
|
if (jitsi != null) {
|
||||||
if (!jitsi.endsWith('/')) {
|
if (!jitsi.endsWith('/')) {
|
||||||
@ -150,10 +150,10 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
await Matrix.of(context).getLoginClient().register();
|
await Matrix.of(context).getLoginClient().register();
|
||||||
registrationSupported = true;
|
registrationSupported = true;
|
||||||
} on MatrixException catch (e) {
|
} on MatrixException catch (e) {
|
||||||
registrationSupported = e.requireAdditionalAuthentication ?? false;
|
registrationSupported = e.requireAdditionalAuthentication;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState(() => error = (e as Object).toLocalizedString(context));
|
setState(() => error = (e).toLocalizedString(context));
|
||||||
} finally {
|
} finally {
|
||||||
_lastCheckedHomeserver = domain;
|
_lastCheckedHomeserver = domain;
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -162,12 +162,12 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> _rawLoginTypes;
|
Map<String, dynamic>? _rawLoginTypes;
|
||||||
bool registrationSupported;
|
bool? registrationSupported;
|
||||||
|
|
||||||
List<IdentityProvider> get identityProviders {
|
List<IdentityProvider> get identityProviders {
|
||||||
if (!ssoLoginSupported) return [];
|
if (!ssoLoginSupported) return [];
|
||||||
final rawProviders = _rawLoginTypes.tryGetList('flows').singleWhere(
|
final rawProviders = _rawLoginTypes!.tryGetList('flows')!.singleWhere(
|
||||||
(flow) =>
|
(flow) =>
|
||||||
flow['type'] == AuthenticationTypes.sso)['identity_providers'];
|
flow['type'] == AuthenticationTypes.sso)['identity_providers'];
|
||||||
final list = (rawProviders as List)
|
final list = (rawProviders as List)
|
||||||
@ -184,8 +184,8 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
.client
|
.client
|
||||||
.supportedLoginTypes
|
.supportedLoginTypes
|
||||||
.contains(AuthenticationTypes.password) &&
|
.contains(AuthenticationTypes.password) &&
|
||||||
_rawLoginTypes
|
_rawLoginTypes!
|
||||||
.tryGetList('flows')
|
.tryGetList('flows')!
|
||||||
.any((flow) => flow['type'] == AuthenticationTypes.password);
|
.any((flow) => flow['type'] == AuthenticationTypes.password);
|
||||||
|
|
||||||
bool get ssoLoginSupported =>
|
bool get ssoLoginSupported =>
|
||||||
@ -193,11 +193,11 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
.client
|
.client
|
||||||
.supportedLoginTypes
|
.supportedLoginTypes
|
||||||
.contains(AuthenticationTypes.sso) &&
|
.contains(AuthenticationTypes.sso) &&
|
||||||
_rawLoginTypes
|
_rawLoginTypes!
|
||||||
.tryGetList('flows')
|
.tryGetList('flows')!
|
||||||
.any((flow) => flow['type'] == AuthenticationTypes.sso);
|
.any((flow) => flow['type'] == AuthenticationTypes.sso);
|
||||||
|
|
||||||
ChromeSafariBrowser browser;
|
ChromeSafariBrowser? browser;
|
||||||
|
|
||||||
static const String ssoHomeserverKey = 'sso-homeserver';
|
static const String ssoHomeserverKey = 'sso-homeserver';
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
'${Matrix.of(context).getLoginClient().homeserver?.toString()}/_matrix/client/r0/login/sso/redirect/${Uri.encodeComponent(id)}?redirectUrl=${Uri.encodeQueryComponent(redirectUrl)}';
|
'${Matrix.of(context).getLoginClient().homeserver?.toString()}/_matrix/client/r0/login/sso/redirect/${Uri.encodeComponent(id)}?redirectUrl=${Uri.encodeQueryComponent(redirectUrl)}';
|
||||||
if (PlatformInfos.isMobile) {
|
if (PlatformInfos.isMobile) {
|
||||||
browser ??= ChromeSafariBrowser();
|
browser ??= ChromeSafariBrowser();
|
||||||
browser.open(url: Uri.parse(url));
|
browser!.open(url: Uri.parse(url));
|
||||||
} else {
|
} else {
|
||||||
launch(redirectUrl);
|
launch(redirectUrl);
|
||||||
}
|
}
|
||||||
@ -234,10 +234,10 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class IdentityProvider {
|
class IdentityProvider {
|
||||||
final String id;
|
final String? id;
|
||||||
final String name;
|
final String? name;
|
||||||
final String icon;
|
final String? icon;
|
||||||
final String brand;
|
final String? brand;
|
||||||
|
|
||||||
IdentityProvider({this.id, this.name, this.icon, this.brand});
|
IdentityProvider({this.id, this.name, this.icon, this.brand});
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import 'homeserver_picker.dart';
|
|||||||
class HomeserverPickerView extends StatelessWidget {
|
class HomeserverPickerView extends StatelessWidget {
|
||||||
final HomeserverPickerController controller;
|
final HomeserverPickerController controller;
|
||||||
|
|
||||||
const HomeserverPickerView(this.controller, {Key key}) : super(key: key);
|
const HomeserverPickerView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -27,7 +27,7 @@ class HomeserverPickerView extends StatelessWidget {
|
|||||||
titleSpacing: 8,
|
titleSpacing: 8,
|
||||||
title: DefaultAppBarSearchField(
|
title: DefaultAppBarSearchField(
|
||||||
prefixText: 'https://',
|
prefixText: 'https://',
|
||||||
hintText: L10n.of(context).enterYourHomeserver,
|
hintText: L10n.of(context)!.enterYourHomeserver,
|
||||||
searchController: controller.homeserverController,
|
searchController: controller.homeserverController,
|
||||||
suffix: const Icon(Icons.edit_outlined),
|
suffix: const Icon(Icons.edit_outlined),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
@ -36,7 +36,7 @@ class HomeserverPickerView extends StatelessWidget {
|
|||||||
onSubmit: (_) => controller.checkHomeserverAction(),
|
onSubmit: (_) => controller.checkHomeserverAction(),
|
||||||
unfocusOnClear: false,
|
unfocusOnClear: false,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
labelText: L10n.of(context).homeserver,
|
labelText: L10n.of(context)!.homeserver,
|
||||||
),
|
),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
@ -54,7 +54,7 @@ class HomeserverPickerView extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
controller.error,
|
controller.error!,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
@ -77,7 +77,7 @@ class HomeserverPickerView extends StatelessWidget {
|
|||||||
const Expanded(child: Divider()),
|
const Expanded(child: Divider()),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Text(L10n.of(context).loginWithOneClick),
|
child: Text(L10n.of(context)!.loginWithOneClick),
|
||||||
),
|
),
|
||||||
const Expanded(child: Divider()),
|
const Expanded(child: Divider()),
|
||||||
]),
|
]),
|
||||||
@ -88,20 +88,20 @@ class HomeserverPickerView extends StatelessWidget {
|
|||||||
in controller.identityProviders)
|
in controller.identityProviders)
|
||||||
_SsoButton(
|
_SsoButton(
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
controller.ssoLoginAction(identityProvider.id),
|
controller.ssoLoginAction(identityProvider.id!),
|
||||||
identityProvider: identityProvider,
|
identityProvider: identityProvider,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
].toList(),
|
].toList(),
|
||||||
),
|
),
|
||||||
if (controller.ssoLoginSupported &&
|
if (controller.ssoLoginSupported &&
|
||||||
(controller.registrationSupported ||
|
(controller.registrationSupported! ||
|
||||||
controller.passwordLoginSupported))
|
controller.passwordLoginSupported))
|
||||||
Row(children: [
|
Row(children: [
|
||||||
const Expanded(child: Divider()),
|
const Expanded(child: Divider()),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Text(L10n.of(context).or),
|
child: Text(L10n.of(context)!.or),
|
||||||
),
|
),
|
||||||
const Expanded(child: Divider()),
|
const Expanded(child: Divider()),
|
||||||
]),
|
]),
|
||||||
@ -111,22 +111,22 @@ class HomeserverPickerView extends StatelessWidget {
|
|||||||
onPressed: () => VRouter.of(context).to('login'),
|
onPressed: () => VRouter.of(context).to('login'),
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
CupertinoIcons.lock_open_fill,
|
CupertinoIcons.lock_open_fill,
|
||||||
color: Theme.of(context).textTheme.bodyText1.color,
|
color: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
),
|
),
|
||||||
labelText: L10n.of(context).login,
|
labelText: L10n.of(context)!.login,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
if (controller.registrationSupported)
|
if (controller.registrationSupported!)
|
||||||
Center(
|
Center(
|
||||||
child: _LoginButton(
|
child: _LoginButton(
|
||||||
onPressed: controller.signUpAction,
|
onPressed: controller.signUpAction,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
CupertinoIcons.person_add,
|
CupertinoIcons.person_add,
|
||||||
color: Theme.of(context).textTheme.bodyText1.color,
|
color: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
),
|
),
|
||||||
labelText: L10n.of(context).register,
|
labelText: L10n.of(context)!.register,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -142,7 +142,7 @@ class HomeserverPickerView extends StatelessWidget {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => launch(AppConfig.privacyUrl),
|
onPressed: () => launch(AppConfig.privacyUrl),
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).privacy,
|
L10n.of(context)!.privacy,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
color: Colors.blueGrey,
|
color: Colors.blueGrey,
|
||||||
@ -152,7 +152,7 @@ class HomeserverPickerView extends StatelessWidget {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => PlatformInfos.showDialog(context),
|
onPressed: () => PlatformInfos.showDialog(context),
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).about,
|
L10n.of(context)!.about,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
color: Colors.blueGrey,
|
color: Colors.blueGrey,
|
||||||
@ -169,10 +169,10 @@ class HomeserverPickerView extends StatelessWidget {
|
|||||||
|
|
||||||
class _SsoButton extends StatelessWidget {
|
class _SsoButton extends StatelessWidget {
|
||||||
final IdentityProvider identityProvider;
|
final IdentityProvider identityProvider;
|
||||||
final void Function() onPressed;
|
final void Function()? onPressed;
|
||||||
const _SsoButton({
|
const _SsoButton({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.identityProvider,
|
required this.identityProvider,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ class _SsoButton extends StatelessWidget {
|
|||||||
child: identityProvider.icon == null
|
child: identityProvider.icon == null
|
||||||
? const Icon(Icons.web_outlined)
|
? const Icon(Icons.web_outlined)
|
||||||
: CachedNetworkImage(
|
: CachedNetworkImage(
|
||||||
imageUrl: Uri.parse(identityProvider.icon)
|
imageUrl: Uri.parse(identityProvider.icon!)
|
||||||
.getDownloadLink(
|
.getDownloadLink(
|
||||||
Matrix.of(context).getLoginClient())
|
Matrix.of(context).getLoginClient())
|
||||||
.toString(),
|
.toString(),
|
||||||
@ -209,11 +209,11 @@ class _SsoButton extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
identityProvider.name ??
|
identityProvider.name ??
|
||||||
identityProvider.brand ??
|
identityProvider.brand ??
|
||||||
L10n.of(context).singlesignon,
|
L10n.of(context)!.singlesignon,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Theme.of(context).textTheme.subtitle2.color,
|
color: Theme.of(context).textTheme.subtitle2!.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -224,11 +224,11 @@ class _SsoButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LoginButton extends StatelessWidget {
|
class _LoginButton extends StatelessWidget {
|
||||||
final String labelText;
|
final String? labelText;
|
||||||
final Widget icon;
|
final Widget? icon;
|
||||||
final void Function() onPressed;
|
final void Function()? onPressed;
|
||||||
const _LoginButton({
|
const _LoginButton({
|
||||||
Key key,
|
Key? key,
|
||||||
this.labelText,
|
this.labelText,
|
||||||
this.icon,
|
this.icon,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
@ -246,11 +246,11 @@ class _LoginButton extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
icon: icon,
|
icon: icon!,
|
||||||
label: Text(
|
label: Text(
|
||||||
labelText,
|
labelText!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).textTheme.bodyText1.color,
|
color: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -10,9 +10,9 @@ import '../../utils/matrix_sdk_extensions.dart/event_extension.dart';
|
|||||||
|
|
||||||
class ImageViewer extends StatefulWidget {
|
class ImageViewer extends StatefulWidget {
|
||||||
final Event event;
|
final Event event;
|
||||||
final void Function() onLoaded;
|
final void Function()? onLoaded;
|
||||||
|
|
||||||
const ImageViewer(this.event, {Key key, this.onLoaded}) : super(key: key);
|
const ImageViewer(this.event, {Key? key, this.onLoaded}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ImageViewerController createState() => ImageViewerController();
|
ImageViewerController createState() => ImageViewerController();
|
||||||
|
@ -8,7 +8,7 @@ import 'image_viewer.dart';
|
|||||||
class ImageViewerView extends StatelessWidget {
|
class ImageViewerView extends StatelessWidget {
|
||||||
final ImageViewerController controller;
|
final ImageViewerController controller;
|
||||||
|
|
||||||
const ImageViewerView(this.controller, {Key key}) : super(key: key);
|
const ImageViewerView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -21,7 +21,7 @@ class ImageViewerView extends StatelessWidget {
|
|||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: Navigator.of(context).pop,
|
onPressed: Navigator.of(context).pop,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
tooltip: L10n.of(context).close,
|
tooltip: L10n.of(context)!.close,
|
||||||
),
|
),
|
||||||
backgroundColor: const Color(0x44000000),
|
backgroundColor: const Color(0x44000000),
|
||||||
actions: [
|
actions: [
|
||||||
@ -29,13 +29,13 @@ class ImageViewerView extends StatelessWidget {
|
|||||||
icon: const Icon(Icons.reply_outlined),
|
icon: const Icon(Icons.reply_outlined),
|
||||||
onPressed: controller.forwardAction,
|
onPressed: controller.forwardAction,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
tooltip: L10n.of(context).share,
|
tooltip: L10n.of(context)!.share,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.download_outlined),
|
icon: const Icon(Icons.download_outlined),
|
||||||
onPressed: controller.saveFileAction,
|
onPressed: controller.saveFileAction,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
tooltip: L10n.of(context).downloadFile,
|
tooltip: L10n.of(context)!.downloadFile,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -12,7 +12,7 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
import '../../utils/localized_exception_extension.dart';
|
import '../../utils/localized_exception_extension.dart';
|
||||||
|
|
||||||
class InvitationSelection extends StatefulWidget {
|
class InvitationSelection extends StatefulWidget {
|
||||||
const InvitationSelection({Key key}) : super(key: key);
|
const InvitationSelection({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
InvitationSelectionController createState() =>
|
InvitationSelectionController createState() =>
|
||||||
@ -21,16 +21,16 @@ class InvitationSelection extends StatefulWidget {
|
|||||||
|
|
||||||
class InvitationSelectionController extends State<InvitationSelection> {
|
class InvitationSelectionController extends State<InvitationSelection> {
|
||||||
TextEditingController controller = TextEditingController();
|
TextEditingController controller = TextEditingController();
|
||||||
String currentSearchTerm;
|
late String currentSearchTerm;
|
||||||
bool loading = false;
|
bool loading = false;
|
||||||
List<Profile> foundProfiles = [];
|
List<Profile> foundProfiles = [];
|
||||||
Timer coolDown;
|
Timer? coolDown;
|
||||||
|
|
||||||
String get roomId => VRouter.of(context).pathParameters['roomid'];
|
String? get roomId => VRouter.of(context).pathParameters['roomid'];
|
||||||
|
|
||||||
Future<List<User>> getContacts(BuildContext context) async {
|
Future<List<User>> getContacts(BuildContext context) async {
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
final room = client.getRoomById(roomId);
|
final room = client.getRoomById(roomId!)!;
|
||||||
final participants = await room.requestParticipants();
|
final participants = await room.requestParticipants();
|
||||||
participants.removeWhere(
|
participants.removeWhere(
|
||||||
(u) => ![Membership.join, Membership.invite].contains(u.membership),
|
(u) => ![Membership.join, Membership.invite].contains(u.membership),
|
||||||
@ -38,7 +38,7 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
|||||||
final participantsIds = participants.map((p) => p.stateKey).toList();
|
final participantsIds = participants.map((p) => p.stateKey).toList();
|
||||||
final contacts = client.rooms
|
final contacts = client.rooms
|
||||||
.where((r) => r.isDirectChat)
|
.where((r) => r.isDirectChat)
|
||||||
.map((r) => r.getUserByMXIDSync(r.directChatMatrixID))
|
.map((r) => r.getUserByMXIDSync(r.directChatMatrixID!))
|
||||||
.toList()
|
.toList()
|
||||||
..removeWhere((u) => participantsIds.contains(u.stateKey));
|
..removeWhere((u) => participantsIds.contains(u.stateKey));
|
||||||
contacts.sort(
|
contacts.sort(
|
||||||
@ -50,14 +50,14 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void inviteAction(BuildContext context, String id) async {
|
void inviteAction(BuildContext context, String id) async {
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
final room = Matrix.of(context).client.getRoomById(roomId!);
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.invite(id),
|
future: () => room!.invite(id),
|
||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: Text(L10n.of(context).contactHasBeenInvitedToTheGroup)));
|
content: Text(L10n.of(context)!.contactHasBeenInvitedToTheGroup)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
|||||||
response = await matrix.client.searchUserDirectory(text, limit: 10);
|
response = await matrix.client.searchUserDirectory(text, limit: 10);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text((e as Object).toLocalizedString(context))));
|
SnackBar(content: Text((e).toLocalizedString(context))));
|
||||||
return;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
setState(() => loading = false);
|
setState(() => loading = false);
|
||||||
@ -99,7 +99,7 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
|||||||
}
|
}
|
||||||
final participants = Matrix.of(context)
|
final participants = Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.getRoomById(roomId)
|
.getRoomById(roomId!)!
|
||||||
.getParticipants()
|
.getParticipants()
|
||||||
.where((user) =>
|
.where((user) =>
|
||||||
[Membership.join, Membership.invite].contains(user.membership))
|
[Membership.join, Membership.invite].contains(user.membership))
|
||||||
|
@ -13,13 +13,12 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
class InvitationSelectionView extends StatelessWidget {
|
class InvitationSelectionView extends StatelessWidget {
|
||||||
final InvitationSelectionController controller;
|
final InvitationSelectionController controller;
|
||||||
|
|
||||||
const InvitationSelectionView(this.controller, {Key key}) : super(key: key);
|
const InvitationSelectionView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final room = Matrix.of(context).client.getRoomById(controller.roomId);
|
final room = Matrix.of(context).client.getRoomById(controller.roomId!)!;
|
||||||
final groupName =
|
final groupName = room.name.isEmpty ? L10n.of(context)!.group : room.name;
|
||||||
room.name?.isEmpty ?? false ? L10n.of(context).group : room.name;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: VRouter.of(context).path.startsWith('/spaces/')
|
leading: VRouter.of(context).path.startsWith('/spaces/')
|
||||||
@ -27,12 +26,12 @@ class InvitationSelectionView extends StatelessWidget {
|
|||||||
: IconButton(
|
: IconButton(
|
||||||
icon: const Icon(Icons.close_outlined),
|
icon: const Icon(Icons.close_outlined),
|
||||||
onPressed: () => VRouter.of(context)
|
onPressed: () => VRouter.of(context)
|
||||||
.toSegments(['rooms', controller.roomId]),
|
.toSegments(['rooms', controller.roomId!]),
|
||||||
),
|
),
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: DefaultAppBarSearchField(
|
title: DefaultAppBarSearchField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
hintText: L10n.of(context).inviteContactToGroup(groupName),
|
hintText: L10n.of(context)!.inviteContactToGroup(groupName),
|
||||||
onChanged: controller.searchUserWithCoolDown,
|
onChanged: controller.searchUserWithCoolDown,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -51,7 +50,7 @@ class InvitationSelectionView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
controller.foundProfiles[i].displayName ??
|
controller.foundProfiles[i].displayName ??
|
||||||
controller.foundProfiles[i].userId.localpart,
|
controller.foundProfiles[i].userId.localpart!,
|
||||||
),
|
),
|
||||||
subtitle: Text(controller.foundProfiles[i].userId),
|
subtitle: Text(controller.foundProfiles[i].userId),
|
||||||
onTap: () => controller.inviteAction(
|
onTap: () => controller.inviteAction(
|
||||||
@ -66,7 +65,7 @@ class InvitationSelectionView extends StatelessWidget {
|
|||||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final contacts = snapshot.data;
|
final contacts = snapshot.data!;
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
|
@ -34,8 +34,8 @@ class KeyVerificationDialog extends StatefulWidget {
|
|||||||
final KeyVerification request;
|
final KeyVerification request;
|
||||||
|
|
||||||
const KeyVerificationDialog({
|
const KeyVerificationDialog({
|
||||||
Key key,
|
Key? key,
|
||||||
this.request,
|
required this.request,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -43,25 +43,23 @@ class KeyVerificationDialog extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
||||||
void Function() originalOnUpdate;
|
void Function()? originalOnUpdate;
|
||||||
List<dynamic> sasEmoji;
|
late final List<dynamic> sasEmoji;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
originalOnUpdate = widget.request.onUpdate;
|
originalOnUpdate = widget.request.onUpdate;
|
||||||
widget.request.onUpdate = () {
|
widget.request.onUpdate = () {
|
||||||
if (originalOnUpdate != null) {
|
originalOnUpdate?.call();
|
||||||
originalOnUpdate();
|
setState(() {});
|
||||||
}
|
|
||||||
setState(() => null);
|
|
||||||
};
|
};
|
||||||
widget.request.client.getProfileFromUserId(widget.request.userId).then((p) {
|
widget.request.client.getProfileFromUserId(widget.request.userId).then((p) {
|
||||||
profile = p;
|
profile = p;
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
});
|
});
|
||||||
rootBundle.loadString('assets/sas-emoji.json').then((e) {
|
rootBundle.loadString('assets/sas-emoji.json').then((e) {
|
||||||
sasEmoji = json.decode(e);
|
sasEmoji = json.decode(e);
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
@ -77,12 +75,11 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Profile profile;
|
Profile? profile;
|
||||||
|
|
||||||
Future<void> checkInput(String input) async {
|
Future<void> checkInput(String input) async {
|
||||||
if (input == null || input.isEmpty) {
|
if (input.isEmpty) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
final valid = await showFutureLoadingDialog(
|
final valid = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () async {
|
future: () async {
|
||||||
@ -101,24 +98,24 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
await showOkAlertDialog(
|
await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
message: L10n.of(context).incorrectPassphraseOrKey,
|
message: L10n.of(context)!.incorrectPassphraseOrKey,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
User user;
|
User? user;
|
||||||
final directChatId =
|
final directChatId =
|
||||||
widget.request.client.getDirectChatFromUserId(widget.request.userId);
|
widget.request.client.getDirectChatFromUserId(widget.request.userId);
|
||||||
if (directChatId != null) {
|
if (directChatId != null) {
|
||||||
user = widget.request.client
|
user = widget.request.client
|
||||||
.getRoomById(directChatId)
|
.getRoomById(directChatId)!
|
||||||
?.getUserByMXIDSync(widget.request.userId);
|
.getUserByMXIDSync(widget.request.userId);
|
||||||
}
|
}
|
||||||
final displayName =
|
final displayName =
|
||||||
user?.calcDisplayname() ?? widget.request.userId.localpart;
|
user?.calcDisplayname() ?? widget.request.userId.localpart!;
|
||||||
var title = Text(L10n.of(context).verifyTitle);
|
var title = Text(L10n.of(context)!.verifyTitle);
|
||||||
Widget body;
|
Widget body;
|
||||||
final buttons = <Widget>[];
|
final buttons = <Widget>[];
|
||||||
switch (widget.request.state) {
|
switch (widget.request.state) {
|
||||||
@ -131,7 +128,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(L10n.of(context).askSSSSSign,
|
Text(L10n.of(context)!.askSSSSSign,
|
||||||
style: const TextStyle(fontSize: 20)),
|
style: const TextStyle(fontSize: 20)),
|
||||||
Container(height: 10),
|
Container(height: 10),
|
||||||
TextField(
|
TextField(
|
||||||
@ -146,7 +143,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: L10n.of(context).passphraseOrKey,
|
hintText: L10n.of(context)!.passphraseOrKey,
|
||||||
prefixStyle: TextStyle(color: Theme.of(context).primaryColor),
|
prefixStyle: TextStyle(color: Theme.of(context).primaryColor),
|
||||||
suffixStyle: TextStyle(color: Theme.of(context).primaryColor),
|
suffixStyle: TextStyle(color: Theme.of(context).primaryColor),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
@ -156,16 +153,16 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).submit,
|
label: L10n.of(context)!.submit,
|
||||||
onPressed: () => checkInput(textEditingController.text),
|
onPressed: () => checkInput(textEditingController.text),
|
||||||
));
|
));
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).skip,
|
label: L10n.of(context)!.skip,
|
||||||
onPressed: () => widget.request.openSSSS(skip: true),
|
onPressed: () => widget.request.openSSSS(skip: true),
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
case KeyVerificationState.askAccept:
|
case KeyVerificationState.askAccept:
|
||||||
title = Text(L10n.of(context).newVerificationRequest);
|
title = Text(L10n.of(context)!.newVerificationRequest);
|
||||||
body = Column(
|
body = Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -199,19 +196,19 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
Image.asset('assets/verification.png', fit: BoxFit.contain),
|
Image.asset('assets/verification.png', fit: BoxFit.contain),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context).askVerificationRequest(displayName),
|
L10n.of(context)!.askVerificationRequest(displayName),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).reject,
|
label: L10n.of(context)!.reject,
|
||||||
textColor: Colors.red,
|
textColor: Colors.red,
|
||||||
onPressed: () => widget.request
|
onPressed: () => widget.request
|
||||||
.rejectVerification()
|
.rejectVerification()
|
||||||
.then((_) => Navigator.of(context, rootNavigator: false).pop()),
|
.then((_) => Navigator.of(context, rootNavigator: false).pop()),
|
||||||
));
|
));
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).accept,
|
label: L10n.of(context)!.accept,
|
||||||
onPressed: () => widget.request.acceptVerification(),
|
onPressed: () => widget.request.acceptVerification(),
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
@ -224,22 +221,22 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
const CircularProgressIndicator.adaptive(strokeWidth: 2),
|
const CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context).waitingPartnerAcceptRequest,
|
L10n.of(context)!.waitingPartnerAcceptRequest,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
final key = widget.request.client.userDeviceKeys[widget.request.userId]
|
final key = widget.request.client.userDeviceKeys[widget.request.userId]
|
||||||
.deviceKeys[widget.request.deviceId];
|
?.deviceKeys[widget.request.deviceId];
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).verifyManual,
|
label: L10n.of(context)!.verifyManual,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final result = await showOkCancelAlertDialog(
|
final result = await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).verifyManual,
|
title: L10n.of(context)!.verifyManual,
|
||||||
message: key.ed25519Key.beautified,
|
message: key.ed25519Key?.beautified ?? 'Key not found',
|
||||||
);
|
);
|
||||||
if (result == OkCancelResult.ok) {
|
if (result == OkCancelResult.ok) {
|
||||||
await key.setVerified(true);
|
await key.setVerified(true);
|
||||||
@ -257,14 +254,14 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
// view for if "emoji" is a present sasType or not?
|
// view for if "emoji" is a present sasType or not?
|
||||||
String compareText;
|
String compareText;
|
||||||
if (widget.request.sasTypes.contains('emoji')) {
|
if (widget.request.sasTypes.contains('emoji')) {
|
||||||
compareText = L10n.of(context).compareEmojiMatch;
|
compareText = L10n.of(context)!.compareEmojiMatch;
|
||||||
compareWidget = TextSpan(
|
compareWidget = TextSpan(
|
||||||
children: widget.request.sasEmojis
|
children: widget.request.sasEmojis
|
||||||
.map((e) => WidgetSpan(child: _Emoji(e, sasEmoji)))
|
.map((e) => WidgetSpan(child: _Emoji(e, sasEmoji)))
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
compareText = L10n.of(context).compareNumbersMatch;
|
compareText = L10n.of(context)!.compareNumbersMatch;
|
||||||
final numbers = widget.request.sasNumbers;
|
final numbers = widget.request.sasNumbers;
|
||||||
final numbstr = '${numbers[0]}-${numbers[1]}-${numbers[2]}';
|
final numbstr = '${numbers[0]}-${numbers[1]}-${numbers[2]}';
|
||||||
compareWidget =
|
compareWidget =
|
||||||
@ -289,18 +286,18 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
);
|
);
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
textColor: Colors.red,
|
textColor: Colors.red,
|
||||||
label: L10n.of(context).theyDontMatch,
|
label: L10n.of(context)!.theyDontMatch,
|
||||||
onPressed: () => widget.request.rejectSas(),
|
onPressed: () => widget.request.rejectSas(),
|
||||||
));
|
));
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).theyMatch,
|
label: L10n.of(context)!.theyMatch,
|
||||||
onPressed: () => widget.request.acceptSas(),
|
onPressed: () => widget.request.acceptSas(),
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
case KeyVerificationState.waitingSas:
|
case KeyVerificationState.waitingSas:
|
||||||
final acceptText = widget.request.sasTypes.contains('emoji')
|
final acceptText = widget.request.sasTypes.contains('emoji')
|
||||||
? L10n.of(context).waitingPartnerEmoji
|
? L10n.of(context)!.waitingPartnerEmoji
|
||||||
: L10n.of(context).waitingPartnerNumbers;
|
: L10n.of(context)!.waitingPartnerNumbers;
|
||||||
body = Column(
|
body = Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -321,13 +318,13 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
color: Colors.green, size: 200.0),
|
color: Colors.green, size: 200.0),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context).verifySuccess,
|
L10n.of(context)!.verifySuccess,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).close,
|
label: L10n.of(context)!.close,
|
||||||
onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
|
onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
@ -344,12 +341,11 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
buttons.add(AdaptiveFlatButton(
|
buttons.add(AdaptiveFlatButton(
|
||||||
label: L10n.of(context).close,
|
label: L10n.of(context)!.close,
|
||||||
onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
|
onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
body ??= Text('ERROR: Unknown state ' + widget.request.state.toString());
|
|
||||||
final content = SingleChildScrollView(
|
final content = SingleChildScrollView(
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -377,16 +373,17 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
|
|
||||||
class _Emoji extends StatelessWidget {
|
class _Emoji extends StatelessWidget {
|
||||||
final KeyVerificationEmoji emoji;
|
final KeyVerificationEmoji emoji;
|
||||||
final List<dynamic> sasEmoji;
|
final List<dynamic>? sasEmoji;
|
||||||
|
|
||||||
const _Emoji(this.emoji, this.sasEmoji);
|
const _Emoji(this.emoji, this.sasEmoji);
|
||||||
|
|
||||||
String getLocalizedName() {
|
String getLocalizedName() {
|
||||||
|
final sasEmoji = this.sasEmoji;
|
||||||
if (sasEmoji == null) {
|
if (sasEmoji == null) {
|
||||||
// asset is still being loaded
|
// asset is still being loaded
|
||||||
return emoji.name;
|
return emoji.name;
|
||||||
}
|
}
|
||||||
final translations = Map<String, String>.from(
|
final translations = Map<String, String?>.from(
|
||||||
sasEmoji[emoji.number]['translated_descriptions']);
|
sasEmoji[emoji.number]['translated_descriptions']);
|
||||||
translations['en'] = emoji.name;
|
translations['en'] = emoji.name;
|
||||||
for (final locale in window.locales) {
|
for (final locale in window.locales) {
|
||||||
@ -398,7 +395,7 @@ class _Emoji extends StatelessWidget {
|
|||||||
if (haveLanguage == wantLanguage &&
|
if (haveLanguage == wantLanguage &&
|
||||||
(Set.from(haveLocaleParts)..removeAll(wantLocaleParts)).isEmpty &&
|
(Set.from(haveLocaleParts)..removeAll(wantLocaleParts)).isEmpty &&
|
||||||
(translations[haveLocale]?.isNotEmpty ?? false)) {
|
(translations[haveLocale]?.isNotEmpty ?? false)) {
|
||||||
return translations[haveLocale];
|
return translations[haveLocale]!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import '../../utils/platform_infos.dart';
|
|||||||
import 'login_view.dart';
|
import 'login_view.dart';
|
||||||
|
|
||||||
class Login extends StatefulWidget {
|
class Login extends StatefulWidget {
|
||||||
const Login({Key key}) : super(key: key);
|
const Login({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
LoginController createState() => LoginController();
|
LoginController createState() => LoginController();
|
||||||
@ -24,8 +24,8 @@ class Login extends StatefulWidget {
|
|||||||
class LoginController extends State<Login> {
|
class LoginController extends State<Login> {
|
||||||
final TextEditingController usernameController = TextEditingController();
|
final TextEditingController usernameController = TextEditingController();
|
||||||
final TextEditingController passwordController = TextEditingController();
|
final TextEditingController passwordController = TextEditingController();
|
||||||
String usernameError;
|
String? usernameError;
|
||||||
String passwordError;
|
String? passwordError;
|
||||||
bool loading = false;
|
bool loading = false;
|
||||||
bool showPassword = false;
|
bool showPassword = false;
|
||||||
|
|
||||||
@ -34,12 +34,12 @@ class LoginController extends State<Login> {
|
|||||||
void login([_]) async {
|
void login([_]) async {
|
||||||
final matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
if (usernameController.text.isEmpty) {
|
if (usernameController.text.isEmpty) {
|
||||||
setState(() => usernameError = L10n.of(context).pleaseEnterYourUsername);
|
setState(() => usernameError = L10n.of(context)!.pleaseEnterYourUsername);
|
||||||
} else {
|
} else {
|
||||||
setState(() => usernameError = null);
|
setState(() => usernameError = null);
|
||||||
}
|
}
|
||||||
if (passwordController.text.isEmpty) {
|
if (passwordController.text.isEmpty) {
|
||||||
setState(() => passwordError = L10n.of(context).pleaseEnterYourPassword);
|
setState(() => passwordError = L10n.of(context)!.pleaseEnterYourPassword);
|
||||||
} else {
|
} else {
|
||||||
setState(() => passwordError = null);
|
setState(() => passwordError = null);
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ class LoginController extends State<Login> {
|
|||||||
if (mounted) setState(() => loading = false);
|
if (mounted) setState(() => loading = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer _coolDown;
|
Timer? _coolDown;
|
||||||
|
|
||||||
void checkWellKnownWithCoolDown(String userId) async {
|
void checkWellKnownWithCoolDown(String userId) async {
|
||||||
_coolDown?.cancel();
|
_coolDown?.cancel();
|
||||||
@ -100,14 +100,13 @@ class LoginController extends State<Login> {
|
|||||||
if (!userId.isValidMatrixId) return;
|
if (!userId.isValidMatrixId) return;
|
||||||
try {
|
try {
|
||||||
final oldHomeserver = Matrix.of(context).getLoginClient().homeserver;
|
final oldHomeserver = Matrix.of(context).getLoginClient().homeserver;
|
||||||
var newDomain = Uri.https(userId.domain, '');
|
var newDomain = Uri.https(userId.domain!, '');
|
||||||
Matrix.of(context).getLoginClient().homeserver = newDomain;
|
Matrix.of(context).getLoginClient().homeserver = newDomain;
|
||||||
DiscoveryInformation wellKnownInformation;
|
DiscoveryInformation? wellKnownInformation;
|
||||||
try {
|
try {
|
||||||
wellKnownInformation =
|
wellKnownInformation =
|
||||||
await Matrix.of(context).getLoginClient().getWellknown();
|
await Matrix.of(context).getLoginClient().getWellknown();
|
||||||
if (wellKnownInformation.mHomeserver?.baseUrl?.toString()?.isNotEmpty ??
|
if (wellKnownInformation.mHomeserver.baseUrl.toString().isNotEmpty) {
|
||||||
false) {
|
|
||||||
newDomain = wellKnownInformation.mHomeserver.baseUrl;
|
newDomain = wellKnownInformation.mHomeserver.baseUrl;
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
@ -130,9 +129,10 @@ class LoginController extends State<Login> {
|
|||||||
final dialogResult = await showOkCancelAlertDialog(
|
final dialogResult = await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
message: L10n.of(context).noMatrixServer(newDomain, oldHomeserver),
|
message:
|
||||||
okLabel: L10n.of(context).ok,
|
L10n.of(context)!.noMatrixServer(newDomain, oldHomeserver!),
|
||||||
cancelLabel: L10n.of(context).cancel,
|
okLabel: L10n.of(context)!.ok,
|
||||||
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
);
|
);
|
||||||
if (dialogResult == OkCancelResult.ok) {
|
if (dialogResult == OkCancelResult.ok) {
|
||||||
setState(() => usernameError = null);
|
setState(() => usernameError = null);
|
||||||
@ -142,7 +142,7 @@ class LoginController extends State<Login> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var jitsi = wellKnownInformation?.additionalProperties
|
var jitsi = wellKnownInformation?.additionalProperties
|
||||||
?.tryGet<Map<String, dynamic>>('im.vector.riot.jitsi')
|
.tryGet<Map<String, dynamic>>('im.vector.riot.jitsi')
|
||||||
?.tryGet<String>('preferredDomain');
|
?.tryGet<String>('preferredDomain');
|
||||||
if (jitsi != null) {
|
if (jitsi != null) {
|
||||||
if (!jitsi.endsWith('/')) {
|
if (!jitsi.endsWith('/')) {
|
||||||
@ -168,12 +168,12 @@ class LoginController extends State<Login> {
|
|||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).enterAnEmailAddress,
|
title: L10n.of(context)!.enterAnEmailAddress,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
hintText: L10n.of(context).enterAnEmailAddress,
|
hintText: L10n.of(context)!.enterAnEmailAddress,
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -194,17 +194,17 @@ class LoginController extends State<Login> {
|
|||||||
final ok = await showOkAlertDialog(
|
final ok = await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).weSentYouAnEmail,
|
title: L10n.of(context)!.weSentYouAnEmail,
|
||||||
message: L10n.of(context).pleaseClickOnLink,
|
message: L10n.of(context)!.pleaseClickOnLink,
|
||||||
okLabel: L10n.of(context).iHaveClickedOnLink,
|
okLabel: L10n.of(context)!.iHaveClickedOnLink,
|
||||||
);
|
);
|
||||||
if (ok == null) return;
|
if (ok != OkCancelResult.ok) return;
|
||||||
final password = await showTextInputDialog(
|
final password = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).chooseAStrongPassword,
|
title: L10n.of(context)!.chooseAStrongPassword,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
const DialogTextField(
|
const DialogTextField(
|
||||||
hintText: '******',
|
hintText: '******',
|
||||||
@ -222,7 +222,7 @@ class LoginController extends State<Login> {
|
|||||||
auth: AuthenticationThreePidCreds(
|
auth: AuthenticationThreePidCreds(
|
||||||
type: AuthenticationTypes.emailIdentity,
|
type: AuthenticationTypes.emailIdentity,
|
||||||
threepidCreds: ThreepidCreds(
|
threepidCreds: ThreepidCreds(
|
||||||
sid: response.result.sid,
|
sid: response.result!.sid,
|
||||||
clientSecret: clientSecret,
|
clientSecret: clientSecret,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -230,7 +230,7 @@ class LoginController extends State<Login> {
|
|||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).passwordHasBeenChanged)));
|
SnackBar(content: Text(L10n.of(context)!.passwordHasBeenChanged)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import 'login.dart';
|
|||||||
class LoginView extends StatelessWidget {
|
class LoginView extends StatelessWidget {
|
||||||
final LoginController controller;
|
final LoginController controller;
|
||||||
|
|
||||||
const LoginView(this.controller, {Key key}) : super(key: key);
|
const LoginView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -19,7 +19,7 @@ class LoginView extends StatelessWidget {
|
|||||||
leading: controller.loading ? Container() : const BackButton(),
|
leading: controller.loading ? Container() : const BackButton(),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).logInTo(Matrix.of(context)
|
L10n.of(context)!.logInTo(Matrix.of(context)
|
||||||
.getLoginClient()
|
.getLoginClient()
|
||||||
.homeserver
|
.homeserver
|
||||||
.toString()
|
.toString()
|
||||||
@ -42,9 +42,9 @@ class LoginView extends StatelessWidget {
|
|||||||
controller.loading ? null : [AutofillHints.username],
|
controller.loading ? null : [AutofillHints.username],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.account_box_outlined),
|
prefixIcon: const Icon(Icons.account_box_outlined),
|
||||||
hintText: L10n.of(context).username,
|
hintText: L10n.of(context)!.username,
|
||||||
errorText: controller.usernameError,
|
errorText: controller.usernameError,
|
||||||
labelText: L10n.of(context).username),
|
labelText: L10n.of(context)!.username),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
@ -62,13 +62,13 @@ class LoginView extends StatelessWidget {
|
|||||||
hintText: '****',
|
hintText: '****',
|
||||||
errorText: controller.passwordError,
|
errorText: controller.passwordError,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
tooltip: L10n.of(context).showPassword,
|
tooltip: L10n.of(context)!.showPassword,
|
||||||
icon: Icon(controller.showPassword
|
icon: Icon(controller.showPassword
|
||||||
? Icons.visibility_off_outlined
|
? Icons.visibility_off_outlined
|
||||||
: Icons.visibility_outlined),
|
: Icons.visibility_outlined),
|
||||||
onPressed: controller.toggleShowPassword,
|
onPressed: controller.toggleShowPassword,
|
||||||
),
|
),
|
||||||
labelText: L10n.of(context).password,
|
labelText: L10n.of(context)!.password,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -83,7 +83,7 @@ class LoginView extends StatelessWidget {
|
|||||||
: () => controller.login(context),
|
: () => controller.login(context),
|
||||||
child: controller.loading
|
child: controller.loading
|
||||||
? const LinearProgressIndicator()
|
? const LinearProgressIndicator()
|
||||||
: Text(L10n.of(context).login),
|
: Text(L10n.of(context)!.login),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -91,7 +91,7 @@ class LoginView extends StatelessWidget {
|
|||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: controller.passwordForgotten,
|
onPressed: controller.passwordForgotten,
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).passwordForgotten,
|
L10n.of(context)!.passwordForgotten,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
|
@ -8,7 +8,7 @@ import 'package:fluffychat/pages/new_group/new_group_view.dart';
|
|||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
|
|
||||||
class NewGroup extends StatefulWidget {
|
class NewGroup extends StatefulWidget {
|
||||||
const NewGroup({Key key}) : super(key: key);
|
const NewGroup({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
NewGroupController createState() => NewGroupController();
|
NewGroupController createState() => NewGroupController();
|
||||||
@ -35,7 +35,7 @@ class NewGroupController extends State<NewGroup> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (roomID.error == null) {
|
if (roomID.error == null) {
|
||||||
VRouter.of(context).toSegments(['rooms', roomID.result, 'invite']);
|
VRouter.of(context).toSegments(['rooms', roomID.result!, 'invite']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,13 +8,13 @@ import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
|||||||
class NewGroupView extends StatelessWidget {
|
class NewGroupView extends StatelessWidget {
|
||||||
final NewGroupController controller;
|
final NewGroupController controller;
|
||||||
|
|
||||||
const NewGroupView(this.controller, {Key key}) : super(key: key);
|
const NewGroupView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(L10n.of(context).createNewGroup),
|
title: Text(L10n.of(context)!.createNewGroup),
|
||||||
),
|
),
|
||||||
body: MaxWidthBody(
|
body: MaxWidthBody(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -29,13 +29,13 @@ class NewGroupView extends StatelessWidget {
|
|||||||
textInputAction: TextInputAction.go,
|
textInputAction: TextInputAction.go,
|
||||||
onSubmitted: controller.submitAction,
|
onSubmitted: controller.submitAction,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: L10n.of(context).optionalGroupName,
|
labelText: L10n.of(context)!.optionalGroupName,
|
||||||
prefixIcon: const Icon(Icons.people_outlined),
|
prefixIcon: const Icon(Icons.people_outlined),
|
||||||
hintText: L10n.of(context).enterAGroupName),
|
hintText: L10n.of(context)!.enterAGroupName),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
title: Text(L10n.of(context).groupIsPublic),
|
title: Text(L10n.of(context)!.groupIsPublic),
|
||||||
value: controller.publicGroup,
|
value: controller.publicGroup,
|
||||||
onChanged: controller.setPublicGroup,
|
onChanged: controller.setPublicGroup,
|
||||||
),
|
),
|
||||||
|
@ -11,7 +11,7 @@ import 'package:fluffychat/utils/url_launcher.dart';
|
|||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
|
|
||||||
class NewPrivateChat extends StatefulWidget {
|
class NewPrivateChat extends StatefulWidget {
|
||||||
const NewPrivateChat({Key key}) : super(key: key);
|
const NewPrivateChat({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
NewPrivateChatController createState() => NewPrivateChatController();
|
NewPrivateChatController createState() => NewPrivateChatController();
|
||||||
@ -48,20 +48,20 @@ class NewPrivateChatController extends State<NewPrivateChat> {
|
|||||||
|
|
||||||
void submitAction([_]) async {
|
void submitAction([_]) async {
|
||||||
controller.text = controller.text.trim();
|
controller.text = controller.text.trim();
|
||||||
if (!formKey.currentState.validate()) return;
|
if (!formKey.currentState!.validate()) return;
|
||||||
UrlLauncher(context, '$prefix${controller.text}').openMatrixToUrl();
|
UrlLauncher(context, '$prefix${controller.text}').openMatrixToUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
String validateForm(String value) {
|
String? validateForm(String? value) {
|
||||||
if (value.isEmpty) {
|
if (value!.isEmpty) {
|
||||||
return L10n.of(context).pleaseEnterAMatrixIdentifier;
|
return L10n.of(context)!.pleaseEnterAMatrixIdentifier;
|
||||||
}
|
}
|
||||||
if (!controller.text.isValidMatrixId ||
|
if (!controller.text.isValidMatrixId ||
|
||||||
!supportedSigils.contains(controller.text.sigil)) {
|
!supportedSigils.contains(controller.text.sigil)) {
|
||||||
return L10n.of(context).makeSureTheIdentifierIsValid;
|
return L10n.of(context)!.makeSureTheIdentifierIsValid;
|
||||||
}
|
}
|
||||||
if (controller.text == Matrix.of(context).client.userID) {
|
if (controller.text == Matrix.of(context).client.userID) {
|
||||||
return L10n.of(context).youCannotInviteYourself;
|
return L10n.of(context)!.youCannotInviteYourself;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
class NewPrivateChatView extends StatelessWidget {
|
class NewPrivateChatView extends StatelessWidget {
|
||||||
final NewPrivateChatController controller;
|
final NewPrivateChatController controller;
|
||||||
|
|
||||||
const NewPrivateChatView(this.controller, {Key key}) : super(key: key);
|
const NewPrivateChatView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
static const double _qrCodePadding = 8;
|
static const double _qrCodePadding = 8;
|
||||||
|
|
||||||
@ -24,13 +24,13 @@ class NewPrivateChatView extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
title: Text(L10n.of(context).newChat),
|
title: Text(L10n.of(context)!.newChat),
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => VRouter.of(context).to('/newgroup'),
|
onPressed: () => VRouter.of(context).to('/newgroup'),
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).createNewGroup,
|
L10n.of(context)!.createNewGroup,
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -67,7 +67,7 @@ class NewPrivateChatView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
subtitle: Text(L10n.of(context).createNewChatExplaination),
|
subtitle: Text(L10n.of(context)!.createNewChatExplaination),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
@ -81,7 +81,7 @@ class NewPrivateChatView extends StatelessWidget {
|
|||||||
onFieldSubmitted: controller.submitAction,
|
onFieldSubmitted: controller.submitAction,
|
||||||
validator: controller.validateForm,
|
validator: controller.validateForm,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: L10n.of(context).typeInInviteLinkManually,
|
labelText: L10n.of(context)!.typeInInviteLinkManually,
|
||||||
hintText: '@username',
|
hintText: '@username',
|
||||||
prefixText: 'matrix.to/#/',
|
prefixText: 'matrix.to/#/',
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
@ -105,7 +105,7 @@ class NewPrivateChatView extends StatelessWidget {
|
|||||||
floatingActionButton: PlatformInfos.isMobile && !controller.hideFab
|
floatingActionButton: PlatformInfos.isMobile && !controller.hideFab
|
||||||
? FloatingActionButton.extended(
|
? FloatingActionButton.extended(
|
||||||
onPressed: controller.openScannerAction,
|
onPressed: controller.openScannerAction,
|
||||||
label: Text(L10n.of(context).scanQrCode),
|
label: Text(L10n.of(context)!.scanQrCode),
|
||||||
icon: const Icon(Icons.camera_alt_outlined),
|
icon: const Icon(Icons.camera_alt_outlined),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
|
@ -9,7 +9,7 @@ import 'package:qr_code_scanner/qr_code_scanner.dart';
|
|||||||
import 'package:fluffychat/utils/url_launcher.dart';
|
import 'package:fluffychat/utils/url_launcher.dart';
|
||||||
|
|
||||||
class QrScannerModal extends StatefulWidget {
|
class QrScannerModal extends StatefulWidget {
|
||||||
const QrScannerModal({Key key}) : super(key: key);
|
const QrScannerModal({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_QrScannerModalState createState() => _QrScannerModalState();
|
_QrScannerModalState createState() => _QrScannerModalState();
|
||||||
@ -17,15 +17,15 @@ class QrScannerModal extends StatefulWidget {
|
|||||||
|
|
||||||
class _QrScannerModalState extends State<QrScannerModal> {
|
class _QrScannerModalState extends State<QrScannerModal> {
|
||||||
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
|
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
|
||||||
QRViewController controller;
|
QRViewController? controller;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void reassemble() {
|
void reassemble() {
|
||||||
super.reassemble();
|
super.reassemble();
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
controller.pauseCamera();
|
controller!.pauseCamera();
|
||||||
} else if (Platform.isIOS) {
|
} else if (Platform.isIOS) {
|
||||||
controller.resumeCamera();
|
controller!.resumeCamera();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,9 +36,9 @@ class _QrScannerModalState extends State<QrScannerModal> {
|
|||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.close_outlined),
|
icon: const Icon(Icons.close_outlined),
|
||||||
onPressed: Navigator.of(context).pop,
|
onPressed: Navigator.of(context).pop,
|
||||||
tooltip: L10n.of(context).close,
|
tooltip: L10n.of(context)!.close,
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).scanQrCode),
|
title: Text(L10n.of(context)!.scanQrCode),
|
||||||
),
|
),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
@ -59,7 +59,7 @@ class _QrScannerModalState extends State<QrScannerModal> {
|
|||||||
|
|
||||||
void _onQRViewCreated(QRViewController controller) {
|
void _onQRViewCreated(QRViewController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
StreamSubscription sub;
|
late StreamSubscription sub;
|
||||||
sub = controller.scannedDataStream.listen((scanData) {
|
sub = controller.scannedDataStream.listen((scanData) {
|
||||||
sub.cancel();
|
sub.cancel();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
@ -9,7 +9,7 @@ import 'package:fluffychat/pages/new_space/new_space_view.dart';
|
|||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
|
|
||||||
class NewSpace extends StatefulWidget {
|
class NewSpace extends StatefulWidget {
|
||||||
const NewSpace({Key key}) : super(key: key);
|
const NewSpace({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
NewSpaceController createState() => NewSpaceController();
|
NewSpaceController createState() => NewSpaceController();
|
||||||
@ -38,7 +38,7 @@ class NewSpaceController extends State<NewSpace> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (roomID.error == null) {
|
if (roomID.error == null) {
|
||||||
VRouter.of(context).toSegments(['rooms', roomID.result, 'details']);
|
VRouter.of(context).toSegments(['rooms', roomID.result!, 'details']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,13 +8,13 @@ import 'new_space.dart';
|
|||||||
class NewSpaceView extends StatelessWidget {
|
class NewSpaceView extends StatelessWidget {
|
||||||
final NewSpaceController controller;
|
final NewSpaceController controller;
|
||||||
|
|
||||||
const NewSpaceView(this.controller, {Key key}) : super(key: key);
|
const NewSpaceView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(L10n.of(context).createNewSpace),
|
title: Text(L10n.of(context)!.createNewSpace),
|
||||||
),
|
),
|
||||||
body: MaxWidthBody(
|
body: MaxWidthBody(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -29,13 +29,13 @@ class NewSpaceView extends StatelessWidget {
|
|||||||
textInputAction: TextInputAction.go,
|
textInputAction: TextInputAction.go,
|
||||||
onSubmitted: controller.submitAction,
|
onSubmitted: controller.submitAction,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: L10n.of(context).spaceName,
|
labelText: L10n.of(context)!.spaceName,
|
||||||
prefixIcon: const Icon(Icons.people_outlined),
|
prefixIcon: const Icon(Icons.people_outlined),
|
||||||
hintText: L10n.of(context).enterASpacepName),
|
hintText: L10n.of(context)!.enterASpacepName),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
title: Text(L10n.of(context).spaceIsPublic),
|
title: Text(L10n.of(context)!.spaceIsPublic),
|
||||||
value: controller.publicGroup,
|
value: controller.publicGroup,
|
||||||
onChanged: controller.setPublicGroup,
|
onChanged: controller.setPublicGroup,
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -114,7 +112,7 @@ class SearchController extends State<Search> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance?.addPostFrameCallback((_) async {
|
WidgetsBinding.instance?.addPostFrameCallback((_) async {
|
||||||
controller.text = VRouter.of(context).queryParameters['query'] ?? '';
|
controller.text = VRouter.of(context).queryParameters['query'] ?? '';
|
||||||
final server = await Store().getItem(_serverStoreNamespace) as String?;
|
final server = await Store().getItem(_serverStoreNamespace);
|
||||||
if (server?.isNotEmpty ?? false) {
|
if (server?.isNotEmpty ?? false) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,12 @@ import 'search.dart';
|
|||||||
class SearchView extends StatelessWidget {
|
class SearchView extends StatelessWidget {
|
||||||
final SearchController controller;
|
final SearchController controller;
|
||||||
|
|
||||||
const SearchView(this.controller, {Key key}) : super(key: key);
|
const SearchView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final server = controller.genericSearchTerm?.isValidMatrixId ?? false
|
final server = controller.genericSearchTerm?.isValidMatrixId ?? false
|
||||||
? controller.genericSearchTerm.domain
|
? controller.genericSearchTerm!.domain
|
||||||
: controller.server;
|
: controller.server;
|
||||||
if (controller.lastServer != server) {
|
if (controller.lastServer != server) {
|
||||||
controller.lastServer = server;
|
controller.lastServer = server;
|
||||||
@ -44,15 +44,22 @@ class SearchView extends StatelessWidget {
|
|||||||
'chunk': [],
|
'chunk': [],
|
||||||
});
|
});
|
||||||
}).then((QueryPublicRoomsResponse res) {
|
}).then((QueryPublicRoomsResponse res) {
|
||||||
if (controller.genericSearchTerm != null &&
|
final genericSearchTerm = controller.genericSearchTerm;
|
||||||
|
if (genericSearchTerm != null &&
|
||||||
!res.chunk.any((room) =>
|
!res.chunk.any((room) =>
|
||||||
(room.aliases?.contains(controller.genericSearchTerm) ?? false) ||
|
(room.aliases?.contains(controller.genericSearchTerm) ?? false) ||
|
||||||
room.canonicalAlias == controller.genericSearchTerm)) {
|
room.canonicalAlias == controller.genericSearchTerm)) {
|
||||||
// we have to tack on the original alias
|
// we have to tack on the original alias
|
||||||
res.chunk.add(PublicRoomsChunk.fromJson(<String, dynamic>{
|
res.chunk.add(
|
||||||
'aliases': [controller.genericSearchTerm],
|
PublicRoomsChunk(
|
||||||
'name': controller.genericSearchTerm,
|
aliases: [genericSearchTerm],
|
||||||
}));
|
name: genericSearchTerm,
|
||||||
|
numJoinedMembers: 0,
|
||||||
|
roomId: '!unknown',
|
||||||
|
worldReadable: true,
|
||||||
|
guestCanJoin: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
@ -68,15 +75,14 @@ class SearchView extends StatelessWidget {
|
|||||||
const tabCount = 3;
|
const tabCount = 3;
|
||||||
return DefaultTabController(
|
return DefaultTabController(
|
||||||
length: tabCount,
|
length: tabCount,
|
||||||
initialIndex:
|
initialIndex: controller.controller.text.startsWith('#') ? 0 : 1,
|
||||||
controller.controller.text?.startsWith('#') ?? false ? 0 : 1,
|
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: DefaultAppBarSearchField(
|
title: DefaultAppBarSearchField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
hintText: L10n.of(context).search,
|
hintText: L10n.of(context)!.search,
|
||||||
searchController: controller.controller,
|
searchController: controller.controller,
|
||||||
suffix: const Icon(Icons.search_outlined),
|
suffix: const Icon(Icons.search_outlined),
|
||||||
onChanged: controller.search,
|
onChanged: controller.search,
|
||||||
@ -84,16 +90,16 @@ class SearchView extends StatelessWidget {
|
|||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
indicatorColor: Theme.of(context).colorScheme.secondary,
|
indicatorColor: Theme.of(context).colorScheme.secondary,
|
||||||
labelColor: Theme.of(context).colorScheme.secondary,
|
labelColor: Theme.of(context).colorScheme.secondary,
|
||||||
unselectedLabelColor: Theme.of(context).textTheme.bodyText1.color,
|
unselectedLabelColor: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
labelStyle: const TextStyle(fontSize: 16),
|
labelStyle: const TextStyle(fontSize: 16),
|
||||||
labelPadding: const EdgeInsets.symmetric(
|
labelPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: 8,
|
horizontal: 8,
|
||||||
vertical: 0,
|
vertical: 0,
|
||||||
),
|
),
|
||||||
tabs: [
|
tabs: [
|
||||||
Tab(child: Text(L10n.of(context).discover, maxLines: 1)),
|
Tab(child: Text(L10n.of(context)!.discover, maxLines: 1)),
|
||||||
Tab(child: Text(L10n.of(context).chats, maxLines: 1)),
|
Tab(child: Text(L10n.of(context)!.chats, maxLines: 1)),
|
||||||
Tab(child: Text(L10n.of(context).people, maxLines: 1)),
|
Tab(child: Text(L10n.of(context)!.people, maxLines: 1)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -111,7 +117,7 @@ class SearchView extends StatelessWidget {
|
|||||||
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
||||||
child: const Icon(Icons.edit_outlined),
|
child: const Icon(Icons.edit_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).changeTheServer),
|
title: Text(L10n.of(context)!.changeTheServer),
|
||||||
onTap: controller.setServer,
|
onTap: controller.setServer,
|
||||||
),
|
),
|
||||||
FutureBuilder<QueryPublicRoomsResponse>(
|
FutureBuilder<QueryPublicRoomsResponse>(
|
||||||
@ -130,7 +136,7 @@ class SearchView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
snapshot.error.toLocalizedString(context),
|
snapshot.error!.toLocalizedString(context),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
@ -146,7 +152,7 @@ class SearchView extends StatelessWidget {
|
|||||||
child: CircularProgressIndicator.adaptive(
|
child: CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 2));
|
strokeWidth: 2));
|
||||||
}
|
}
|
||||||
final publicRoomsResponse = snapshot.data;
|
final publicRoomsResponse = snapshot.data!;
|
||||||
if (publicRoomsResponse.chunk.isEmpty) {
|
if (publicRoomsResponse.chunk.isEmpty) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -159,7 +165,7 @@ class SearchView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).noPublicRoomsFound,
|
L10n.of(context)!.noPublicRoomsFound,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
@ -201,7 +207,7 @@ class SearchView extends StatelessWidget {
|
|||||||
name: publicRoomsResponse.chunk[i].name,
|
name: publicRoomsResponse.chunk[i].name,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
publicRoomsResponse.chunk[i].name,
|
publicRoomsResponse.chunk[i].name!,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -210,17 +216,16 @@ class SearchView extends StatelessWidget {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context).countParticipants(
|
L10n.of(context)!.countParticipants(
|
||||||
publicRoomsResponse
|
publicRoomsResponse
|
||||||
.chunk[i].numJoinedMembers ??
|
.chunk[i].numJoinedMembers),
|
||||||
0),
|
|
||||||
style: const TextStyle(fontSize: 10.5),
|
style: const TextStyle(fontSize: 10.5),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
publicRoomsResponse.chunk[i].topic ??
|
publicRoomsResponse.chunk[i].topic ??
|
||||||
L10n.of(context).noDescription,
|
L10n.of(context)!.noDescription,
|
||||||
maxLines: 4,
|
maxLines: 4,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
@ -261,7 +266,7 @@ class SearchView extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
if (roomID.error == null) {
|
if (roomID.error == null) {
|
||||||
VRouter.of(context)
|
VRouter.of(context)
|
||||||
.toSegments(['rooms', roomID.result]);
|
.toSegments(['rooms', roomID.result!]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leading: Avatar(
|
leading: Avatar(
|
||||||
@ -271,7 +276,7 @@ class SearchView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
foundProfile.displayName ??
|
foundProfile.displayName ??
|
||||||
foundProfile.userId.localpart,
|
foundProfile.userId.localpart!,
|
||||||
style: const TextStyle(),
|
style: const TextStyle(),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
|
@ -14,19 +14,19 @@ import '../../widgets/matrix.dart';
|
|||||||
import 'settings_view.dart';
|
import 'settings_view.dart';
|
||||||
|
|
||||||
class Settings extends StatefulWidget {
|
class Settings extends StatefulWidget {
|
||||||
const Settings({Key key}) : super(key: key);
|
const Settings({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SettingsController createState() => SettingsController();
|
SettingsController createState() => SettingsController();
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsController extends State<Settings> {
|
class SettingsController extends State<Settings> {
|
||||||
Future<bool> crossSigningCachedFuture;
|
Future<bool>? crossSigningCachedFuture;
|
||||||
bool crossSigningCached;
|
bool? crossSigningCached;
|
||||||
Future<bool> megolmBackupCachedFuture;
|
Future<bool>? megolmBackupCachedFuture;
|
||||||
bool megolmBackupCached;
|
bool? megolmBackupCached;
|
||||||
Future<dynamic> profileFuture;
|
Future<dynamic>? profileFuture;
|
||||||
Profile profile;
|
Profile? profile;
|
||||||
bool profileUpdated = false;
|
bool profileUpdated = false;
|
||||||
|
|
||||||
void updateProfile() => setState(() {
|
void updateProfile() => setState(() {
|
||||||
@ -39,19 +39,19 @@ class SettingsController extends State<Settings> {
|
|||||||
if (PlatformInfos.isMobile)
|
if (PlatformInfos.isMobile)
|
||||||
SheetAction(
|
SheetAction(
|
||||||
key: AvatarAction.camera,
|
key: AvatarAction.camera,
|
||||||
label: L10n.of(context).openCamera,
|
label: L10n.of(context)!.openCamera,
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
icon: Icons.camera_alt_outlined,
|
icon: Icons.camera_alt_outlined,
|
||||||
),
|
),
|
||||||
SheetAction(
|
SheetAction(
|
||||||
key: AvatarAction.file,
|
key: AvatarAction.file,
|
||||||
label: L10n.of(context).openGallery,
|
label: L10n.of(context)!.openGallery,
|
||||||
icon: Icons.photo_outlined,
|
icon: Icons.photo_outlined,
|
||||||
),
|
),
|
||||||
if (profile?.avatarUrl != null)
|
if (profile?.avatarUrl != null)
|
||||||
SheetAction(
|
SheetAction(
|
||||||
key: AvatarAction.remove,
|
key: AvatarAction.remove,
|
||||||
label: L10n.of(context).removeYourAvatar,
|
label: L10n.of(context)!.removeYourAvatar,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
icon: Icons.delete_outlined,
|
icon: Icons.delete_outlined,
|
||||||
),
|
),
|
||||||
@ -60,7 +60,7 @@ class SettingsController extends State<Settings> {
|
|||||||
? actions.single
|
? actions.single
|
||||||
: await showModalActionSheet<AvatarAction>(
|
: await showModalActionSheet<AvatarAction>(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).changeYourAvatar,
|
title: L10n.of(context)!.changeYourAvatar,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
if (action == null) return;
|
if (action == null) return;
|
||||||
@ -91,10 +91,10 @@ class SettingsController extends State<Settings> {
|
|||||||
} else {
|
} else {
|
||||||
final result =
|
final result =
|
||||||
await FilePickerCross.importFromStorage(type: FileTypeCross.image);
|
await FilePickerCross.importFromStorage(type: FileTypeCross.image);
|
||||||
if (result == null) return;
|
if (result.fileName == null) return;
|
||||||
file = MatrixFile(
|
file = MatrixFile(
|
||||||
bytes: result.toUint8List(),
|
bytes: result.toUint8List(),
|
||||||
name: result.fileName,
|
name: result.fileName!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
@ -111,7 +111,7 @@ class SettingsController extends State<Settings> {
|
|||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
profileFuture ??= client
|
profileFuture ??= client
|
||||||
.getProfileFromUserId(
|
.getProfileFromUserId(
|
||||||
client.userID,
|
client.userID!,
|
||||||
cache: !profileUpdated,
|
cache: !profileUpdated,
|
||||||
getFromRooms: !profileUpdated,
|
getFromRooms: !profileUpdated,
|
||||||
)
|
)
|
||||||
@ -121,12 +121,12 @@ class SettingsController extends State<Settings> {
|
|||||||
});
|
});
|
||||||
if (client.encryption != null) {
|
if (client.encryption != null) {
|
||||||
crossSigningCachedFuture ??=
|
crossSigningCachedFuture ??=
|
||||||
client.encryption?.crossSigning?.isCached()?.then((c) {
|
client.encryption?.crossSigning.isCached().then((c) {
|
||||||
if (mounted) setState(() => crossSigningCached = c);
|
if (mounted) setState(() => crossSigningCached = c);
|
||||||
return c;
|
return c;
|
||||||
});
|
});
|
||||||
megolmBackupCachedFuture ??=
|
megolmBackupCachedFuture ??=
|
||||||
client.encryption?.keyManager?.isCached()?.then((c) {
|
client.encryption?.keyManager.isCached().then((c) {
|
||||||
if (mounted) setState(() => megolmBackupCached = c);
|
if (mounted) setState(() => megolmBackupCached = c);
|
||||||
return c;
|
return c;
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,7 @@ import 'settings.dart';
|
|||||||
class SettingsView extends StatelessWidget {
|
class SettingsView extends StatelessWidget {
|
||||||
final SettingsController controller;
|
final SettingsController controller;
|
||||||
|
|
||||||
const SettingsView(this.controller, {Key key}) : super(key: key);
|
const SettingsView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -25,11 +25,11 @@ class SettingsView extends StatelessWidget {
|
|||||||
expandedHeight: 300.0,
|
expandedHeight: 300.0,
|
||||||
floating: true,
|
floating: true,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
title: Text(L10n.of(context).settings),
|
title: Text(L10n.of(context)!.settings),
|
||||||
backgroundColor: Theme.of(context).appBarTheme.backgroundColor,
|
backgroundColor: Theme.of(context).appBarTheme.backgroundColor,
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
background: ContentBanner(
|
background: ContentBanner(
|
||||||
controller.profile?.avatarUrl,
|
mxContent: controller.profile?.avatarUrl,
|
||||||
onEdit: controller.setAvatarAction,
|
onEdit: controller.setAvatarAction,
|
||||||
defaultIcon: Icons.person_outline_outlined,
|
defaultIcon: Icons.person_outline_outlined,
|
||||||
),
|
),
|
||||||
@ -37,54 +37,54 @@ class SettingsView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
body: ListTileTheme(
|
body: ListTileTheme(
|
||||||
iconColor: Theme.of(context).textTheme.bodyText1.color,
|
iconColor: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.format_paint_outlined),
|
leading: const Icon(Icons.format_paint_outlined),
|
||||||
title: Text(L10n.of(context).changeTheme),
|
title: Text(L10n.of(context)!.changeTheme),
|
||||||
onTap: () => VRouter.of(context).to('/settings/style'),
|
onTap: () => VRouter.of(context).to('/settings/style'),
|
||||||
),
|
),
|
||||||
const Divider(thickness: 1),
|
const Divider(thickness: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.notifications_outlined),
|
leading: const Icon(Icons.notifications_outlined),
|
||||||
title: Text(L10n.of(context).notifications),
|
title: Text(L10n.of(context)!.notifications),
|
||||||
onTap: () => VRouter.of(context).to('/settings/notifications'),
|
onTap: () => VRouter.of(context).to('/settings/notifications'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.devices_outlined),
|
leading: const Icon(Icons.devices_outlined),
|
||||||
title: Text(L10n.of(context).devices),
|
title: Text(L10n.of(context)!.devices),
|
||||||
onTap: () => VRouter.of(context).to('/settings/devices'),
|
onTap: () => VRouter.of(context).to('/settings/devices'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.chat_bubble_outline_outlined),
|
leading: const Icon(Icons.chat_bubble_outline_outlined),
|
||||||
title: Text(L10n.of(context).chat),
|
title: Text(L10n.of(context)!.chat),
|
||||||
onTap: () => VRouter.of(context).to('/settings/chat'),
|
onTap: () => VRouter.of(context).to('/settings/chat'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.account_circle_outlined),
|
leading: const Icon(Icons.account_circle_outlined),
|
||||||
title: Text(L10n.of(context).account),
|
title: Text(L10n.of(context)!.account),
|
||||||
onTap: () => VRouter.of(context).to('/settings/account'),
|
onTap: () => VRouter.of(context).to('/settings/account'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.shield_outlined),
|
leading: const Icon(Icons.shield_outlined),
|
||||||
title: Text(L10n.of(context).security),
|
title: Text(L10n.of(context)!.security),
|
||||||
onTap: () => VRouter.of(context).to('/settings/security'),
|
onTap: () => VRouter.of(context).to('/settings/security'),
|
||||||
),
|
),
|
||||||
const Divider(thickness: 1),
|
const Divider(thickness: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.help_outline_outlined),
|
leading: const Icon(Icons.help_outline_outlined),
|
||||||
title: Text(L10n.of(context).help),
|
title: Text(L10n.of(context)!.help),
|
||||||
onTap: () => launch(AppConfig.supportUrl),
|
onTap: () => launch(AppConfig.supportUrl),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.shield_sharp),
|
leading: const Icon(Icons.shield_sharp),
|
||||||
title: Text(L10n.of(context).privacy),
|
title: Text(L10n.of(context)!.privacy),
|
||||||
onTap: () => launch(AppConfig.privacyUrl),
|
onTap: () => launch(AppConfig.privacyUrl),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.info_outline_rounded),
|
leading: const Icon(Icons.info_outline_rounded),
|
||||||
title: Text(L10n.of(context).about),
|
title: Text(L10n.of(context)!.about),
|
||||||
onTap: () => PlatformInfos.showDialog(context),
|
onTap: () => PlatformInfos.showDialog(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -11,7 +11,7 @@ import 'settings_3pid_view.dart';
|
|||||||
class Settings3Pid extends StatefulWidget {
|
class Settings3Pid extends StatefulWidget {
|
||||||
static int sendAttempt = 0;
|
static int sendAttempt = 0;
|
||||||
|
|
||||||
const Settings3Pid({Key key}) : super(key: key);
|
const Settings3Pid({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Settings3PidController createState() => Settings3PidController();
|
Settings3PidController createState() => Settings3PidController();
|
||||||
@ -22,12 +22,12 @@ class Settings3PidController extends State<Settings3Pid> {
|
|||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).enterAnEmailAddress,
|
title: L10n.of(context)!.enterAnEmailAddress,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
hintText: L10n.of(context).enterAnEmailAddress,
|
hintText: L10n.of(context)!.enterAnEmailAddress,
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -46,17 +46,17 @@ class Settings3PidController extends State<Settings3Pid> {
|
|||||||
final ok = await showOkAlertDialog(
|
final ok = await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).weSentYouAnEmail,
|
title: L10n.of(context)!.weSentYouAnEmail,
|
||||||
message: L10n.of(context).pleaseClickOnLink,
|
message: L10n.of(context)!.pleaseClickOnLink,
|
||||||
okLabel: L10n.of(context).iHaveClickedOnLink,
|
okLabel: L10n.of(context)!.iHaveClickedOnLink,
|
||||||
);
|
);
|
||||||
if (ok == null) return;
|
if (ok != OkCancelResult.ok) return;
|
||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context).client.uiaRequestBackground(
|
future: () => Matrix.of(context).client.uiaRequestBackground(
|
||||||
(auth) => Matrix.of(context).client.add3PID(
|
(auth) => Matrix.of(context).client.add3PID(
|
||||||
clientSecret,
|
clientSecret,
|
||||||
response.result.sid,
|
response.result!.sid,
|
||||||
auth: auth,
|
auth: auth,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -65,15 +65,15 @@ class Settings3PidController extends State<Settings3Pid> {
|
|||||||
setState(() => request = null);
|
setState(() => request = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ThirdPartyIdentifier>> request;
|
Future<List<ThirdPartyIdentifier>?>? request;
|
||||||
|
|
||||||
void delete3Pid(ThirdPartyIdentifier identifier) async {
|
void delete3Pid(ThirdPartyIdentifier identifier) async {
|
||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: L10n.of(context)!.areYouSure,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: L10n.of(context)!.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
) !=
|
) !=
|
||||||
OkCancelResult.ok) {
|
OkCancelResult.ok) {
|
||||||
return;
|
return;
|
||||||
|
@ -10,7 +10,7 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
class Settings3PidView extends StatelessWidget {
|
class Settings3PidView extends StatelessWidget {
|
||||||
final Settings3PidController controller;
|
final Settings3PidController controller;
|
||||||
|
|
||||||
const Settings3PidView(this.controller, {Key key}) : super(key: key);
|
const Settings3PidView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -18,20 +18,20 @@ class Settings3PidView extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
title: Text(L10n.of(context).passwordRecovery),
|
title: Text(L10n.of(context)!.passwordRecovery),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.add_outlined),
|
icon: const Icon(Icons.add_outlined),
|
||||||
onPressed: controller.add3PidAction,
|
onPressed: controller.add3PidAction,
|
||||||
tooltip: L10n.of(context).addEmail,
|
tooltip: L10n.of(context)!.addEmail,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: MaxWidthBody(
|
body: MaxWidthBody(
|
||||||
child: FutureBuilder<List<ThirdPartyIdentifier>>(
|
child: FutureBuilder<List<ThirdPartyIdentifier>?>(
|
||||||
future: controller.request,
|
future: controller.request,
|
||||||
builder: (BuildContext context,
|
builder: (BuildContext context,
|
||||||
AsyncSnapshot<List<ThirdPartyIdentifier>> snapshot) {
|
AsyncSnapshot<List<ThirdPartyIdentifier>?> snapshot) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -44,7 +44,7 @@ class Settings3PidView extends StatelessWidget {
|
|||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2));
|
child: CircularProgressIndicator.adaptive(strokeWidth: 2));
|
||||||
}
|
}
|
||||||
final identifier = snapshot.data;
|
final identifier = snapshot.data!;
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
@ -60,8 +60,8 @@ class Settings3PidView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
identifier.isEmpty
|
identifier.isEmpty
|
||||||
? L10n.of(context).noPasswordRecoveryDescription
|
? L10n.of(context)!.noPasswordRecoveryDescription
|
||||||
: L10n.of(context)
|
: L10n.of(context)!
|
||||||
.withTheseAddressesRecoveryDescription,
|
.withTheseAddressesRecoveryDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -77,7 +77,7 @@ class Settings3PidView extends StatelessWidget {
|
|||||||
child: Icon(identifier[i].iconData)),
|
child: Icon(identifier[i].iconData)),
|
||||||
title: Text(identifier[i].address),
|
title: Text(identifier[i].address),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
tooltip: L10n.of(context).delete,
|
tooltip: L10n.of(context)!.delete,
|
||||||
icon: const Icon(Icons.delete_forever_outlined),
|
icon: const Icon(Icons.delete_forever_outlined),
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
onPressed: () => controller.delete3Pid(identifier[i]),
|
onPressed: () => controller.delete3Pid(identifier[i]),
|
||||||
@ -102,6 +102,5 @@ extension on ThirdPartyIdentifier {
|
|||||||
case ThirdPartyIdentifierMedium.msisdn:
|
case ThirdPartyIdentifierMedium.msisdn:
|
||||||
return Icons.phone_android_outlined;
|
return Icons.phone_android_outlined;
|
||||||
}
|
}
|
||||||
return Icons.device_unknown_outlined;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,15 @@ import 'package:fluffychat/pages/settings_account/settings_account_view.dart';
|
|||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
|
|
||||||
class SettingsAccount extends StatefulWidget {
|
class SettingsAccount extends StatefulWidget {
|
||||||
const SettingsAccount({Key key}) : super(key: key);
|
const SettingsAccount({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SettingsAccountController createState() => SettingsAccountController();
|
SettingsAccountController createState() => SettingsAccountController();
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsAccountController extends State<SettingsAccount> {
|
class SettingsAccountController extends State<SettingsAccount> {
|
||||||
Future<dynamic> profileFuture;
|
Future<dynamic>? profileFuture;
|
||||||
Profile profile;
|
Profile? profile;
|
||||||
bool profileUpdated = false;
|
bool profileUpdated = false;
|
||||||
|
|
||||||
void updateProfile() => setState(() {
|
void updateProfile() => setState(() {
|
||||||
@ -30,13 +30,13 @@ class SettingsAccountController extends State<SettingsAccount> {
|
|||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).editDisplayname,
|
title: L10n.of(context)!.editDisplayname,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
initialText: profile?.displayName ??
|
initialText: profile?.displayName ??
|
||||||
Matrix.of(context).client.userID.localpart,
|
Matrix.of(context).client.userID!.localpart,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -45,7 +45,7 @@ class SettingsAccountController extends State<SettingsAccount> {
|
|||||||
final success = await showFutureLoadingDialog(
|
final success = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () =>
|
future: () =>
|
||||||
matrix.client.setDisplayName(matrix.client.userID, input.single),
|
matrix.client.setDisplayName(matrix.client.userID!, input.single),
|
||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
updateProfile();
|
updateProfile();
|
||||||
@ -56,9 +56,9 @@ class SettingsAccountController extends State<SettingsAccount> {
|
|||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSureYouWantToLogout,
|
title: L10n.of(context)!.areYouSureYouWantToLogout,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: L10n.of(context)!.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.cancel) {
|
OkCancelResult.cancel) {
|
||||||
return;
|
return;
|
||||||
@ -74,10 +74,10 @@ class SettingsAccountController extends State<SettingsAccount> {
|
|||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).warning,
|
title: L10n.of(context)!.warning,
|
||||||
message: L10n.of(context).deactivateAccountWarning,
|
message: L10n.of(context)!.deactivateAccountWarning,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.cancel) {
|
OkCancelResult.cancel) {
|
||||||
return;
|
return;
|
||||||
@ -85,9 +85,9 @@ class SettingsAccountController extends State<SettingsAccount> {
|
|||||||
if (await showOkCancelAlertDialog(
|
if (await showOkCancelAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).areYouSure,
|
title: L10n.of(context)!.areYouSure,
|
||||||
okLabel: L10n.of(context).yes,
|
okLabel: L10n.of(context)!.yes,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
) ==
|
) ==
|
||||||
OkCancelResult.cancel) {
|
OkCancelResult.cancel) {
|
||||||
return;
|
return;
|
||||||
@ -95,9 +95,9 @@ class SettingsAccountController extends State<SettingsAccount> {
|
|||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).pleaseEnterYourPassword,
|
title: L10n.of(context)!.pleaseEnterYourPassword,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
const DialogTextField(
|
const DialogTextField(
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
@ -114,7 +114,7 @@ class SettingsAccountController extends State<SettingsAccount> {
|
|||||||
auth: AuthenticationPassword(
|
auth: AuthenticationPassword(
|
||||||
password: input.single,
|
password: input.single,
|
||||||
identifier: AuthenticationUserIdentifier(
|
identifier: AuthenticationUserIdentifier(
|
||||||
user: Matrix.of(context).client.userID),
|
user: Matrix.of(context).client.userID!),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -127,7 +127,7 @@ class SettingsAccountController extends State<SettingsAccount> {
|
|||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
profileFuture ??= client
|
profileFuture ??= client
|
||||||
.getProfileFromUserId(
|
.getProfileFromUserId(
|
||||||
client.userID,
|
client.userID!,
|
||||||
cache: !profileUpdated,
|
cache: !profileUpdated,
|
||||||
getFromRooms: !profileUpdated,
|
getFromRooms: !profileUpdated,
|
||||||
)
|
)
|
||||||
|
@ -10,51 +10,51 @@ import 'settings_account.dart';
|
|||||||
|
|
||||||
class SettingsAccountView extends StatelessWidget {
|
class SettingsAccountView extends StatelessWidget {
|
||||||
final SettingsAccountController controller;
|
final SettingsAccountController controller;
|
||||||
const SettingsAccountView(this.controller, {Key key}) : super(key: key);
|
const SettingsAccountView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(L10n.of(context).account)),
|
appBar: AppBar(title: Text(L10n.of(context)!.account)),
|
||||||
body: ListTileTheme(
|
body: ListTileTheme(
|
||||||
iconColor: Theme.of(context).textTheme.bodyText1.color,
|
iconColor: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
child: MaxWidthBody(
|
child: MaxWidthBody(
|
||||||
withScrolling: true,
|
withScrolling: true,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).yourUserId),
|
title: Text(L10n.of(context)!.yourUserId),
|
||||||
subtitle: Text(Matrix.of(context).client.userID),
|
subtitle: Text(Matrix.of(context).client.userID!),
|
||||||
trailing: const Icon(Icons.copy_outlined),
|
trailing: const Icon(Icons.copy_outlined),
|
||||||
onTap: () => FluffyShare.share(
|
onTap: () => FluffyShare.share(
|
||||||
Matrix.of(context).client.userID,
|
Matrix.of(context).client.userID!,
|
||||||
context,
|
context,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.edit_outlined),
|
trailing: const Icon(Icons.edit_outlined),
|
||||||
title: Text(L10n.of(context).editDisplayname),
|
title: Text(L10n.of(context)!.editDisplayname),
|
||||||
subtitle: Text(controller.profile?.displayName ??
|
subtitle: Text(controller.profile?.displayName ??
|
||||||
Matrix.of(context).client.userID.localpart),
|
Matrix.of(context).client.userID!.localpart!),
|
||||||
onTap: controller.setDisplaynameAction,
|
onTap: controller.setDisplaynameAction,
|
||||||
),
|
),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.person_add_outlined),
|
trailing: const Icon(Icons.person_add_outlined),
|
||||||
title: Text(L10n.of(context).addAccount),
|
title: Text(L10n.of(context)!.addAccount),
|
||||||
subtitle: Text(L10n.of(context).enableMultiAccounts),
|
subtitle: Text(L10n.of(context)!.enableMultiAccounts),
|
||||||
onTap: controller.addAccountAction,
|
onTap: controller.addAccountAction,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.exit_to_app_outlined),
|
trailing: const Icon(Icons.exit_to_app_outlined),
|
||||||
title: Text(L10n.of(context).logout),
|
title: Text(L10n.of(context)!.logout),
|
||||||
onTap: controller.logoutAction,
|
onTap: controller.logoutAction,
|
||||||
),
|
),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.delete_outlined),
|
trailing: const Icon(Icons.delete_outlined),
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).deleteAccount,
|
L10n.of(context)!.deleteAccount,
|
||||||
style: const TextStyle(color: Colors.red),
|
style: const TextStyle(color: Colors.red),
|
||||||
),
|
),
|
||||||
onTap: controller.deleteAccountAction,
|
onTap: controller.deleteAccountAction,
|
||||||
|
@ -9,7 +9,7 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
import 'settings_chat_view.dart';
|
import 'settings_chat_view.dart';
|
||||||
|
|
||||||
class SettingsChat extends StatefulWidget {
|
class SettingsChat extends StatefulWidget {
|
||||||
const SettingsChat({Key key}) : super(key: key);
|
const SettingsChat({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SettingsChatController createState() => SettingsChatController();
|
SettingsChatController createState() => SettingsChatController();
|
||||||
@ -21,9 +21,9 @@ class SettingsChatController extends State<SettingsChat> {
|
|||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).editJitsiInstance,
|
title: L10n.of(context)!.editJitsiInstance,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
initialText: AppConfig.jitsiInstance.replaceFirst(prefix, ''),
|
initialText: AppConfig.jitsiInstance.replaceFirst(prefix, ''),
|
||||||
|
@ -12,52 +12,52 @@ import 'settings_chat.dart';
|
|||||||
|
|
||||||
class SettingsChatView extends StatelessWidget {
|
class SettingsChatView extends StatelessWidget {
|
||||||
final SettingsChatController controller;
|
final SettingsChatController controller;
|
||||||
const SettingsChatView(this.controller, {Key key}) : super(key: key);
|
const SettingsChatView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(L10n.of(context).chat)),
|
appBar: AppBar(title: Text(L10n.of(context)!.chat)),
|
||||||
body: ListTileTheme(
|
body: ListTileTheme(
|
||||||
iconColor: Theme.of(context).textTheme.bodyText1.color,
|
iconColor: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
child: MaxWidthBody(
|
child: MaxWidthBody(
|
||||||
withScrolling: true,
|
withScrolling: true,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SettingsSwitchListTile.adaptive(
|
SettingsSwitchListTile.adaptive(
|
||||||
title: L10n.of(context).renderRichContent,
|
title: L10n.of(context)!.renderRichContent,
|
||||||
onChanged: (b) => AppConfig.renderHtml = b,
|
onChanged: (b) => AppConfig.renderHtml = b,
|
||||||
storeKey: SettingKeys.renderHtml,
|
storeKey: SettingKeys.renderHtml,
|
||||||
defaultValue: AppConfig.renderHtml,
|
defaultValue: AppConfig.renderHtml,
|
||||||
),
|
),
|
||||||
SettingsSwitchListTile.adaptive(
|
SettingsSwitchListTile.adaptive(
|
||||||
title: L10n.of(context).hideRedactedEvents,
|
title: L10n.of(context)!.hideRedactedEvents,
|
||||||
onChanged: (b) => AppConfig.hideRedactedEvents = b,
|
onChanged: (b) => AppConfig.hideRedactedEvents = b,
|
||||||
storeKey: SettingKeys.hideRedactedEvents,
|
storeKey: SettingKeys.hideRedactedEvents,
|
||||||
defaultValue: AppConfig.hideRedactedEvents,
|
defaultValue: AppConfig.hideRedactedEvents,
|
||||||
),
|
),
|
||||||
SettingsSwitchListTile.adaptive(
|
SettingsSwitchListTile.adaptive(
|
||||||
title: L10n.of(context).hideUnknownEvents,
|
title: L10n.of(context)!.hideUnknownEvents,
|
||||||
onChanged: (b) => AppConfig.hideUnknownEvents = b,
|
onChanged: (b) => AppConfig.hideUnknownEvents = b,
|
||||||
storeKey: SettingKeys.hideUnknownEvents,
|
storeKey: SettingKeys.hideUnknownEvents,
|
||||||
defaultValue: AppConfig.hideUnknownEvents,
|
defaultValue: AppConfig.hideUnknownEvents,
|
||||||
),
|
),
|
||||||
SettingsSwitchListTile.adaptive(
|
SettingsSwitchListTile.adaptive(
|
||||||
title: L10n.of(context).autoplayImages,
|
title: L10n.of(context)!.autoplayImages,
|
||||||
onChanged: (b) => AppConfig.autoplayImages = b,
|
onChanged: (b) => AppConfig.autoplayImages = b,
|
||||||
storeKey: SettingKeys.autoplayImages,
|
storeKey: SettingKeys.autoplayImages,
|
||||||
defaultValue: AppConfig.autoplayImages,
|
defaultValue: AppConfig.autoplayImages,
|
||||||
),
|
),
|
||||||
if (PlatformInfos.isMobile)
|
if (PlatformInfos.isMobile)
|
||||||
SettingsSwitchListTile.adaptive(
|
SettingsSwitchListTile.adaptive(
|
||||||
title: L10n.of(context).sendOnEnter,
|
title: L10n.of(context)!.sendOnEnter,
|
||||||
onChanged: (b) => AppConfig.sendOnEnter = b,
|
onChanged: (b) => AppConfig.sendOnEnter = b,
|
||||||
storeKey: SettingKeys.sendOnEnter,
|
storeKey: SettingKeys.sendOnEnter,
|
||||||
defaultValue: AppConfig.sendOnEnter,
|
defaultValue: AppConfig.sendOnEnter,
|
||||||
),
|
),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).emoteSettings),
|
title: Text(L10n.of(context)!.emoteSettings),
|
||||||
onTap: () => VRouter.of(context).to('emotes'),
|
onTap: () => VRouter.of(context).to('emotes'),
|
||||||
trailing: const Padding(
|
trailing: const Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
@ -69,7 +69,7 @@ class SettingsChatView extends StatelessWidget {
|
|||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
child: Icon(Icons.phone_outlined),
|
child: Icon(Icons.phone_outlined),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context).editJitsiInstance),
|
title: Text(L10n.of(context)!.editJitsiInstance),
|
||||||
subtitle: Text(AppConfig.jitsiInstance),
|
subtitle: Text(AppConfig.jitsiInstance),
|
||||||
onTap: controller.setJitsiInstanceAction,
|
onTap: controller.setJitsiInstanceAction,
|
||||||
),
|
),
|
||||||
|
@ -12,27 +12,27 @@ import '../../widgets/matrix.dart';
|
|||||||
import 'settings_emotes_view.dart';
|
import 'settings_emotes_view.dart';
|
||||||
|
|
||||||
class EmotesSettings extends StatefulWidget {
|
class EmotesSettings extends StatefulWidget {
|
||||||
const EmotesSettings({Key key}) : super(key: key);
|
const EmotesSettings({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
EmotesSettingsController createState() => EmotesSettingsController();
|
EmotesSettingsController createState() => EmotesSettingsController();
|
||||||
}
|
}
|
||||||
|
|
||||||
class EmotesSettingsController extends State<EmotesSettings> {
|
class EmotesSettingsController extends State<EmotesSettings> {
|
||||||
String get roomId => VRouter.of(context).pathParameters['roomid'];
|
String? get roomId => VRouter.of(context).pathParameters['roomid'];
|
||||||
Room get room =>
|
Room? get room =>
|
||||||
roomId != null ? Matrix.of(context).client.getRoomById(roomId) : null;
|
roomId != null ? Matrix.of(context).client.getRoomById(roomId!) : null;
|
||||||
String get stateKey => VRouter.of(context).pathParameters['state_key'];
|
String? get stateKey => VRouter.of(context).pathParameters['state_key'];
|
||||||
|
|
||||||
bool showSave = false;
|
bool showSave = false;
|
||||||
TextEditingController newImageCodeController = TextEditingController();
|
TextEditingController newImageCodeController = TextEditingController();
|
||||||
ValueNotifier<ImagePackImageContent> newImageController =
|
ValueNotifier<ImagePackImageContent?> newImageController =
|
||||||
ValueNotifier<ImagePackImageContent>(null);
|
ValueNotifier<ImagePackImageContent?>(null);
|
||||||
|
|
||||||
ImagePackContent _getPack() {
|
ImagePackContent _getPack() {
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
final event = (room != null
|
final event = (room != null
|
||||||
? room.getState('im.ponies.room_emotes', stateKey ?? '')
|
? room!.getState('im.ponies.room_emotes', stateKey ?? '')
|
||||||
: client.accountData['im.ponies.user_emotes']) ??
|
: client.accountData['im.ponies.user_emotes']) ??
|
||||||
BasicEvent.fromJson(<String, dynamic>{
|
BasicEvent.fromJson(<String, dynamic>{
|
||||||
'type': 'm.dummy',
|
'type': 'm.dummy',
|
||||||
@ -42,8 +42,8 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||||||
return BasicEvent.fromJson(event.toJson()).parsedImagePackContent;
|
return BasicEvent.fromJson(event.toJson()).parsedImagePackContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImagePackContent _pack;
|
ImagePackContent? _pack;
|
||||||
ImagePackContent get pack {
|
ImagePackContent? get pack {
|
||||||
if (_pack != null) {
|
if (_pack != null) {
|
||||||
return _pack;
|
return _pack;
|
||||||
}
|
}
|
||||||
@ -60,13 +60,13 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => client.setRoomStateWithKey(
|
future: () => client.setRoomStateWithKey(
|
||||||
room.id, 'im.ponies.room_emotes', stateKey ?? '', pack.toJson()),
|
room!.id, 'im.ponies.room_emotes', stateKey ?? '', pack!.toJson()),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => client.setAccountData(
|
future: () => client.setAccountData(
|
||||||
client.userID, 'im.ponies.user_emotes', pack.toJson()),
|
client.userID!, 'im.ponies.user_emotes', pack!.toJson()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,26 +82,26 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||||||
if (content['rooms'] is! Map) {
|
if (content['rooms'] is! Map) {
|
||||||
content['rooms'] = <String, dynamic>{};
|
content['rooms'] = <String, dynamic>{};
|
||||||
}
|
}
|
||||||
if (content['rooms'][room.id] is! Map) {
|
if (content['rooms'][room!.id] is! Map) {
|
||||||
content['rooms'][room.id] = <String, dynamic>{};
|
content['rooms'][room!.id] = <String, dynamic>{};
|
||||||
}
|
}
|
||||||
if (content['rooms'][room.id][stateKey ?? ''] is! Map) {
|
if (content['rooms'][room!.id][stateKey ?? ''] is! Map) {
|
||||||
content['rooms'][room.id][stateKey ?? ''] = <String, dynamic>{};
|
content['rooms'][room!.id][stateKey ?? ''] = <String, dynamic>{};
|
||||||
}
|
}
|
||||||
} else if (content['rooms'] is Map && content['rooms'][room.id] is Map) {
|
} else if (content['rooms'] is Map && content['rooms'][room!.id] is Map) {
|
||||||
content['rooms'][room.id].remove(stateKey ?? '');
|
content['rooms'][room!.id].remove(stateKey ?? '');
|
||||||
}
|
}
|
||||||
// and save
|
// and save
|
||||||
await showFutureLoadingDialog(
|
await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => client.setAccountData(
|
future: () => client.setAccountData(
|
||||||
client.userID, 'im.ponies.emote_rooms', content),
|
client.userID!, 'im.ponies.emote_rooms', content),
|
||||||
);
|
);
|
||||||
setState(() => null);
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeImageAction(String oldImageCode) => setState(() {
|
void removeImageAction(String oldImageCode) => setState(() {
|
||||||
pack.images.remove(oldImageCode);
|
pack!.images.remove(oldImageCode);
|
||||||
showSave = true;
|
showSave = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,13 +111,13 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||||||
ImagePackImageContent image,
|
ImagePackImageContent image,
|
||||||
TextEditingController controller,
|
TextEditingController controller,
|
||||||
) {
|
) {
|
||||||
if (pack.images.keys.any((k) => k == imageCode && k != oldImageCode)) {
|
if (pack!.images.keys.any((k) => k == imageCode && k != oldImageCode)) {
|
||||||
controller.text = oldImageCode;
|
controller.text = oldImageCode;
|
||||||
showOkAlertDialog(
|
showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
message: L10n.of(context).emoteExists,
|
message: L10n.of(context)!.emoteExists,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -126,29 +126,29 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||||||
showOkAlertDialog(
|
showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
message: L10n.of(context).emoteInvalid,
|
message: L10n.of(context)!.emoteInvalid,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
pack.images[imageCode] = image;
|
pack!.images[imageCode] = image;
|
||||||
pack.images.remove(oldImageCode);
|
pack!.images.remove(oldImageCode);
|
||||||
showSave = true;
|
showSave = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isGloballyActive(Client client) =>
|
bool isGloballyActive(Client? client) =>
|
||||||
room != null &&
|
room != null &&
|
||||||
client.accountData['im.ponies.emote_rooms']?.content is Map &&
|
client!.accountData['im.ponies.emote_rooms']?.content is Map &&
|
||||||
client.accountData['im.ponies.emote_rooms'].content['rooms'] is Map &&
|
client.accountData['im.ponies.emote_rooms']!.content['rooms'] is Map &&
|
||||||
client.accountData['im.ponies.emote_rooms'].content['rooms'][room.id]
|
client.accountData['im.ponies.emote_rooms']!.content['rooms'][room!.id]
|
||||||
is Map &&
|
is Map &&
|
||||||
client.accountData['im.ponies.emote_rooms'].content['rooms'][room.id]
|
client.accountData['im.ponies.emote_rooms']!.content['rooms'][room!.id]
|
||||||
[stateKey ?? ''] is Map;
|
[stateKey ?? ''] is Map;
|
||||||
|
|
||||||
bool get readonly =>
|
bool get readonly =>
|
||||||
room == null ? false : !(room.canSendEvent('im.ponies.room_emotes'));
|
room == null ? false : !(room!.canSendEvent('im.ponies.room_emotes'));
|
||||||
|
|
||||||
void saveAction() async {
|
void saveAction() async {
|
||||||
await _save(context);
|
await _save(context);
|
||||||
@ -158,24 +158,23 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void addImageAction() async {
|
void addImageAction() async {
|
||||||
if (newImageCodeController.text == null ||
|
if (newImageCodeController.text.isEmpty ||
|
||||||
newImageCodeController.text.isEmpty ||
|
|
||||||
newImageController.value == null) {
|
newImageController.value == null) {
|
||||||
await showOkAlertDialog(
|
await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
message: L10n.of(context).emoteWarnNeedToPick,
|
message: L10n.of(context)!.emoteWarnNeedToPick,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final imageCode = newImageCodeController.text;
|
final imageCode = newImageCodeController.text;
|
||||||
if (pack.images.containsKey(imageCode)) {
|
if (pack!.images.containsKey(imageCode)) {
|
||||||
await showOkAlertDialog(
|
await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
message: L10n.of(context).emoteExists,
|
message: L10n.of(context)!.emoteExists,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -183,12 +182,12 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||||||
await showOkAlertDialog(
|
await showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
message: L10n.of(context).emoteInvalid,
|
message: L10n.of(context)!.emoteInvalid,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pack.images[imageCode] = newImageController.value;
|
pack!.images[imageCode] = newImageController.value!;
|
||||||
await _save(context);
|
await _save(context);
|
||||||
setState(() {
|
setState(() {
|
||||||
newImageCodeController.text = '';
|
newImageCodeController.text = '';
|
||||||
@ -198,13 +197,13 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void imagePickerAction(
|
void imagePickerAction(
|
||||||
ValueNotifier<ImagePackImageContent> controller) async {
|
ValueNotifier<ImagePackImageContent?> controller) async {
|
||||||
final result =
|
final result =
|
||||||
await FilePickerCross.importFromStorage(type: FileTypeCross.image);
|
await FilePickerCross.importFromStorage(type: FileTypeCross.image);
|
||||||
if (result == null) return;
|
if (result.fileName == null) return;
|
||||||
var file = MatrixImageFile(
|
var file = MatrixImageFile(
|
||||||
bytes: result.toUint8List(),
|
bytes: result.toUint8List(),
|
||||||
name: result.fileName,
|
name: result.fileName!,
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
file = await file.resizeImage(calcBlurhash: false);
|
file = await file.resizeImage(calcBlurhash: false);
|
||||||
|
@ -13,16 +13,16 @@ import 'settings_emotes.dart';
|
|||||||
class EmotesSettingsView extends StatelessWidget {
|
class EmotesSettingsView extends StatelessWidget {
|
||||||
final EmotesSettingsController controller;
|
final EmotesSettingsController controller;
|
||||||
|
|
||||||
const EmotesSettingsView(this.controller, {Key key}) : super(key: key);
|
const EmotesSettingsView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
final imageKeys = controller.pack.images.keys.toList();
|
final imageKeys = controller.pack!.images.keys.toList();
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
title: Text(L10n.of(context).emoteSettings),
|
title: Text(L10n.of(context)!.emoteSettings),
|
||||||
),
|
),
|
||||||
floatingActionButton: controller.showSave
|
floatingActionButton: controller.showSave
|
||||||
? FloatingActionButton(
|
? FloatingActionButton(
|
||||||
@ -53,7 +53,7 @@ class EmotesSettingsView extends StatelessWidget {
|
|||||||
minLines: 1,
|
minLines: 1,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: L10n.of(context).emoteShortcode,
|
hintText: L10n.of(context)!.emoteShortcode,
|
||||||
prefixText: ': ',
|
prefixText: ': ',
|
||||||
suffixText: ':',
|
suffixText: ':',
|
||||||
prefixStyle: TextStyle(
|
prefixStyle: TextStyle(
|
||||||
@ -84,7 +84,7 @@ class EmotesSettingsView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (controller.room != null)
|
if (controller.room != null)
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
title: Text(L10n.of(context).enableEmotesGlobally),
|
title: Text(L10n.of(context)!.enableEmotesGlobally),
|
||||||
value: controller.isGloballyActive(client),
|
value: controller.isGloballyActive(client),
|
||||||
onChanged: controller.setIsGloballyActive,
|
onChanged: controller.setIsGloballyActive,
|
||||||
),
|
),
|
||||||
@ -100,7 +100,7 @@ class EmotesSettingsView extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).noEmotesFound,
|
L10n.of(context)!.noEmotesFound,
|
||||||
style: const TextStyle(fontSize: 20),
|
style: const TextStyle(fontSize: 20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -114,7 +114,7 @@ class EmotesSettingsView extends StatelessWidget {
|
|||||||
return Container(height: 70);
|
return Container(height: 70);
|
||||||
}
|
}
|
||||||
final imageCode = imageKeys[i];
|
final imageCode = imageKeys[i];
|
||||||
final image = controller.pack.images[imageCode];
|
final image = controller.pack!.images[imageCode]!;
|
||||||
final textEditingController = TextEditingController();
|
final textEditingController = TextEditingController();
|
||||||
textEditingController.text = imageCode;
|
textEditingController.text = imageCode;
|
||||||
final useShortCuts =
|
final useShortCuts =
|
||||||
@ -158,7 +158,7 @@ class EmotesSettingsView extends StatelessWidget {
|
|||||||
minLines: 1,
|
minLines: 1,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: L10n.of(context).emoteShortcode,
|
hintText: L10n.of(context)!.emoteShortcode,
|
||||||
prefixText: ': ',
|
prefixText: ': ',
|
||||||
suffixText: ':',
|
suffixText: ':',
|
||||||
prefixStyle: TextStyle(
|
prefixStyle: TextStyle(
|
||||||
@ -217,7 +217,7 @@ class _EmoteImage extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
const size = 38.0;
|
const size = 38.0;
|
||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
final url = mxc?.getThumbnail(
|
final url = mxc.getThumbnail(
|
||||||
Matrix.of(context).client,
|
Matrix.of(context).client,
|
||||||
width: size * devicePixelRatio,
|
width: size * devicePixelRatio,
|
||||||
height: size * devicePixelRatio,
|
height: size * devicePixelRatio,
|
||||||
@ -233,11 +233,11 @@ class _EmoteImage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ImagePicker extends StatefulWidget {
|
class _ImagePicker extends StatefulWidget {
|
||||||
final ValueNotifier<ImagePackImageContent> controller;
|
final ValueNotifier<ImagePackImageContent?> controller;
|
||||||
|
|
||||||
final void Function(ValueNotifier<ImagePackImageContent>) onPressed;
|
final void Function(ValueNotifier<ImagePackImageContent?>) onPressed;
|
||||||
|
|
||||||
const _ImagePicker({@required this.controller, @required this.onPressed});
|
const _ImagePicker({required this.controller, required this.onPressed});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ImagePickerState createState() => _ImagePickerState();
|
_ImagePickerState createState() => _ImagePickerState();
|
||||||
@ -249,10 +249,10 @@ class _ImagePickerState extends State<_ImagePicker> {
|
|||||||
if (widget.controller.value == null) {
|
if (widget.controller.value == null) {
|
||||||
return ElevatedButton(
|
return ElevatedButton(
|
||||||
onPressed: () => widget.onPressed(widget.controller),
|
onPressed: () => widget.onPressed(widget.controller),
|
||||||
child: Text(L10n.of(context).pickImage),
|
child: Text(L10n.of(context)!.pickImage),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return _EmoteImage(widget.controller.value.url);
|
return _EmoteImage(widget.controller.value!.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ import '../../widgets/matrix.dart';
|
|||||||
import 'settings_ignore_list_view.dart';
|
import 'settings_ignore_list_view.dart';
|
||||||
|
|
||||||
class SettingsIgnoreList extends StatefulWidget {
|
class SettingsIgnoreList extends StatefulWidget {
|
||||||
final String initialUserId;
|
final String? initialUserId;
|
||||||
|
|
||||||
const SettingsIgnoreList({Key key, this.initialUserId}) : super(key: key);
|
const SettingsIgnoreList({Key? key, this.initialUserId}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SettingsIgnoreListController createState() => SettingsIgnoreListController();
|
SettingsIgnoreListController createState() => SettingsIgnoreListController();
|
||||||
@ -21,7 +21,7 @@ class SettingsIgnoreListController extends State<SettingsIgnoreList> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
if (widget.initialUserId != null) {
|
if (widget.initialUserId != null) {
|
||||||
controller.text = widget.initialUserId.replaceAll('@', '');
|
controller.text = widget.initialUserId!.replaceAll('@', '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import 'settings_ignore_list.dart';
|
|||||||
class SettingsIgnoreListView extends StatelessWidget {
|
class SettingsIgnoreListView extends StatelessWidget {
|
||||||
final SettingsIgnoreListController controller;
|
final SettingsIgnoreListController controller;
|
||||||
|
|
||||||
const SettingsIgnoreListView(this.controller, {Key key}) : super(key: key);
|
const SettingsIgnoreListView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -20,7 +20,7 @@ class SettingsIgnoreListView extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
title: Text(L10n.of(context).ignoredUsers),
|
title: Text(L10n.of(context)!.ignoredUsers),
|
||||||
),
|
),
|
||||||
body: MaxWidthBody(
|
body: MaxWidthBody(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -39,9 +39,9 @@ class SettingsIgnoreListView extends StatelessWidget {
|
|||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
hintText: 'bad_guy:domain.abc',
|
hintText: 'bad_guy:domain.abc',
|
||||||
prefixText: '@',
|
prefixText: '@',
|
||||||
labelText: L10n.of(context).ignoreUsername,
|
labelText: L10n.of(context)!.ignoreUsername,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
tooltip: L10n.of(context).ignore,
|
tooltip: L10n.of(context)!.ignore,
|
||||||
icon: const Icon(Icons.done_outlined),
|
icon: const Icon(Icons.done_outlined),
|
||||||
onPressed: () => controller.ignoreUser(context),
|
onPressed: () => controller.ignoreUser(context),
|
||||||
),
|
),
|
||||||
@ -49,7 +49,7 @@ class SettingsIgnoreListView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context).ignoreListDescription,
|
L10n.of(context)!.ignoreListDescription,
|
||||||
style: const TextStyle(color: Colors.orange),
|
style: const TextStyle(color: Colors.orange),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -74,7 +74,7 @@ class SettingsIgnoreListView extends StatelessWidget {
|
|||||||
title: Text(
|
title: Text(
|
||||||
s.data?.displayName ?? client.ignoredUsers[i]),
|
s.data?.displayName ?? client.ignoredUsers[i]),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
tooltip: L10n.of(context).delete,
|
tooltip: L10n.of(context)!.delete,
|
||||||
icon: const Icon(Icons.delete_forever_outlined),
|
icon: const Icon(Icons.delete_forever_outlined),
|
||||||
onPressed: () => showFutureLoadingDialog(
|
onPressed: () => showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -5,7 +5,7 @@ import 'package:vrouter/vrouter.dart';
|
|||||||
import 'settings_multiple_emotes_view.dart';
|
import 'settings_multiple_emotes_view.dart';
|
||||||
|
|
||||||
class MultipleEmotesSettings extends StatefulWidget {
|
class MultipleEmotesSettings extends StatefulWidget {
|
||||||
const MultipleEmotesSettings({Key key}) : super(key: key);
|
const MultipleEmotesSettings({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MultipleEmotesSettingsController createState() =>
|
MultipleEmotesSettingsController createState() =>
|
||||||
@ -13,7 +13,7 @@ class MultipleEmotesSettings extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MultipleEmotesSettingsController extends State<MultipleEmotesSettings> {
|
class MultipleEmotesSettingsController extends State<MultipleEmotesSettings> {
|
||||||
String get roomId => VRouter.of(context).pathParameters['roomid'];
|
String? get roomId => VRouter.of(context).pathParameters['roomid'];
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => MultipleEmotesSettingsView(this);
|
Widget build(BuildContext context) => MultipleEmotesSettingsView(this);
|
||||||
}
|
}
|
||||||
|
@ -10,21 +10,21 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||||||
class MultipleEmotesSettingsView extends StatelessWidget {
|
class MultipleEmotesSettingsView extends StatelessWidget {
|
||||||
final MultipleEmotesSettingsController controller;
|
final MultipleEmotesSettingsController controller;
|
||||||
|
|
||||||
const MultipleEmotesSettingsView(this.controller, {Key key})
|
const MultipleEmotesSettingsView(this.controller, {Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final room = Matrix.of(context).client.getRoomById(controller.roomId);
|
final room = Matrix.of(context).client.getRoomById(controller.roomId!)!;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
title: Text(L10n.of(context).emotePacks),
|
title: Text(L10n.of(context)!.emotePacks),
|
||||||
),
|
),
|
||||||
body: StreamBuilder(
|
body: StreamBuilder(
|
||||||
stream: room.onUpdate.stream,
|
stream: room.onUpdate.stream,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final packs =
|
final Map<String, Event?> packs =
|
||||||
room.states['im.ponies.room_emotes'] ?? <String, Event>{};
|
room.states['im.ponies.room_emotes'] ?? <String, Event>{};
|
||||||
if (!packs.containsKey('')) {
|
if (!packs.containsKey('')) {
|
||||||
packs[''] = null;
|
packs[''] = null;
|
||||||
@ -36,7 +36,8 @@ class MultipleEmotesSettingsView extends StatelessWidget {
|
|||||||
itemCount: keys.length,
|
itemCount: keys.length,
|
||||||
itemBuilder: (BuildContext context, int i) {
|
itemBuilder: (BuildContext context, int i) {
|
||||||
final event = packs[keys[i]];
|
final event = packs[keys[i]];
|
||||||
var packName = keys[i].isNotEmpty ? keys[i] : 'Default Pack';
|
String? packName =
|
||||||
|
keys[i].isNotEmpty ? keys[i] : 'Default Pack';
|
||||||
if (event != null && event.content['pack'] is Map) {
|
if (event != null && event.content['pack'] is Map) {
|
||||||
if (event.content['pack']['displayname'] is String) {
|
if (event.content['pack']['displayname'] is String) {
|
||||||
packName = event.content['pack']['displayname'];
|
packName = event.content['pack']['displayname'];
|
||||||
@ -45,7 +46,7 @@ class MultipleEmotesSettingsView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(packName),
|
title: Text(packName!),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
VRouter.of(context).toSegments(
|
VRouter.of(context).toSegments(
|
||||||
['rooms', room.id, 'details', 'emotes', keys[i]]);
|
['rooms', room.id, 'details', 'emotes', keys[i]]);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
@ -19,38 +20,38 @@ class NotificationSettingsItem {
|
|||||||
NotificationSettingsItem(
|
NotificationSettingsItem(
|
||||||
PushRuleKind.underride,
|
PushRuleKind.underride,
|
||||||
'.m.rule.room_one_to_one',
|
'.m.rule.room_one_to_one',
|
||||||
(c) => L10n.of(c).directChats,
|
(c) => L10n.of(c)!.directChats,
|
||||||
),
|
),
|
||||||
NotificationSettingsItem(
|
NotificationSettingsItem(
|
||||||
PushRuleKind.override,
|
PushRuleKind.override,
|
||||||
'.m.rule.contains_display_name',
|
'.m.rule.contains_display_name',
|
||||||
(c) => L10n.of(c).containsDisplayName,
|
(c) => L10n.of(c)!.containsDisplayName,
|
||||||
),
|
),
|
||||||
NotificationSettingsItem(
|
NotificationSettingsItem(
|
||||||
PushRuleKind.content,
|
PushRuleKind.content,
|
||||||
'.m.rule.contains_user_name',
|
'.m.rule.contains_user_name',
|
||||||
(c) => L10n.of(c).containsUserName,
|
(c) => L10n.of(c)!.containsUserName,
|
||||||
),
|
),
|
||||||
NotificationSettingsItem(
|
NotificationSettingsItem(
|
||||||
PushRuleKind.override,
|
PushRuleKind.override,
|
||||||
'.m.rule.invite_for_me',
|
'.m.rule.invite_for_me',
|
||||||
(c) => L10n.of(c).inviteForMe,
|
(c) => L10n.of(c)!.inviteForMe,
|
||||||
),
|
),
|
||||||
NotificationSettingsItem(
|
NotificationSettingsItem(
|
||||||
PushRuleKind.override,
|
PushRuleKind.override,
|
||||||
'.m.rule.member_event',
|
'.m.rule.member_event',
|
||||||
(c) => L10n.of(c).memberChanges,
|
(c) => L10n.of(c)!.memberChanges,
|
||||||
),
|
),
|
||||||
NotificationSettingsItem(
|
NotificationSettingsItem(
|
||||||
PushRuleKind.override,
|
PushRuleKind.override,
|
||||||
'.m.rule.suppress_notices',
|
'.m.rule.suppress_notices',
|
||||||
(c) => L10n.of(c).botMessages,
|
(c) => L10n.of(c)!.botMessages,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsNotifications extends StatefulWidget {
|
class SettingsNotifications extends StatefulWidget {
|
||||||
const SettingsNotifications({Key key}) : super(key: key);
|
const SettingsNotifications({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SettingsNotificationsController createState() =>
|
SettingsNotificationsController createState() =>
|
||||||
@ -71,31 +72,30 @@ class SettingsNotificationsController extends State<SettingsNotifications> {
|
|||||||
return NotificationSetting.open();
|
return NotificationSetting.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getNotificationSetting(NotificationSettingsItem item) {
|
bool? getNotificationSetting(NotificationSettingsItem item) {
|
||||||
final pushRules = Matrix.of(context).client.globalPushRules;
|
final pushRules = Matrix.of(context).client.globalPushRules;
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case PushRuleKind.content:
|
case PushRuleKind.content:
|
||||||
return pushRules.content
|
return pushRules!.content
|
||||||
?.singleWhere((r) => r.ruleId == item.key, orElse: () => null)
|
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||||
?.enabled;
|
?.enabled;
|
||||||
case PushRuleKind.override:
|
case PushRuleKind.override:
|
||||||
return pushRules.override
|
return pushRules!.override
|
||||||
?.singleWhere((r) => r.ruleId == item.key, orElse: () => null)
|
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||||
?.enabled;
|
?.enabled;
|
||||||
case PushRuleKind.room:
|
case PushRuleKind.room:
|
||||||
return pushRules.room
|
return pushRules!.room
|
||||||
?.singleWhere((r) => r.ruleId == item.key, orElse: () => null)
|
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||||
?.enabled;
|
?.enabled;
|
||||||
case PushRuleKind.sender:
|
case PushRuleKind.sender:
|
||||||
return pushRules.sender
|
return pushRules!.sender
|
||||||
?.singleWhere((r) => r.ruleId == item.key, orElse: () => null)
|
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||||
?.enabled;
|
?.enabled;
|
||||||
case PushRuleKind.underride:
|
case PushRuleKind.underride:
|
||||||
return pushRules.underride
|
return pushRules!.underride
|
||||||
?.singleWhere((r) => r.ruleId == item.key, orElse: () => null)
|
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||||
?.enabled;
|
?.enabled;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setNotificationSetting(NotificationSettingsItem item, bool enabled) {
|
void setNotificationSetting(NotificationSettingsItem item, bool enabled) {
|
||||||
|
@ -15,14 +15,15 @@ import 'settings_notifications.dart';
|
|||||||
class SettingsNotificationsView extends StatelessWidget {
|
class SettingsNotificationsView extends StatelessWidget {
|
||||||
final SettingsNotificationsController controller;
|
final SettingsNotificationsController controller;
|
||||||
|
|
||||||
const SettingsNotificationsView(this.controller, {Key key}) : super(key: key);
|
const SettingsNotificationsView(this.controller, {Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
title: Text(L10n.of(context).notifications),
|
title: Text(L10n.of(context)!.notifications),
|
||||||
),
|
),
|
||||||
body: MaxWidthBody(
|
body: MaxWidthBody(
|
||||||
withScrolling: true,
|
withScrolling: true,
|
||||||
@ -38,7 +39,7 @@ class SettingsNotificationsView extends StatelessWidget {
|
|||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
value: !Matrix.of(context).client.allPushNotificationsMuted,
|
value: !Matrix.of(context).client.allPushNotificationsMuted,
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).notificationsEnabledForThisAccount),
|
L10n.of(context)!.notificationsEnabledForThisAccount),
|
||||||
onChanged: (_) => showFutureLoadingDialog(
|
onChanged: (_) => showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () =>
|
future: () =>
|
||||||
@ -52,7 +53,7 @@ class SettingsNotificationsView extends StatelessWidget {
|
|||||||
if (!Matrix.of(context).client.allPushNotificationsMuted) ...{
|
if (!Matrix.of(context).client.allPushNotificationsMuted) ...{
|
||||||
if (!kIsWeb && Platform.isAndroid)
|
if (!kIsWeb && Platform.isAndroid)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).soundVibrationLedColor),
|
title: Text(L10n.of(context)!.soundVibrationLedColor),
|
||||||
trailing: CircleAvatar(
|
trailing: CircleAvatar(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
@ -64,7 +65,7 @@ class SettingsNotificationsView extends StatelessWidget {
|
|||||||
const Divider(thickness: 1),
|
const Divider(thickness: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).pushRules,
|
L10n.of(context)!.pushRules,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -82,20 +83,20 @@ class SettingsNotificationsView extends StatelessWidget {
|
|||||||
const Divider(thickness: 1),
|
const Divider(thickness: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).devices,
|
L10n.of(context)!.devices,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FutureBuilder<List<Pusher>>(
|
FutureBuilder<List<Pusher>?>(
|
||||||
future: Matrix.of(context).client.getPushers(),
|
future: Matrix.of(context).client.getPushers(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
snapshot.error.toLocalizedString(context),
|
snapshot.error!.toLocalizedString(context),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import '../bootstrap/bootstrap_dialog.dart';
|
|||||||
import 'settings_security_view.dart';
|
import 'settings_security_view.dart';
|
||||||
|
|
||||||
class SettingsSecurity extends StatefulWidget {
|
class SettingsSecurity extends StatefulWidget {
|
||||||
const SettingsSecurity({Key key}) : super(key: key);
|
const SettingsSecurity({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SettingsSecurityController createState() => SettingsSecurityController();
|
SettingsSecurityController createState() => SettingsSecurityController();
|
||||||
@ -23,18 +23,18 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
|||||||
final input = await showTextInputDialog(
|
final input = await showTextInputDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).changePassword,
|
title: L10n.of(context)!.changePassword,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
hintText: L10n.of(context).pleaseEnterYourPassword,
|
hintText: L10n.of(context)!.pleaseEnterYourPassword,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
hintText: L10n.of(context).chooseAStrongPassword,
|
hintText: L10n.of(context)!.chooseAStrongPassword,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@ -50,7 +50,7 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
|||||||
);
|
);
|
||||||
if (success.error == null) {
|
if (success.error == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(L10n.of(context).passwordHasBeenChanged)));
|
SnackBar(content: Text(L10n.of(context)!.passwordHasBeenChanged)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,21 +58,22 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
|||||||
final currentLock =
|
final currentLock =
|
||||||
await const FlutterSecureStorage().read(key: SettingKeys.appLockKey);
|
await const FlutterSecureStorage().read(key: SettingKeys.appLockKey);
|
||||||
if (currentLock?.isNotEmpty ?? false) {
|
if (currentLock?.isNotEmpty ?? false) {
|
||||||
await AppLock.of(context).showLockScreen();
|
await 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.of(context)!.pleaseChooseAPasscode,
|
||||||
message: L10n.of(context).pleaseEnter4Digits,
|
message: L10n.of(context)!.pleaseEnter4Digits,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
cancelLabel: L10n.of(context)!.cancel,
|
||||||
textFields: [
|
textFields: [
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
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.of(context)!.pleaseEnter4Digits;
|
||||||
},
|
},
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
@ -85,9 +86,9 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
|||||||
await const FlutterSecureStorage()
|
await const FlutterSecureStorage()
|
||||||
.write(key: SettingKeys.appLockKey, value: newLock.single);
|
.write(key: SettingKeys.appLockKey, value: newLock.single);
|
||||||
if (newLock.single.isEmpty) {
|
if (newLock.single.isEmpty) {
|
||||||
AppLock.of(context).disable();
|
AppLock.of(context)!.disable();
|
||||||
} else {
|
} else {
|
||||||
AppLock.of(context).enable();
|
AppLock.of(context)!.enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,38 +12,38 @@ import 'settings_security.dart';
|
|||||||
|
|
||||||
class SettingsSecurityView extends StatelessWidget {
|
class SettingsSecurityView extends StatelessWidget {
|
||||||
final SettingsSecurityController controller;
|
final SettingsSecurityController controller;
|
||||||
const SettingsSecurityView(this.controller, {Key key}) : super(key: key);
|
const SettingsSecurityView(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(L10n.of(context).security)),
|
appBar: AppBar(title: Text(L10n.of(context)!.security)),
|
||||||
body: ListTileTheme(
|
body: ListTileTheme(
|
||||||
iconColor: Theme.of(context).textTheme.bodyText1.color,
|
iconColor: Theme.of(context).textTheme.bodyText1!.color,
|
||||||
child: MaxWidthBody(
|
child: MaxWidthBody(
|
||||||
withScrolling: true,
|
withScrolling: true,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.panorama_fish_eye),
|
trailing: const Icon(Icons.panorama_fish_eye),
|
||||||
title: Text(L10n.of(context).whoCanSeeMyStories),
|
title: Text(L10n.of(context)!.whoCanSeeMyStories),
|
||||||
onTap: () => VRouter.of(context).to('stories'),
|
onTap: () => VRouter.of(context).to('stories'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.close),
|
trailing: const Icon(Icons.close),
|
||||||
title: Text(L10n.of(context).ignoredUsers),
|
title: Text(L10n.of(context)!.ignoredUsers),
|
||||||
onTap: () => VRouter.of(context).to('ignorelist'),
|
onTap: () => VRouter.of(context).to('ignorelist'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.vpn_key_outlined),
|
trailing: const Icon(Icons.vpn_key_outlined),
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).changePassword,
|
L10n.of(context)!.changePassword,
|
||||||
),
|
),
|
||||||
onTap: controller.changePasswordAccountAction,
|
onTap: controller.changePasswordAccountAction,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.mail_outlined),
|
trailing: const Icon(Icons.mail_outlined),
|
||||||
title: Text(L10n.of(context).passwordRecovery),
|
title: Text(L10n.of(context)!.passwordRecovery),
|
||||||
onTap: () => VRouter.of(context).to('3pid'),
|
onTap: () => VRouter.of(context).to('3pid'),
|
||||||
),
|
),
|
||||||
if (Matrix.of(context).client.encryption != null) ...{
|
if (Matrix.of(context).client.encryption != null) ...{
|
||||||
@ -51,30 +51,30 @@ class SettingsSecurityView extends StatelessWidget {
|
|||||||
if (PlatformInfos.isMobile)
|
if (PlatformInfos.isMobile)
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.lock_outlined),
|
trailing: const Icon(Icons.lock_outlined),
|
||||||
title: Text(L10n.of(context).appLock),
|
title: Text(L10n.of(context)!.appLock),
|
||||||
onTap: controller.setAppLockAction,
|
onTap: controller.setAppLockAction,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).yourPublicKey),
|
title: Text(L10n.of(context)!.yourPublicKey),
|
||||||
onTap: () => showOkAlertDialog(
|
onTap: () => showOkAlertDialog(
|
||||||
useRootNavigator: false,
|
useRootNavigator: false,
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context).yourPublicKey,
|
title: L10n.of(context)!.yourPublicKey,
|
||||||
message:
|
message:
|
||||||
Matrix.of(context).client.fingerprintKey.beautified,
|
Matrix.of(context).client.fingerprintKey.beautified,
|
||||||
okLabel: L10n.of(context).ok,
|
okLabel: L10n.of(context)!.ok,
|
||||||
),
|
),
|
||||||
trailing: const Icon(Icons.vpn_key_outlined),
|
trailing: const Icon(Icons.vpn_key_outlined),
|
||||||
),
|
),
|
||||||
if (!Matrix.of(context).client.encryption.crossSigning.enabled)
|
if (!Matrix.of(context).client.encryption!.crossSigning.enabled)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).crossSigningEnabled),
|
title: Text(L10n.of(context)!.crossSigningEnabled),
|
||||||
trailing: const Icon(Icons.error, color: Colors.red),
|
trailing: const Icon(Icons.error, color: Colors.red),
|
||||||
onTap: () => controller.showBootstrapDialog(context),
|
onTap: () => controller.showBootstrapDialog(context),
|
||||||
),
|
),
|
||||||
if (!Matrix.of(context).client.encryption.keyManager.enabled)
|
if (!Matrix.of(context).client.encryption!.keyManager.enabled)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).onlineKeyBackupEnabled),
|
title: Text(L10n.of(context)!.onlineKeyBackupEnabled),
|
||||||
trailing: const Icon(Icons.error, color: Colors.red),
|
trailing: const Icon(Icons.error, color: Colors.red),
|
||||||
onTap: () => controller.showBootstrapDialog(context),
|
onTap: () => controller.showBootstrapDialog(context),
|
||||||
),
|
),
|
||||||
@ -88,19 +88,19 @@ class SettingsSecurityView extends StatelessWidget {
|
|||||||
future: () async {
|
future: () async {
|
||||||
return (await Matrix.of(context)
|
return (await Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.encryption
|
.encryption!
|
||||||
.keyManager
|
.keyManager
|
||||||
.isCached()) &&
|
.isCached()) &&
|
||||||
(await Matrix.of(context)
|
(await Matrix.of(context)
|
||||||
.client
|
.client
|
||||||
.encryption
|
.encryption!
|
||||||
.crossSigning
|
.crossSigning
|
||||||
.isCached());
|
.isCached());
|
||||||
}(),
|
}(),
|
||||||
builder: (context, snapshot) => snapshot.data == true
|
builder: (context, snapshot) => snapshot.data == true
|
||||||
? Container()
|
? Container()
|
||||||
: ListTile(
|
: ListTile(
|
||||||
title: Text(L10n.of(context).keysCached),
|
title: Text(L10n.of(context)!.keysCached),
|
||||||
trailing: const Icon(Icons.error, color: Colors.red),
|
trailing: const Icon(Icons.error, color: Colors.red),
|
||||||
onTap: () => controller.showBootstrapDialog(context),
|
onTap: () => controller.showBootstrapDialog(context),
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//@dart=2.12
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:adaptive_theme/adaptive_theme.dart';
|
import 'package:adaptive_theme/adaptive_theme.dart';
|
||||||
@ -19,6 +17,7 @@ class SettingsStyleView extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
controller.currentTheme ??= AdaptiveTheme.of(context).mode;
|
controller.currentTheme ??= AdaptiveTheme.of(context).mode;
|
||||||
const colorPickerSize = 32.0;
|
const colorPickerSize = 32.0;
|
||||||
|
final wallpaper = Matrix.of(context).wallpaper;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
@ -86,10 +85,10 @@ class SettingsStyleView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (Matrix.of(context).wallpaper != null)
|
if (wallpaper != null)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Image.file(
|
title: Image.file(
|
||||||
Matrix.of(context).wallpaper,
|
wallpaper,
|
||||||
height: 38,
|
height: 38,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user