diff --git a/lib/models/account.dart b/lib/models/account.dart index 3ac59e2..1c7b9f8 100644 --- a/lib/models/account.dart +++ b/lib/models/account.dart @@ -10,19 +10,20 @@ class Account { String token; String login; String avatarUrl; - int gitlabId; + int gitlabId; // For GitLab + String appPassword; // For Bitbucket - equals(Account a) { - final uri = Uri.parse(domain); - final uriA = Uri.parse(a.domain); + // equals(Account a) { + // final uri = Uri.parse(domain); + // final uriA = Uri.parse(a.domain); - // Treat domains as the same if they have the same hosts and ports - return a.platform == platform && - a.login == login && - a.gitlabId == gitlabId && - uri.host == uriA.host && - uri.port == uriA.port; - } + // // Treat domains as the same if they have the same hosts and ports + // return a.platform == platform && + // a.login == login && + // a.gitlabId == gitlabId && + // uri.host == uriA.host && + // uri.port == uriA.port; + // } Account({ @required this.platform, @@ -31,6 +32,7 @@ class Account { @required this.login, @required this.avatarUrl, this.gitlabId, + this.appPassword, }); factory Account.fromJson(Map json) => diff --git a/lib/models/account.g.dart b/lib/models/account.g.dart index 33ad935..5afdd11 100644 --- a/lib/models/account.g.dart +++ b/lib/models/account.g.dart @@ -14,6 +14,7 @@ Account _$AccountFromJson(Map json) { login: json['login'] as String, avatarUrl: json['avatarUrl'] as String, gitlabId: json['gitlabId'] as int, + appPassword: json['appPassword'] as String, ); } @@ -32,5 +33,6 @@ Map _$AccountToJson(Account instance) { writeNotNull('login', instance.login); writeNotNull('avatarUrl', instance.avatarUrl); writeNotNull('gitlabId', instance.gitlabId); + writeNotNull('appPassword', instance.appPassword); return val; } diff --git a/lib/models/auth.dart b/lib/models/auth.dart index 692917b..90bead2 100644 --- a/lib/models/auth.dart +++ b/lib/models/auth.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'dart:convert'; import 'dart:async'; +import 'package:git_touch/models/bitbucket.dart'; import 'package:git_touch/models/gitea.dart'; import 'package:git_touch/utils/request_serilizer.dart'; import 'package:gql_http_link/gql_http_link.dart'; @@ -22,6 +23,7 @@ const clientId = 'df930d7d2e219f26142a'; class PlatformType { static const github = 'github'; static const gitlab = 'gitlab'; + static const bitbucket = 'bitbucket'; static const gitea = 'gitea'; } @@ -214,6 +216,32 @@ class AuthModel with ChangeNotifier { ); } + Future loginToBb(String domain, String username, String appPassword) async { + try { + loading = true; + notifyListeners(); + final uri = Uri.parse('$domain/api/2.0/user') + .replace(userInfo: '$username:$appPassword'); + final res = await http.get(uri); + if (res.statusCode >= 400) { + throw 'status ${res.statusCode}'; + } + final info = json.decode(res.body); + final user = BbUser.fromJson(info); + await _addAccount(Account( + platform: PlatformType.bitbucket, + domain: domain, + token: user.username, + login: username, + avatarUrl: null, + appPassword: appPassword, + )); + } finally { + loading = false; + notifyListeners(); + } + } + Future init() async { // Listen scheme _sub = getUriLinksStream().listen(_onSchemeDetected, onError: (err) { diff --git a/lib/models/bitbucket.dart b/lib/models/bitbucket.dart new file mode 100644 index 0000000..b2e1189 --- /dev/null +++ b/lib/models/bitbucket.dart @@ -0,0 +1,12 @@ +import 'package:json_annotation/json_annotation.dart'; +part 'bitbucket.g.dart'; + +@JsonSerializable(fieldRename: FieldRename.snake) +class BbUser { + String username; + String displayName; + bool isStaff; + DateTime createdOn; + BbUser(); + factory BbUser.fromJson(Map json) => _$BbUserFromJson(json); +} diff --git a/lib/models/bitbucket.g.dart b/lib/models/bitbucket.g.dart new file mode 100644 index 0000000..100185a --- /dev/null +++ b/lib/models/bitbucket.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'bitbucket.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BbUser _$BbUserFromJson(Map json) { + return BbUser() + ..username = json['username'] as String + ..displayName = json['display_name'] as String + ..isStaff = json['is_staff'] as bool + ..createdOn = json['created_on'] == null + ? null + : DateTime.parse(json['created_on'] as String); +} + +Map _$BbUserToJson(BbUser instance) => { + 'username': instance.username, + 'display_name': instance.displayName, + 'is_staff': instance.isStaff, + 'created_on': instance.createdOn?.toIso8601String(), + }; diff --git a/lib/screens/login.dart b/lib/screens/login.dart index 2ee2190..5231499 100644 --- a/lib/screens/login.dart +++ b/lib/screens/login.dart @@ -22,6 +22,10 @@ class _LoginScreenState extends State { final _tokenController = TextEditingController(); final _domainController = TextEditingController(); + // For Bitbucket + final _usernameController = TextEditingController(); + final _passwordController = TextEditingController(); + // @override // initState() { // super.initState(); @@ -114,15 +118,9 @@ class _LoginScreenState extends State { return Column( children: [ if (showDomain) - MyTextField( - controller: _domainController, - placeholder: 'Domain', - ), + MyTextField(controller: _domainController, placeholder: 'Domain'), SizedBox(height: 8), - MyTextField( - placeholder: 'Access token', - controller: _tokenController, - ), + MyTextField(placeholder: 'Access token', controller: _tokenController), SizedBox(height: 8), if (notes != null) ...notes, ], @@ -228,6 +226,43 @@ class _LoginScreenState extends State { } }, ), + _buildAddItem( + text: 'Bitbucket Account', + brand: FontAwesome5Brands.bitbucket, + onTap: () async { + _domainController.text = 'https://bitbucket.org'; + final result = await theme.showConfirm( + context, + Column( + children: [ + MyTextField( + controller: _domainController, + placeholder: 'Domain'), + SizedBox(height: 8), + MyTextField( + placeholder: 'Username', + controller: _usernameController), + SizedBox(height: 8), + MyTextField( + placeholder: 'App password', + controller: _passwordController), + SizedBox(height: 8), + // TODO: permissions + ], + ), + ); + if (result == true) { + try { + await auth.loginToBb( + _domainController.text, + _usernameController.text, + _passwordController.text); + } catch (err) { + showError(err); + } + } + }, + ), _buildAddItem( text: 'Gitea Account', brand: Octicons.git_branch, // TODO: brand icon