diff --git a/packages/neon/integration_test/screenshot_test.dart b/packages/neon/integration_test/screenshot_test.dart index ee81c5a4..1cebc89f 100644 --- a/packages/neon/integration_test/screenshot_test.dart +++ b/packages/neon/integration_test/screenshot_test.dart @@ -119,7 +119,6 @@ Future pumpAppPage( AppStorage('accounts', sharedPreferences), sharedPreferences, globalOptions, - packageInfo, allAppImplementations, ); if (account != null) { @@ -227,9 +226,10 @@ Future main() async { packageInfo = await PackageInfo.fromPlatform(); account = Account( serverURL: 'http://10.0.2.2', + loginName: 'user1', username: 'user1', password: 'user1', - )..setupClient(packageInfo); + ); await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); }); @@ -462,14 +462,12 @@ Future main() async { }); testWidgets('notifications', (final tester) async { - await (Account( + await Account( serverURL: 'http://10.0.2.2', + loginName: 'admin', username: 'admin', password: 'admin', - )..setupClient(packageInfo)) - .client - .notifications - .sendAdminNotification( + ).client.notifications.sendAdminNotification( userId: account.username, shortMessage: 'Notifications demo', longMessage: 'This is a notifications demo of the Neon app', diff --git a/packages/neon/lib/main.dart b/packages/neon/lib/main.dart index 0032f061..074c5266 100644 --- a/packages/neon/lib/main.dart +++ b/packages/neon/lib/main.dart @@ -46,7 +46,6 @@ Future main() async { AppStorage('accounts', sharedPreferences), sharedPreferences, globalOptions, - packageInfo, allAppImplementations, ); final pushNotificationsBloc = PushNotificationsBloc( diff --git a/packages/neon/lib/src/blocs/accounts.dart b/packages/neon/lib/src/blocs/accounts.dart index 0c9f1556..605c88e6 100644 --- a/packages/neon/lib/src/blocs/accounts.dart +++ b/packages/neon/lib/src/blocs/accounts.dart @@ -7,7 +7,6 @@ import 'package:neon/src/blocs/user_details.dart'; import 'package:neon/src/blocs/user_status.dart'; import 'package:neon/src/models/account.dart'; import 'package:neon/src/neon.dart'; -import 'package:package_info_plus/package_info_plus.dart'; import 'package:rxdart/rxdart.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -30,7 +29,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState this._storage, this._sharedPreferences, this._globalOptions, - this._packageInfo, this._allAppImplementations, ) { accounts.listen((final as) async { @@ -42,7 +40,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState accounts.add( _storage .getStringList(_keyAccounts)! - .map((final a) => (Account.fromJson(json.decode(a) as Map))..setupClient(_packageInfo)) + .map((final a) => Account.fromJson(json.decode(a) as Map)) .toList(), ); } @@ -69,7 +67,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState final SharedPreferences _sharedPreferences; final GlobalOptions _globalOptions; final List _allAppImplementations; - final PackageInfo _packageInfo; final _keyAccounts = 'accounts'; final _keyLastUsedAccount = 'last-used-account'; @@ -102,7 +99,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState @override void addAccount(final Account account) { - account.setupClient(_packageInfo); if (activeAccount.valueOrNull == null) { setActiveAccount(account); } @@ -140,7 +136,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState @override void updateAccount(final Account account) { - account.setupClient(_packageInfo); final as = accounts.value; final index = as.indexWhere((final a) => a.id == account.id); if (index == -1) { diff --git a/packages/neon/lib/src/blocs/push_notifications.dart b/packages/neon/lib/src/blocs/push_notifications.dart index e9ce878a..f459767a 100644 --- a/packages/neon/lib/src/blocs/push_notifications.dart +++ b/packages/neon/lib/src/blocs/push_notifications.dart @@ -134,7 +134,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, Future _registerUnifiedPushInstances(final List accounts) async { // Notifications will only work on accounts with app password - for (final account in accounts.where((final a) => a.appPassword != null)) { + for (final account in accounts.where((final a) => a.password != null)) { await UnifiedPush.registerApp(account.client.id); } } diff --git a/packages/neon/lib/src/models/account.dart b/packages/neon/lib/src/models/account.dart index a28d84f1..2d23034f 100644 --- a/packages/neon/lib/src/models/account.dart +++ b/packages/neon/lib/src/models/account.dart @@ -7,6 +7,29 @@ import 'package:package_info_plus/package_info_plus.dart'; part 'account.g.dart'; +Future getAccount( + final PackageInfo packageInfo, + final String serverURL, + final String loginName, + final String password, +) async { + final username = (await NextcloudClient( + serverURL, + loginName: loginName, + password: password, + ).provisioningApi.getCurrentUser()) + .ocs + .data + .id; + return Account( + serverURL: serverURL, + loginName: loginName, + username: username, + password: password, + userAgent: userAgent(packageInfo), + ); +} + String userAgent(final PackageInfo packageInfo) { var buildNumber = packageInfo.buildNumber; if (buildNumber == '') { @@ -19,30 +42,30 @@ String userAgent(final PackageInfo packageInfo) { class Account { Account({ required this.serverURL, + required this.loginName, required this.username, this.password, - this.appPassword, - }) : assert( - (password != null && appPassword == null) || (password == null && appPassword != null), - 'Either password or appPassword has to be set', - ); + this.userAgent, + }); factory Account.fromJson(final Map json) => _$AccountFromJson(json); Map toJson() => _$AccountToJson(this); final String serverURL; + final String loginName; final String username; final String? password; - final String? appPassword; + final String? userAgent; @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(final Object other) => other is Account && other.serverURL == serverURL && + other.loginName == loginName && other.username == username && other.password == password && - other.appPassword == appPassword; + other.userAgent == userAgent; @override // ignore: avoid_equals_and_hash_code_on_mutable_classes @@ -52,22 +75,13 @@ class Account { NextcloudClient? _client; - void setupClient(final PackageInfo packageInfo) { - _client ??= NextcloudClient( - serverURL, - username: username, - password: appPassword ?? password, - userAgentOverride: userAgent(packageInfo), - ); - } - - NextcloudClient get client { - if (_client == null) { - throw Exception('You need to call setupClient() first'); - } - - return _client!; - } + NextcloudClient get client => _client ??= NextcloudClient( + serverURL, + loginName: loginName, + username: username, + password: password, + userAgentOverride: userAgent, + ); } Map _idCache = {}; diff --git a/packages/neon/lib/src/models/account.g.dart b/packages/neon/lib/src/models/account.g.dart index 9f605a08..fa9b929e 100644 --- a/packages/neon/lib/src/models/account.g.dart +++ b/packages/neon/lib/src/models/account.g.dart @@ -8,14 +8,16 @@ part of 'account.dart'; Account _$AccountFromJson(Map json) => Account( serverURL: json['serverURL'] as String, + loginName: json['loginName'] as String, username: json['username'] as String, password: json['password'] as String?, - appPassword: json['appPassword'] as String?, + userAgent: json['userAgent'] as String?, ); Map _$AccountToJson(Account instance) => { 'serverURL': instance.serverURL, + 'loginName': instance.loginName, 'username': instance.username, 'password': instance.password, - 'appPassword': instance.appPassword, + 'userAgent': instance.userAgent, }; diff --git a/packages/neon/lib/src/pages/login.dart b/packages/neon/lib/src/pages/login.dart index 04e491da..393f6838 100644 --- a/packages/neon/lib/src/pages/login.dart +++ b/packages/neon/lib/src/pages/login.dart @@ -48,31 +48,38 @@ class _LoginPageState extends State { _loginBloc.loginFlowResult.listen((final result) async { if (result != null) { - final account = Account( - serverURL: result.server, - username: result.loginName, - appPassword: result.appPassword, - )..setupClient(await PackageInfo.fromPlatform()); - - if (!mounted) { - return; - } + try { + final account = await getAccount( + _packageInfo, + result.server, + result.loginName, + result.appPassword, + ); + + if (!mounted) { + return; + } - final accountsBloc = Provider.of(context, listen: false); - if (widget.serverURL != null) { - accountsBloc.updateAccount(account); - Navigator.of(context).pop(); - } else { - for (final a in accountsBloc.accounts.value) { - if (a.id == account.id) { - ExceptionWidget.showSnackbar(context, AppLocalizations.of(context).errorAccountAlreadyExists); - await _loginBloc.refresh(); - return; + if (widget.serverURL != null) { + _accountsBloc.updateAccount(account); + Navigator.of(context).pop(); + } else { + for (final a in _accountsBloc.accounts.value) { + if (a.id == account.id) { + ExceptionWidget.showSnackbar(context, AppLocalizations.of(context).errorAccountAlreadyExists); + await _loginBloc.refresh(); + return; + } } + _accountsBloc + ..addAccount(account) + ..setActiveAccount(account); } - accountsBloc - ..addAccount(account) - ..setActiveAccount(account); + } catch (e, s) { + debugPrint(e.toString()); + debugPrint(s.toString()); + ExceptionWidget.showSnackbar(context, e); + await _loginBloc.refresh(); } } }); diff --git a/packages/nextcloud/lib/src/client.dart b/packages/nextcloud/lib/src/client.dart index 4d924364..82786d04 100644 --- a/packages/nextcloud/lib/src/client.dart +++ b/packages/nextcloud/lib/src/client.dart @@ -5,6 +5,7 @@ class NextcloudClient extends Client { // ignore: public_member_api_docs NextcloudClient( super.baseURL, { + this.loginName, this.username, final String? password, final String? language, @@ -19,20 +20,24 @@ class NextcloudClient extends Client { }..removeWhere((final _, final value) => value == null)) .cast(), userAgent: userAgentOverride ?? appType.userAgent, - authentication: username != null && password != null + authentication: loginName != null && password != null ? HttpBasicAuthentication( - username: username, + username: loginName, password: password, ) : null, ); - /// Username used for all operations. Necessary for accessing WebDAV resources + /// Identifier used for authentication. This can be the username or email or something else. + final String? loginName; + + /// Username of the user on the server, it needs to be set for using WebDAV. + /// It can be obtained via the provisioning_api. final String? username; WebDavClient? _webdav; - /// Client for WebDAV. Will be null if no username is set for the client + /// Client for WebDAV. Username needs to be set in order to use it WebDavClient get webdav { if (_webdav != null) { return _webdav!; @@ -43,7 +48,7 @@ class NextcloudClient extends Client { return _webdav = WebDavClient( this, - '/remote.php/dav/files/${(super.authentication! as HttpBasicAuthentication).username}', + '/remote.php/dav/files/$username', ); } } diff --git a/packages/nextcloud/test/helper.dart b/packages/nextcloud/test/helper.dart index 4599ed2b..80b75041 100644 --- a/packages/nextcloud/test/helper.dart +++ b/packages/nextcloud/test/helper.dart @@ -86,6 +86,7 @@ class DockerContainer { class TestNextcloudClient extends NextcloudClient { TestNextcloudClient( super.baseURL, { + super.loginName, super.username, super.password, super.language, @@ -135,6 +136,7 @@ Future getTestClient( final client = TestNextcloudClient( 'http://localhost:${container.port}', + loginName: username, username: username, password: clientPassword, appType: appType,