refactor: function params, add gitlab login screen

This commit is contained in:
Rongjian Zhang 2019-02-21 18:43:41 +08:00
parent 0396ac7f7b
commit 7fe5575d25
14 changed files with 136 additions and 86 deletions

View File

@ -10,6 +10,30 @@ import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../utils/utils.dart';
import '../utils/constants.dart';
import '../models/account.dart';
// enum PlatformType {
// github,
// gitlab,
// }
// abstract class Model<T> {
// Future<T> query(BuildContext context) {
// var settings = SettingsProvider.of(context);
// switch (settings.platformType) {
// case PlatformType.github:
// return queryGithub(settings);
// case PlatformType.gitlab:
// return queryGitlab(settings);
// default:
// return null;
// }
// }
// Future<T> queryGithub(SettingsProviderState settings);
// Future<T> queryGitlab(SettingsProviderState settings);
// }
class ThemeMap {
static const material = 0;
@ -17,59 +41,35 @@ class ThemeMap {
static const all = [0, 1];
}
final prefix = 'https://api.github.com';
class Account {
String avatarUrl;
String token;
/// for github enterprise
String domain;
Account({
@required this.avatarUrl,
@required this.token,
this.domain,
});
Account.fromJson(input) {
avatarUrl = input['avatarUrl'];
token = input['token'];
domain = input['domain'];
}
Map<String, dynamic> toJson() {
var data = {'avatarUrl': avatarUrl, 'token': token};
if (domain != null) {
data['domain'] = domain;
}
return data;
}
}
class SettingsProvider extends StatefulWidget {
final Widget child;
SettingsProvider({@required this.child});
static _SettingsProviderState of(BuildContext context) {
static SettingsProviderState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(_InheritedSettingsProvider)
as _InheritedSettingsProvider)
.data;
}
@override
_SettingsProviderState createState() => new _SettingsProviderState();
SettingsProviderState createState() => new SettingsProviderState();
}
class _SettingsProviderState extends State<SettingsProvider> {
class SettingsProviderState extends State<SettingsProvider> {
bool ready = false;
int theme;
Map<String, Account> githubAccountMap;
Map<String, AccountModel> githubAccountMap;
String activeLogin;
StreamSubscription<Uri> _sub;
bool loading = false;
// PlatformType platformType;
String prefix = 'https://api.github.com';
Future<void> setTheme(int _theme) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
@ -104,7 +104,7 @@ class _SettingsProviderState extends State<SettingsProvider> {
}
// https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/#web-application-flow
void _onSchemeDetected(Uri uri) async {
Future<void> _onSchemeDetected(Uri uri) async {
try {
// FIXME:
await closeWebView();
@ -132,8 +132,10 @@ class _SettingsProviderState extends State<SettingsProvider> {
);
// print(res.body);
var data = json.decode(res.body);
String _token = data['access_token'];
_loginWithToken(data['access_token']);
}
Future<void> _loginWithToken(String token) async {
// get login and avatar url
var queryData = await query('''
{
@ -142,11 +144,11 @@ class _SettingsProviderState extends State<SettingsProvider> {
avatarUrl
}
}
''', _token);
''', token);
String login = queryData['viewer']['login'];
String avatarUrl = queryData['viewer']['avatarUrl'];
githubAccountMap[login] = Account(avatarUrl: avatarUrl, token: _token);
githubAccountMap[login] = AccountModel(avatarUrl: avatarUrl, token: token);
// write
SharedPreferences prefs = await SharedPreferences.getInstance();
@ -168,8 +170,9 @@ class _SettingsProviderState extends State<SettingsProvider> {
var str = prefs.getString('github');
// print('read github: $str');
Map<String, dynamic> github = json.decode(str);
githubAccountMap = github.map<String, Account>((login, _accountMap) =>
MapEntry(login, Account.fromJson(_accountMap)));
githubAccountMap = github.map<String, AccountModel>(
(login, _accountMap) =>
MapEntry(login, AccountModel.fromJson(_accountMap)));
} catch (err) {
print(err);
githubAccountMap = {};
@ -192,17 +195,9 @@ class _SettingsProviderState extends State<SettingsProvider> {
}
void setActiveAccount(String login) {
// FIXME: This is pretty tricky to trigger home screen rebuild
setState(() {
activeLogin = null;
activeLogin = login;
});
nextTick(() {
setState(() {
activeLogin = login;
// activeLogin = null;
// ready = true;
});
}, 100);
}
// http timeout
@ -294,7 +289,7 @@ class _SettingsProviderState extends State<SettingsProvider> {
}
class _InheritedSettingsProvider extends InheritedWidget {
final _SettingsProviderState data;
final SettingsProviderState data;
_InheritedSettingsProvider({
Key key,

View File

@ -129,7 +129,7 @@ class _ListScaffoldState<T, K> extends State<ListScaffold<T, K>> {
Widget _buildSliver(BuildContext context) {
if (error.isNotEmpty) {
return SliverToBoxAdapter(
child: ErrorReload(text: error, reload: _refresh),
child: ErrorReload(text: error, onTap: _refresh),
);
} else if (loading) {
return SliverToBoxAdapter(child: Loading(more: false));
@ -147,7 +147,7 @@ class _ListScaffoldState<T, K> extends State<ListScaffold<T, K>> {
Widget _buildBody(BuildContext context) {
if (error.isNotEmpty) {
return ErrorReload(text: error, reload: _refresh);
return ErrorReload(text: error, onTap: _refresh);
} else if (loading) {
return Loading(more: false);
} else if (items.isEmpty) {

View File

@ -127,7 +127,7 @@ class _LongListScaffoldState<T, K> extends State<LongListScaffold<T, K>> {
),
child: Center(
child: Link(
beforeRedirect: _loadMore,
onTap: _loadMore,
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
@ -168,7 +168,7 @@ class _LongListScaffoldState<T, K> extends State<LongListScaffold<T, K>> {
Widget _buildSliver() {
if (error.isNotEmpty) {
return SliverToBoxAdapter(
child: ErrorReload(text: error, reload: _refresh));
child: ErrorReload(text: error, onTap: _refresh));
} else if (loading) {
return SliverToBoxAdapter(child: Loading(more: false));
} else {

View File

@ -40,7 +40,7 @@ class _RefreshScaffoldState<T> extends State<RefreshScaffold<T>> {
Widget _buildBody() {
if (error.isNotEmpty) {
return ErrorReload(text: error, reload: _refresh);
return ErrorReload(text: error, onTap: _refresh);
} else if (payload == null) {
return Loading(more: false);
} else {

View File

@ -29,7 +29,7 @@ class RefreshStatelessScaffold extends StatelessWidget {
Widget _buildBody() {
if (error.isNotEmpty) {
return ErrorReload(text: error, reload: onRefresh);
return ErrorReload(text: error, onTap: onRefresh);
} else if (loading) {
return Loading(more: false);
} else {

View File

@ -3,11 +3,9 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import '../providers/settings.dart';
typedef WidgetBuilder = Widget Function();
class SimpleScaffold extends StatelessWidget {
final Widget title;
final WidgetBuilder bodyBuilder;
final Widget Function() bodyBuilder;
final Widget trailing;
final List<Widget> actions;
final PreferredSizeWidget bottom;

View File

@ -5,6 +5,7 @@ import '../scaffolds/simple.dart';
import '../utils/constants.dart';
import '../widgets/link.dart';
import '../widgets/loading.dart';
import 'login_gitlab.dart';
class LoginScreen extends StatefulWidget {
@override
@ -12,6 +13,27 @@ class LoginScreen extends StatefulWidget {
}
class _LoginScreenState extends State<LoginScreen> {
Widget _buildAddItem(
{String text, Function onTap, WidgetBuilder screenBuilder}) {
return Link(
child: Container(
padding: EdgeInsets.symmetric(vertical: 20),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.black12)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.add),
Text(text, style: TextStyle(fontSize: 16)),
],
),
),
onTap: onTap,
screenBuilder: screenBuilder,
);
}
@override
Widget build(BuildContext context) {
var settings = SettingsProvider.of(context);
@ -27,7 +49,7 @@ class _LoginScreenState extends State<LoginScreen> {
child: Column(
children: settings.githubAccountMap.entries.map<Widget>((entry) {
return Link(
beforeRedirect: () {
onTap: () {
// Navigator.of(context).pop();
settings.setActiveAccount(entry.key);
},
@ -60,26 +82,21 @@ class _LoginScreenState extends State<LoginScreen> {
),
);
}).toList()
..add(
Link(
child: Container(
padding: EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.add),
Text('Add account', style: TextStyle(fontSize: 16)),
],
),
),
beforeRedirect: () {
..addAll([
_buildAddItem(
text: 'GitHub Account',
onTap: () {
var state = settings.generateRandomString();
launch(
'https://github.com/login/oauth/authorize?client_id=$clientId&redirect_uri=gittouch://login&scope=user%20repo&state=$state',
);
},
),
),
_buildAddItem(
text: 'GitLab Account',
screenBuilder: (_) => LoginGitlabScreen(),
)
]),
),
);
},

View File

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import '../scaffolds/simple.dart';
class LoginGitlabScreen extends StatefulWidget {
@override
_LoginGitlabScreenState createState() => _LoginGitlabScreenState();
}
class _LoginGitlabScreenState extends State<LoginGitlabScreen> {
String _token;
String _domain;
@override
Widget build(BuildContext context) {
return SimpleScaffold(
title: Text('Login to GitLab'),
bodyBuilder: () {
return Column(
children: <Widget>[
TextField(
// decoration: InputDecoration(icon: Icon(Icons.more_vert)),
onChanged: (value) {
_domain = value;
},
),
TextField(
onChanged: (value) {
_token = value;
},
),
MaterialButton(
child: Text('Login'),
onPressed: () {},
)
],
);
},
);
}
}

View File

@ -128,7 +128,7 @@ $key: pullRequest(number: ${item.number}) {
),
Link(
material: false,
beforeRedirect: () async {
onTap: () async {
await SettingsProvider.of(context)
.putWithCredentials('/repos/$repo/notifications');
await _onSwitchTab();

View File

@ -101,7 +101,7 @@ class _UserScreenState extends State<UserScreen> {
Padding(padding: EdgeInsets.only(left: 4)),
Text(email, style: TextStyle(color: Colors.black54, fontSize: 15))
]),
beforeRedirect: () {
onTap: () {
launch('mailto:' + email);
},
);

View File

@ -3,9 +3,9 @@ import 'link.dart';
class ErrorReload extends StatelessWidget {
final String text;
final Function reload;
final Function onTap;
ErrorReload({@required this.text, @required this.reload});
ErrorReload({@required this.text, @required this.onTap});
@override
Widget build(BuildContext context) {
@ -32,7 +32,7 @@ class ErrorReload extends StatelessWidget {
'Reload',
style: TextStyle(fontSize: 20, color: Colors.blueAccent),
),
beforeRedirect: reload,
onTap: onTap,
material: false,
),
],

View File

@ -7,7 +7,7 @@ class Link extends StatelessWidget {
final Widget child;
final String url;
final WidgetBuilder screenBuilder;
final Function beforeRedirect;
final Function onTap;
final Color bgColor;
final bool material;
final bool fullscreenDialog;
@ -17,7 +17,7 @@ class Link extends StatelessWidget {
this.child,
this.url,
this.screenBuilder,
this.beforeRedirect,
this.onTap,
this.bgColor,
this.material = true,
this.fullscreenDialog = false,
@ -26,8 +26,8 @@ class Link extends StatelessWidget {
assert(screenBuilder == null || url == null);
void _onTap(BuildContext context, int theme) {
if (beforeRedirect != null) {
beforeRedirect();
if (onTap != null) {
onTap();
}
if (screenBuilder != null) {

View File

@ -150,7 +150,7 @@ class _NotificationItemState extends State<NotificationItem> {
Widget build(BuildContext context) {
return Link(
screenBuilder: _buildRoute,
beforeRedirect: _markAsRead,
onTap: _markAsRead,
child: Opacity(
opacity: payload.unread ? 1 : 0.5,
child: Container(
@ -168,7 +168,7 @@ class _NotificationItemState extends State<NotificationItem> {
style: TextStyle(fontSize: 15),
),
),
Link(child: _buildCheckIcon(), beforeRedirect: _markAsRead),
Link(child: _buildCheckIcon(), onTap: _markAsRead),
],
),
),

View File

@ -61,7 +61,7 @@ class TableView extends StatelessWidget {
}
return Link(
beforeRedirect: item.onTap,
onTap: item.onTap,
screenBuilder: item.screenBuilder,
child: Container(
decoration: BoxDecoration(