mirror of
https://github.com/git-touch/git-touch
synced 2025-03-05 19:57:42 +01:00
refactor: accounts login
This commit is contained in:
parent
0d5fef8dd7
commit
2de9884756
@ -107,7 +107,7 @@ class _HomeState extends State<Home> {
|
||||
return SearchScreen();
|
||||
case 4:
|
||||
return UserScreen(
|
||||
Provider.of<SettingsModel>(context).activeLogin,
|
||||
Provider.of<SettingsModel>(context).activeAccount.login,
|
||||
isMe: true,
|
||||
);
|
||||
}
|
||||
@ -131,7 +131,7 @@ class _HomeState extends State<Home> {
|
||||
}
|
||||
|
||||
// print(settings.activeLogin);
|
||||
if (settings.activeLogin == null) {
|
||||
if (settings.activeAccount == null) {
|
||||
return MaterialApp(theme: themData, home: LoginScreen());
|
||||
}
|
||||
|
||||
|
@ -5,22 +5,21 @@ part 'account.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class AccountModel {
|
||||
String avatarUrl;
|
||||
String token;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
String platform;
|
||||
@JsonKey(ignore: true)
|
||||
String domain;
|
||||
@JsonKey(ignore: true)
|
||||
String token;
|
||||
String login;
|
||||
String avatarUrl;
|
||||
|
||||
equals(AccountModel a) =>
|
||||
a.platform == platform && a.domain == domain && a.login == login;
|
||||
|
||||
AccountModel({
|
||||
@required this.avatarUrl,
|
||||
@required this.platform,
|
||||
@required this.domain,
|
||||
@required this.token,
|
||||
this.platform,
|
||||
this.domain,
|
||||
this.login,
|
||||
@required this.login,
|
||||
@required this.avatarUrl,
|
||||
});
|
||||
|
||||
factory AccountModel.fromJson(Map<String, dynamic> json) =>
|
||||
|
@ -8,8 +8,19 @@ part of 'account.dart';
|
||||
|
||||
AccountModel _$AccountModelFromJson(Map<String, dynamic> json) {
|
||||
return AccountModel(
|
||||
avatarUrl: json['avatarUrl'] as String, token: json['token'] as String);
|
||||
platform: json['platform'] as String,
|
||||
domain: json['domain'] as String,
|
||||
token: json['token'] as String,
|
||||
login: json['login'] as String,
|
||||
avatarUrl: json['avatarUrl'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$AccountModelToJson(AccountModel instance) =>
|
||||
<String, dynamic>{'avatarUrl': instance.avatarUrl, 'token': instance.token};
|
||||
<String, dynamic>{
|
||||
'platform': instance.platform,
|
||||
'domain': instance.domain,
|
||||
'token': instance.token,
|
||||
'login': instance.login,
|
||||
'avatarUrl': instance.avatarUrl,
|
||||
};
|
||||
|
@ -38,33 +38,22 @@ class PlatformType {
|
||||
// }
|
||||
|
||||
class SettingsModel with ChangeNotifier {
|
||||
bool ready = false;
|
||||
|
||||
Map<String, AccountModel> githubAccountMap;
|
||||
Map<String, Map<String, Map<String, AccountModel>>> accountMap;
|
||||
|
||||
String activePlatform;
|
||||
String activeDomain;
|
||||
String activeLogin;
|
||||
static const _apiPrefix = 'https://api.github.com';
|
||||
|
||||
List<AccountModel> _accounts;
|
||||
int activeAccountIndex;
|
||||
StreamSubscription<Uri> _sub;
|
||||
bool loading = false;
|
||||
|
||||
// PlatformType platformType;
|
||||
|
||||
String _apiPrefix = 'https://api.github.com';
|
||||
|
||||
String get token {
|
||||
if (activeLogin == null) return null;
|
||||
|
||||
switch (activePlatform) {
|
||||
case PlatformType.github:
|
||||
return githubAccountMap[activeLogin].token;
|
||||
default:
|
||||
return accountMap[activePlatform][activeDomain][activeLogin].token;
|
||||
}
|
||||
List<AccountModel> get accounts => _accounts;
|
||||
bool get ready => _accounts != null;
|
||||
AccountModel get activeAccount {
|
||||
if (activeAccountIndex == null || _accounts == null) return null;
|
||||
return _accounts[activeAccountIndex];
|
||||
}
|
||||
|
||||
String get token => activeAccount.token;
|
||||
|
||||
// https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/#web-application-flow
|
||||
Future<void> _onSchemeDetected(Uri uri) async {
|
||||
await closeWebView();
|
||||
@ -72,10 +61,8 @@ class SettingsModel with ChangeNotifier {
|
||||
loading = true;
|
||||
notifyListeners();
|
||||
|
||||
// get token by code
|
||||
var code = uri.queryParameters['code'];
|
||||
// print(code);
|
||||
var res = await http.post(
|
||||
// Get token by code
|
||||
final res = await http.post(
|
||||
'https://github.com/login/oauth/access_token',
|
||||
headers: {
|
||||
HttpHeaders.acceptHeader: 'application/json',
|
||||
@ -84,18 +71,17 @@ class SettingsModel with ChangeNotifier {
|
||||
body: json.encode({
|
||||
'client_id': clientId,
|
||||
'client_secret': clientSecret,
|
||||
'code': code,
|
||||
'code': uri.queryParameters['code'],
|
||||
'state': _oauthState,
|
||||
}),
|
||||
);
|
||||
// print(res.body);
|
||||
var data = json.decode(res.body);
|
||||
_loginWithToken(data['access_token']);
|
||||
final token = json.decode(res.body)['access_token'] as String;
|
||||
await _loginWithToken(token);
|
||||
}
|
||||
|
||||
Future<void> _loginWithToken(String token) async {
|
||||
// get login and avatar url
|
||||
var queryData = await query('''
|
||||
// Get login and avatar url
|
||||
final queryData = await query('''
|
||||
{
|
||||
viewer {
|
||||
login
|
||||
@ -103,56 +89,53 @@ class SettingsModel with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
''', token);
|
||||
String login = queryData['viewer']['login'];
|
||||
String avatarUrl = queryData['viewer']['avatarUrl'];
|
||||
|
||||
githubAccountMap[login] = AccountModel(avatarUrl: avatarUrl, token: token);
|
||||
final account = AccountModel(
|
||||
platform: PlatformType.github,
|
||||
domain: 'github.com',
|
||||
token: token,
|
||||
login: queryData['viewer']['login'] as String,
|
||||
avatarUrl: queryData['viewer']['avatarUrl'] as String,
|
||||
);
|
||||
|
||||
// write
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(
|
||||
StorageKeys.github,
|
||||
json.encode(githubAccountMap
|
||||
.map((login, account) => MapEntry(login, account.toJson()))));
|
||||
// TODO: duplicated
|
||||
// if (_accounts.where(account.equals).isNotEmpty) {}
|
||||
|
||||
_accounts.add(account);
|
||||
|
||||
// Write to perfs
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(StorageKeys.accounts, json.encode(_accounts));
|
||||
|
||||
loading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loginToGitlab(String domain, String token) async {
|
||||
loading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
var res = await http
|
||||
loading = true;
|
||||
notifyListeners();
|
||||
|
||||
final res = await http
|
||||
.get('$domain/api/v4/user', headers: {'Private-Token': token});
|
||||
var info = json.decode(res.body);
|
||||
final info = json.decode(res.body);
|
||||
|
||||
if (info['message'] != null) {
|
||||
throw info['message'];
|
||||
}
|
||||
|
||||
String login = info['username'];
|
||||
String avatarUrl = info['avatar_url'];
|
||||
final account = AccountModel(
|
||||
platform: PlatformType.gitlab,
|
||||
domain: domain,
|
||||
token: token,
|
||||
login: info['username'] as String,
|
||||
avatarUrl: info['avatar_url'] as String,
|
||||
);
|
||||
|
||||
if (accountMap[PlatformType.gitlab] == null)
|
||||
accountMap[PlatformType.gitlab] = {};
|
||||
if (accountMap[PlatformType.gitlab][domain] == null)
|
||||
accountMap[PlatformType.gitlab][domain] = {};
|
||||
_accounts.add(account);
|
||||
|
||||
accountMap[PlatformType.gitlab][domain][login] =
|
||||
AccountModel(token: token, avatarUrl: avatarUrl);
|
||||
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
String str = json.encode(accountMap.map((type, v0) {
|
||||
return MapEntry(type, v0.map((domain, v1) {
|
||||
return MapEntry(domain, v1.map((login, v2) {
|
||||
return MapEntry(login, v2.toJson());
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
await prefs.setString(StorageKeys.account, str);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(StorageKeys.accounts, json.encode(_accounts));
|
||||
} catch (err) {
|
||||
print(err);
|
||||
// TODO: show errors
|
||||
@ -163,65 +146,30 @@ class SettingsModel with ChangeNotifier {
|
||||
}
|
||||
|
||||
void init() async {
|
||||
// Listen scheme
|
||||
_sub = getUriLinksStream().listen(_onSchemeDetected, onError: (err) {
|
||||
print(err);
|
||||
});
|
||||
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
// read GitHub accounts
|
||||
// Read accounts
|
||||
try {
|
||||
String str = prefs.getString(StorageKeys.github);
|
||||
print('read github: $str');
|
||||
|
||||
Map<String, dynamic> data = json.decode(str ?? '{}');
|
||||
githubAccountMap = data.map<String, AccountModel>((login, _accountMap) =>
|
||||
MapEntry(login, AccountModel.fromJson(_accountMap)));
|
||||
String str = prefs.getString(StorageKeys.accounts);
|
||||
print('read accounts: $str');
|
||||
_accounts = (json.decode(str ?? '[]') as List)
|
||||
.map((item) => AccountModel.fromJson(item))
|
||||
.toList();
|
||||
} catch (err) {
|
||||
print(err);
|
||||
githubAccountMap = {};
|
||||
_accounts = [];
|
||||
}
|
||||
|
||||
// read accounts
|
||||
try {
|
||||
String str = prefs.getString(StorageKeys.account);
|
||||
print('read account: $str');
|
||||
|
||||
var data = Map<String, Map<String, dynamic>>.from(
|
||||
Map<String, dynamic>.from(json.decode(str ?? '{}')));
|
||||
|
||||
accountMap = {};
|
||||
data.forEach((platform, v0) {
|
||||
accountMap[platform] = {};
|
||||
v0.forEach((domain, v1) {
|
||||
accountMap[platform][domain] = {};
|
||||
v1.forEach((login, v2) {
|
||||
accountMap[platform][domain][login] = AccountModel.fromJson(v2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: type cast
|
||||
// accountMap = data.map((type, v0) {
|
||||
// return MapEntry(type, v0.map((domain, v1) {
|
||||
// return MapEntry(domain, v1.map((login, v2) {
|
||||
// return MapEntry(login, AccountModel.fromJson(v2));
|
||||
// }));
|
||||
// }));
|
||||
// });
|
||||
} catch (err) {
|
||||
print(err);
|
||||
accountMap = {};
|
||||
}
|
||||
|
||||
ready = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setActiveAccount(String platform, String domain, String login) {
|
||||
activePlatform = platform;
|
||||
activeDomain = domain;
|
||||
activeLogin = login;
|
||||
void setActiveAccountIndex(int index) {
|
||||
activeAccountIndex = index;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@ -237,7 +185,7 @@ class SettingsModel with ChangeNotifier {
|
||||
_token = token;
|
||||
}
|
||||
if (_token == null) {
|
||||
throw Exception('token is null');
|
||||
throw 'token is null';
|
||||
}
|
||||
|
||||
final res = await http
|
||||
@ -253,7 +201,7 @@ class SettingsModel with ChangeNotifier {
|
||||
final data = json.decode(res.body);
|
||||
|
||||
if (data['errors'] != null) {
|
||||
throw Exception(data['errors'][0]['message']);
|
||||
throw data['errors'][0]['message'];
|
||||
}
|
||||
|
||||
return data['data'];
|
||||
|
@ -5,7 +5,6 @@ import 'package:git_touch/widgets/app_bar_title.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../widgets/link.dart';
|
||||
import '../widgets/loading.dart';
|
||||
import '../models/account.dart';
|
||||
import '../widgets/avatar.dart';
|
||||
// import 'login_gitlab.dart';
|
||||
|
||||
@ -15,39 +14,39 @@ class LoginScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
Widget _buildAccountItem(AccountModel account) {
|
||||
var settings = Provider.of<SettingsModel>(context);
|
||||
Widget _buildAccountItem(int index) {
|
||||
final settings = Provider.of<SettingsModel>(context);
|
||||
final account = settings.accounts[index];
|
||||
|
||||
return Link(
|
||||
onTap: () {
|
||||
// Navigator.of(context).pop();
|
||||
settings.setActiveAccount(
|
||||
account.platform, account.domain, account.login);
|
||||
settings.setActiveAccountIndex(index);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: Colors.black12)),
|
||||
),
|
||||
child: Row(children: <Widget>[
|
||||
Avatar(url: account.avatarUrl, size: 24),
|
||||
Padding(padding: EdgeInsets.only(left: 10)),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(account.login, style: TextStyle(fontSize: 20)),
|
||||
Padding(padding: EdgeInsets.only(top: 6)),
|
||||
Text(account.domain)
|
||||
],
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Avatar(url: account.avatarUrl, size: 24),
|
||||
Padding(padding: EdgeInsets.only(left: 10)),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(account.login, style: TextStyle(fontSize: 20)),
|
||||
Padding(padding: EdgeInsets.only(top: 6)),
|
||||
Text(account.domain)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
(settings.activePlatform == account.platform &&
|
||||
settings.activeDomain == account.domain &&
|
||||
settings.activeLogin == account.login)
|
||||
? Icon(Icons.check)
|
||||
: Container(),
|
||||
]),
|
||||
(index == settings.activeAccountIndex)
|
||||
? Icon(Icons.check)
|
||||
: Container(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -73,22 +72,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var settings = Provider.of<SettingsModel>(context);
|
||||
|
||||
List<AccountModel> accounts = [];
|
||||
settings.accountMap.forEach((platform, v0) {
|
||||
v0.forEach((domain, v1) {
|
||||
v1.forEach((login, v2) {
|
||||
accounts.add(AccountModel(
|
||||
avatarUrl: v2.avatarUrl,
|
||||
token: v2.token,
|
||||
platform: platform,
|
||||
domain: domain,
|
||||
login: login,
|
||||
));
|
||||
});
|
||||
});
|
||||
});
|
||||
final settings = Provider.of<SettingsModel>(context);
|
||||
|
||||
return SingleScaffold(
|
||||
title: AppBarTitle('Select account'),
|
||||
@ -97,15 +81,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
: Container(
|
||||
child: Column(
|
||||
children: [
|
||||
...settings.githubAccountMap.entries
|
||||
.map<Widget>((entry) => _buildAccountItem(AccountModel(
|
||||
avatarUrl: entry.value.avatarUrl,
|
||||
token: entry.value.token,
|
||||
platform: PlatformType.github,
|
||||
domain: 'https://github.com',
|
||||
login: entry.key)))
|
||||
.toList(),
|
||||
...accounts.map(_buildAccountItem),
|
||||
...List.generate(settings.accounts.length, _buildAccountItem),
|
||||
_buildAddItem(
|
||||
text: 'GitHub Account',
|
||||
onTap: settings.redirectToGithubOauth,
|
||||
|
@ -49,8 +49,8 @@ class NewsScreenState extends State<NewsScreen> {
|
||||
}
|
||||
|
||||
Future<ListPayload<EventPayload, int>> fetchEvents([int page = 1]) async {
|
||||
var settings = Provider.of<SettingsModel>(context);
|
||||
var login = settings.activeLogin;
|
||||
final settings = Provider.of<SettingsModel>(context);
|
||||
final login = settings.activeAccount.login;
|
||||
List data = await settings.getWithCredentials(
|
||||
'/users/$login/received_events?page=$page&per_page=$pageSize');
|
||||
// print(data.length);
|
||||
|
@ -13,6 +13,7 @@ export 'package:flutter_vector_icons/flutter_vector_icons.dart';
|
||||
final monospaceFont = Platform.isIOS ? 'Menlo' : 'monospace'; // FIXME:
|
||||
|
||||
class StorageKeys {
|
||||
static const accounts = 'accounts';
|
||||
static const account = 'account';
|
||||
static const github = 'github';
|
||||
static const theme = 'theme';
|
||||
|
@ -42,8 +42,8 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^1.1.3
|
||||
json_serializable: ^2.0.1
|
||||
build_runner: ^1.7.1
|
||||
json_serializable: ^3.2.2
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://www.dartlang.org/tools/pub/pubspec
|
||||
|
Loading…
x
Reference in New Issue
Block a user