git-touch-android-ios-app/lib/screens/login.dart

376 lines
15 KiB
Dart
Raw Normal View History

2022-09-24 20:46:37 +02:00
import 'package:antd_mobile/antd_mobile.dart';
2019-10-03 06:55:17 +02:00
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
2022-09-17 14:35:45 +02:00
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
2022-10-07 18:55:47 +02:00
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
2019-09-27 14:52:38 +02:00
import 'package:git_touch/models/auth.dart';
2019-10-03 06:55:17 +02:00
import 'package:git_touch/models/theme.dart';
2019-09-25 11:06:36 +02:00
import 'package:git_touch/scaffolds/single.dart';
2019-10-02 09:39:47 +02:00
import 'package:git_touch/utils/utils.dart';
2020-02-01 10:00:30 +01:00
import 'package:git_touch/widgets/action_button.dart';
2019-09-11 13:59:47 +02:00
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/avatar.dart';
import 'package:git_touch/widgets/loading.dart';
2022-09-20 20:00:03 +02:00
import 'package:git_touch/widgets/text_field.dart';
import 'package:provider/provider.dart';
2019-12-13 06:13:45 +01:00
2019-02-07 07:35:19 +01:00
class LoginScreen extends StatefulWidget {
@override
2022-10-03 19:05:29 +02:00
State<LoginScreen> createState() => _LoginScreenState();
2019-02-07 07:35:19 +01:00
}
class _LoginScreenState extends State<LoginScreen> {
2020-02-01 10:00:30 +01:00
final _tokenController = TextEditingController();
final _domainController = TextEditingController();
2020-02-02 07:08:58 +01:00
// For Bitbucket
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
2019-09-26 16:14:14 +02:00
Widget _buildAccountItem(int index) {
2019-11-08 11:29:08 +01:00
final auth = Provider.of<AuthModel>(context);
2021-05-16 09:16:35 +02:00
final account = auth.accounts![index];
2022-10-07 10:34:55 +02:00
return Dismissible(
key: ValueKey(index),
direction: DismissDirection.endToStart,
background: Container(
color: AntTheme.of(context).colorDanger,
padding: const EdgeInsets.only(right: 12),
child: Align(
alignment: Alignment.centerRight,
child: Text(
AppLocalizations.of(context)!.removeAccount,
style: TextStyle(
fontSize: 16,
color: AntTheme.of(context).colorBackground,
),
2020-02-01 11:30:32 +01:00
),
2022-10-07 10:34:55 +02:00
),
),
onDismissed: (_) {
auth.removeAccount(index);
2020-02-01 11:30:32 +01:00
},
2022-10-03 07:27:11 +02:00
child: AntListItem(
2022-10-07 09:24:56 +02:00
onClick: () {
auth.setActiveAccountAndReload(index);
},
arrow: null,
prefix: Avatar(url: account.avatarUrl),
extra: index == auth.activeAccountIndex
? const Icon(Ionicons.checkmark)
: null,
description: Text(account.domain),
child: Text(account.login),
2019-02-21 14:21:16 +01:00
),
);
}
2022-10-07 09:24:56 +02:00
Widget _buildAddItem({
IconData? brand,
required String text,
void Function()? onTap,
}) {
return AntListItem(
onClick: onTap,
child: Row(
children: <Widget>[
const Icon(Ionicons.add),
const SizedBox(width: 4),
Icon(brand),
const SizedBox(width: 8),
Text(text, style: const TextStyle(fontSize: 15)),
],
),
);
}
2020-02-01 10:00:30 +01:00
Widget _buildPopup(
BuildContext context, {
2021-05-16 09:16:35 +02:00
List<Widget>? notes,
2020-02-01 10:00:30 +01:00
bool showDomain = false,
}) {
return Column(
children: <Widget>[
if (showDomain)
2020-02-02 07:08:58 +01:00
MyTextField(controller: _domainController, placeholder: 'Domain'),
2022-09-06 18:28:12 +02:00
const SizedBox(height: 8),
2020-02-02 07:08:58 +01:00
MyTextField(placeholder: 'Access token', controller: _tokenController),
2022-09-06 18:28:12 +02:00
const SizedBox(height: 8),
2020-02-01 10:00:30 +01:00
if (notes != null) ...notes,
],
);
}
void showError(err) {
context.read<ThemeModel>().showConfirm(context,
2022-09-06 18:28:12 +02:00
Text('${AppLocalizations.of(context)!.somethingBadHappens}$err'));
2020-02-01 10:00:30 +01:00
}
2019-02-07 07:35:19 +01:00
@override
Widget build(BuildContext context) {
2019-10-03 06:55:17 +02:00
final auth = Provider.of<AuthModel>(context);
2019-11-08 11:29:08 +01:00
final theme = Provider.of<ThemeModel>(context);
2019-09-25 11:06:36 +02:00
return SingleScaffold(
2021-05-16 09:16:35 +02:00
title: AppBarTitle(AppLocalizations.of(context)!.selectAccount),
2019-10-03 06:55:17 +02:00
body: auth.loading
2022-09-06 18:28:12 +02:00
? const Center(child: Loading())
2022-10-03 07:27:11 +02:00
: Column(
children: [
AntList(
children: [
...List.generate(auth.accounts!.length, _buildAccountItem),
_buildAddItem(
text: AppLocalizations.of(context)!.githubAccount,
brand: Ionicons.logo_github,
onTap: () async {
theme.showActions(context, [
ActionItem(
text: 'via OAuth',
onTap: (_) {
auth.redirectToGithubOauth();
},
),
ActionItem(
text: 'via OAuth (Public repos only)',
onTap: (_) {
auth.redirectToGithubOauth(true);
},
),
ActionItem(
text: 'via Personal token',
onTap: (_) async {
final result = await theme.showConfirm(
context,
_buildPopup(context, notes: [
Text(
AppLocalizations.of(context)!
.permissionRequiredMessage,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400),
),
const SizedBox(height: 8),
Text(
'user, repo, read:org, notifications',
style: TextStyle(
fontSize: 16,
color:
AntTheme.of(context).colorPrimary),
)
]),
);
if (result == true) {
try {
await auth
.loginWithToken(_tokenController.text);
_tokenController.clear();
} catch (err) {
showError(err);
}
2020-02-01 10:00:30 +01:00
}
2022-10-03 07:27:11 +02:00
},
),
]);
},
),
_buildAddItem(
text: AppLocalizations.of(context)!.gitlabAccount,
brand: Ionicons.git_branch_outline,
onTap: () async {
_domainController.text = 'https://gitlab.com';
final result = await theme.showConfirm(
2020-02-01 10:00:30 +01:00
context,
2022-10-03 07:27:11 +02:00
_buildPopup(
context,
showDomain: true,
notes: [
Text(
AppLocalizations.of(context)!
.permissionRequiredMessage,
style: const TextStyle(
fontSize: 14, fontWeight: FontWeight.w400),
),
const SizedBox(height: 8),
Text(
'api, read_user, read_repository',
style: TextStyle(
fontSize: 16,
color: AntTheme.of(context).colorPrimary),
)
],
),
);
if (result == true) {
try {
await auth.loginToGitlab(
_domainController.text, _tokenController.text);
_tokenController.clear();
} catch (err) {
showError(err);
}
2020-02-02 07:08:58 +01:00
}
2022-10-03 07:27:11 +02:00
},
),
_buildAddItem(
text: AppLocalizations.of(context)!.bitbucketAccount,
brand: Ionicons.logo_bitbucket,
onTap: () async {
_domainController.text = 'https://bitbucket.org';
final result = await theme.showConfirm(
context,
Column(
2022-10-03 07:27:11 +02:00
children: <Widget>[
MyTextField(
controller: _domainController,
placeholder: 'Domain'),
const SizedBox(height: 8),
MyTextField(
placeholder: 'Username',
controller: _usernameController),
const SizedBox(height: 8),
MyTextField(
placeholder: 'App password',
controller: _passwordController),
const SizedBox(height: 8),
Text.rich(
TextSpan(children: [
const TextSpan(
text:
'Note: App password is different with the password. Follow ',
),
TextSpan(
text: 'this guide',
style: TextStyle(
color:
AntTheme.of(context).colorPrimary),
recognizer: TapGestureRecognizer()
..onTap = () {
context.pushUrl(
'https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/');
},
),
const TextSpan(text: ' to create one.')
]),
),
2022-09-06 18:28:12 +02:00
const SizedBox(height: 8),
Text(
2022-10-03 07:27:11 +02:00
AppLocalizations.of(context)!
.permissionRequiredMessage,
style: const TextStyle(
fontSize: 14, fontWeight: FontWeight.w400),
),
const SizedBox(height: 8),
Text(
'Account: read\nTeam membership: read\nProjects: read\nRepositories: read\nPull requests: read\nIssues: read\nSnippets: read',
style: TextStyle(
2022-09-24 20:46:37 +02:00
fontSize: 16,
color: AntTheme.of(context).colorPrimary),
2022-10-03 07:27:11 +02:00
)
],
2022-10-03 07:27:11 +02:00
),
);
if (result == true) {
try {
await auth.loginToBb(
_domainController.text,
_usernameController.text,
_passwordController.text);
} catch (err) {
showError(err);
}
2020-02-01 10:00:30 +01:00
}
2022-10-03 07:27:11 +02:00
},
),
_buildAddItem(
text: AppLocalizations.of(context)!.giteaAccount,
brand: Ionicons.git_branch_outline, // TODO: brand icon
onTap: () async {
_domainController.text = 'https://gitea.com';
final result = await theme.showConfirm(
context,
Column(
children: [
_buildPopup(context, showDomain: true),
const Text.rich(TextSpan(children: [
TextSpan(
text:
'Note: To login with Codeberg change the domain name to: ',
),
])),
const SizedBox(height: 8),
Text(
'https://codeberg.org',
style: TextStyle(
fontSize: 16,
color: AntTheme.of(context).colorPrimary),
),
],
));
if (result == true) {
try {
await auth.loginToGitea(
_domainController.text, _tokenController.text);
_tokenController.clear();
} catch (err) {
showError(err);
}
2020-10-13 18:43:11 +02:00
}
2022-10-03 07:27:11 +02:00
},
),
_buildAddItem(
text: '${AppLocalizations.of(context)!.giteeAccount}(码云)',
brand: Ionicons.git_branch_outline, // TODO: brand icon
onTap: () async {
final result = await theme.showConfirm(
context,
_buildPopup(context),
);
if (result == true) {
try {
await auth.loginToGitee(_tokenController.text);
_tokenController.clear();
} catch (err) {
showError(err);
}
2021-01-23 15:08:05 +01:00
}
2022-10-03 07:27:11 +02:00
},
2020-02-01 11:30:32 +01:00
),
2022-10-03 07:27:11 +02:00
_buildAddItem(
text: 'Gogs Account',
brand: Ionicons.git_branch_outline, // TODO: brand icon
onTap: () async {
_domainController.text = 'https://gogs.com';
final result = await theme.showConfirm(
context,
_buildPopup(context, showDomain: true),
);
if (result == true) {
try {
await auth.loginToGogs(
_domainController.text, _tokenController.text);
_tokenController.clear();
} catch (err) {
showError(err);
}
}
},
),
],
),
Container(
padding: CommonStyle.padding,
child: Text(
2022-10-07 10:34:55 +02:00
'Swipe to left to remove account',
2022-10-03 07:27:11 +02:00
style: TextStyle(
fontSize: 16,
color: AntTheme.of(context).colorTextSecondary,
),
),
)
],
2019-09-25 08:24:20 +02:00
),
2019-02-07 07:35:19 +01:00
);
}
}