feat: Implement adding new clients

This commit is contained in:
Christian Pauly 2021-09-18 12:12:34 +02:00
parent 12217d7cd0
commit 9705bf0c01
10 changed files with 93 additions and 30 deletions

View File

@ -1561,6 +1561,8 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"addAccount": "Add account",
"enableMultiAccounts": "Enable multi accounts on this device",
"openInMaps": "Open in maps", "openInMaps": "Open in maps",
"@openInMaps": { "@openInMaps": {
"type": "text", "type": "text",

View File

@ -217,12 +217,12 @@ class AppRoutes {
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
stackedRoutes: [ stackedRoutes: [
VWidget( VWidget(
path: '/login', path: 'login',
widget: Login(), widget: Login(),
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
), ),
VWidget( VWidget(
path: '/signup', path: 'signup',
widget: SignupPage(), widget: SignupPage(),
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
), ),
@ -296,6 +296,23 @@ class AppRoutes {
widget: DevicesSettings(), widget: DevicesSettings(),
buildTransition: _dynamicTransition, buildTransition: _dynamicTransition,
), ),
VWidget(
path: 'add',
widget: HomeserverPicker(),
buildTransition: _fadeTransition,
stackedRoutes: [
VWidget(
path: 'login',
widget: Login(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'signup',
widget: SignupPage(),
buildTransition: _fadeTransition,
),
],
),
], ],
), ),
VWidget( VWidget(

View File

@ -53,7 +53,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
.getItem(HomeserverPickerController.ssoHomeserverKey), .getItem(HomeserverPickerController.ssoHomeserverKey),
); );
} }
await Matrix.of(context).client.login( await Matrix.of(context).createLoginClient().login(
LoginType.mLoginToken, LoginType.mLoginToken,
token: token, token: token,
initialDeviceDisplayName: PlatformInfos.clientName, initialDeviceDisplayName: PlatformInfos.clientName,
@ -216,7 +216,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
} }
} }
void signUpAction() => VRouter.of(context).to('/signup'); void signUpAction() => VRouter.of(context).to('signup');
bool _initialized = false; bool _initialized = false;

View File

@ -64,7 +64,7 @@ class LoginController extends State<Login> {
} else { } else {
identifier = AuthenticationUserIdentifier(user: username); identifier = AuthenticationUserIdentifier(user: username);
} }
await matrix.client.login(LoginType.mLoginPassword, await matrix.createLoginClient().login(LoginType.mLoginPassword,
identifier: identifier, identifier: identifier,
// To stay compatible with older server versions // To stay compatible with older server versions
// ignore: deprecated_member_use // ignore: deprecated_member_use

View File

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
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/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:vrouter/vrouter.dart';
class SettingsAccount extends StatefulWidget { class SettingsAccount extends StatefulWidget {
const SettingsAccount({Key key}) : super(key: key); const SettingsAccount({Key key}) : super(key: key);
@ -144,6 +145,8 @@ class SettingsAccountController extends State<SettingsAccount> {
); );
} }
void addAccountAction() => VRouter.of(context).to('add');
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final client = Matrix.of(context).client; final client = Matrix.of(context).client;

View File

@ -37,7 +37,7 @@ class SignupPageController extends State<SignupPage> {
setState(() => loading = true); setState(() => loading = true);
try { try {
final client = Matrix.of(context).client; final client = Matrix.of(context).createLoginClient();
await client.uiaRequestBackground( await client.uiaRequestBackground(
(auth) => client.register( (auth) => client.register(
username: usernameController.text, username: usernameController.text,

View File

@ -128,7 +128,7 @@ class HomeserverPickerView extends StatelessWidget {
Expanded( Expanded(
child: _LoginButton( child: _LoginButton(
onPressed: () => onPressed: () =>
VRouter.of(context).to('/login'), VRouter.of(context).to('login'),
icon: Icon(Icons.login_outlined), icon: Icon(Icons.login_outlined),
labelText: L10n.of(context).login, labelText: L10n.of(context).login,
), ),

View File

@ -20,6 +20,14 @@ class SettingsAccountView extends StatelessWidget {
withScrolling: true, withScrolling: true,
child: Column( child: Column(
children: [ children: [
if (!Matrix.of(context).isMultiAccount)
ListTile(
trailing: Icon(Icons.add_box_outlined),
title: Text(L10n.of(context).addAccount),
subtitle: Text(L10n.of(context).enableMultiAccounts),
onTap: controller.addAccountAction,
),
Divider(height: 1),
ListTile( ListTile(
trailing: Icon(Icons.edit_outlined), trailing: Icon(Icons.edit_outlined),
title: Text(L10n.of(context).editDisplayname), title: Text(L10n.of(context).editDisplayname),
@ -38,6 +46,7 @@ class SettingsAccountView extends StatelessWidget {
title: Text(L10n.of(context).devices), title: Text(L10n.of(context).devices),
onTap: () => VRouter.of(context).to('devices'), onTap: () => VRouter.of(context).to('devices'),
), ),
Divider(height: 1),
ListTile( ListTile(
trailing: Icon(Icons.exit_to_app_outlined), trailing: Icon(Icons.exit_to_app_outlined),
title: Text(L10n.of(context).logout), title: Text(L10n.of(context).logout),

View File

@ -11,10 +11,9 @@ import 'matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart';
abstract class ClientManager { abstract class ClientManager {
static const String clientNamespace = 'im.fluffychat.store.clients'; static const String clientNamespace = 'im.fluffychat.store.clients';
static Future<List<Client>> getClients() async { static Future<List<Client>> getClients() async {
final store = Store();
final clientNames = <String>{PlatformInfos.clientName}; final clientNames = <String>{PlatformInfos.clientName};
try { try {
final rawClientNames = await store.getItem(clientNamespace); final rawClientNames = await Store().getItem(clientNamespace);
if (rawClientNames != null) { if (rawClientNames != null) {
final clientNamesList = final clientNamesList =
(jsonDecode(rawClientNames) as List).cast<String>(); (jsonDecode(rawClientNames) as List).cast<String>();
@ -22,27 +21,39 @@ abstract class ClientManager {
} }
} catch (e, s) { } catch (e, s) {
Logs().w('Client names in store are corrupted', e, s); Logs().w('Client names in store are corrupted', e, s);
await Store().deleteItem(clientNamespace);
} }
return clientNames return clientNames.map(createClient).toList();
.map((clientName) => Client(
clientName,
enableE2eeRecovery: true,
verificationMethods: {
KeyVerificationMethod.numbers,
if (PlatformInfos.isMobile || PlatformInfos.isLinux)
KeyVerificationMethod.emoji,
},
importantStateEvents: <String>{
'im.ponies.room_emotes', // we want emotes to work properly
},
databaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder,
supportedLoginTypes: {
AuthenticationTypes.password,
if (PlatformInfos.isMobile || PlatformInfos.isWeb)
AuthenticationTypes.sso
},
compute: compute,
))
.toList();
} }
static Future<void> addClientNameToStore(String clientName) async {
final clientNamesList = <String>[];
final rawClientNames = await Store().getItem(clientNamespace);
if (rawClientNames != null) {
final stored = (jsonDecode(rawClientNames) as List).cast<String>();
clientNamesList.addAll(stored);
}
clientNamesList.add(clientName);
await Store().setItem(clientNamespace, jsonEncode(clientNamesList));
}
static Client createClient(String clientName) => Client(
clientName,
enableE2eeRecovery: true,
verificationMethods: {
KeyVerificationMethod.numbers,
if (PlatformInfos.isMobile || PlatformInfos.isLinux)
KeyVerificationMethod.emoji,
},
importantStateEvents: <String>{
'im.ponies.room_emotes', // we want emotes to work properly
},
databaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder,
supportedLoginTypes: {
AuthenticationTypes.password,
if (PlatformInfos.isMobile || PlatformInfos.isWeb)
AuthenticationTypes.sso
},
compute: compute,
);
} }

View File

@ -3,6 +3,7 @@ import 'dart:io';
import 'dart:convert'; import 'dart:convert';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:fluffychat/utils/client_manager.dart';
import 'package:matrix/encryption.dart'; import 'package:matrix/encryption.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions.dart/matrix_locals.dart';
@ -77,6 +78,26 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
return _activeClient; return _activeClient;
} }
Client createLoginClient() {
final multiAccount = Matrix.of(context).client.isLogged();
final client = multiAccount
? ClientManager.createClient(
Matrix.of(context).client.generateUniqueTransactionId())
: Matrix.of(context).client;
if (multiAccount) {
// Add to client list
client.onLoginStateChanged.stream
.where((l) => l == LoginState.loggedIn)
.first
.then((_) {
widget.clients.add(client);
ClientManager.addClientNameToStore(client.clientName);
// TODO: Connect streamsubscriptions
});
}
return client;
}
Map<String, dynamic> get shareContent => _shareContent; Map<String, dynamic> get shareContent => _shareContent;
set shareContent(Map<String, dynamic> content) { set shareContent(Map<String, dynamic> content) {
_shareContent = content; _shareContent = content;