refactor: settings with provider

This commit is contained in:
Rongjian Zhang 2019-09-08 20:07:35 +08:00
parent 1108346163
commit fe41448c3d
19 changed files with 94 additions and 133 deletions

View File

@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/settings.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/screens/repo.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/notification.dart';
import 'providers/settings.dart';
import 'screens/news.dart';
import 'screens/notifications.dart';
import 'screens/search.dart';
@ -32,6 +32,7 @@ class _HomeState extends State<Home> {
nextTick(() {
// FIXME:
Provider.of<ThemeModel>(context).init();
Provider.of<SettingsModel>(context).init();
});
}
@ -114,7 +115,7 @@ class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
var settings = SettingsProvider.of(context);
var settings = Provider.of<SettingsModel>(context);
var themData = ThemeData(
// primaryColor: HSLColor.fromColor(Palette.primary)
// .withLightness(0.3)
@ -179,8 +180,9 @@ class App extends StatelessWidget {
providers: [
ChangeNotifierProvider(builder: (context) => NotificationModel()),
ChangeNotifierProvider(builder: (context) => ThemeModel()),
ChangeNotifierProvider(builder: (context) => SettingsModel()),
],
child: SettingsProvider(child: Home()),
child: Home(),
);
}
}

View File

@ -12,7 +12,7 @@ import 'package:shared_preferences/shared_preferences.dart';
// import '../utils/utils.dart';
import '../utils/constants.dart';
import '../utils/utils.dart';
import '../models/account.dart';
import 'account.dart';
class PlatformType {
static const github = 'github';
@ -21,7 +21,7 @@ class PlatformType {
// abstract class Model<T> {
// Future<T> query(BuildContext context) {
// var settings = SettingsProvider.of(context);
// var settings = Provider.of<SettingsModel>(context);
// switch (settings.platformType) {
// case PlatformType.github:
@ -37,22 +37,7 @@ class PlatformType {
// Future<T> queryGitlab(SettingsProviderState settings);
// }
class SettingsProvider extends StatefulWidget {
final Widget child;
SettingsProvider({@required this.child});
static SettingsProviderState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(_InheritedSettingsProvider)
as _InheritedSettingsProvider)
.data;
}
@override
SettingsProviderState createState() => SettingsProviderState();
}
class SettingsProviderState extends State<SettingsProvider> {
class SettingsModel with ChangeNotifier {
bool ready = false;
Map<String, AccountModel> githubAccountMap;
@ -67,7 +52,7 @@ class SettingsProviderState extends State<SettingsProvider> {
// PlatformType platformType;
String prefix = 'https://api.github.com';
String _apiPrefix = 'https://api.github.com';
String get token {
if (activeLogin == null) return null;
@ -80,29 +65,12 @@ class SettingsProviderState extends State<SettingsProvider> {
}
}
@override
void initState() {
super.initState();
_initDataFromPref();
_sub = getUriLinksStream().listen(_onSchemeDetected, onError: (err) {
print(err);
});
}
@override
void dispose() {
super.dispose();
_sub.cancel();
}
// https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/#web-application-flow
Future<void> _onSchemeDetected(Uri uri) async {
await closeWebView();
setState(() {
loading = true;
});
loading = true;
notifyListeners();
// get token by code
var code = uri.queryParameters['code'];
@ -147,15 +115,13 @@ class SettingsProviderState extends State<SettingsProvider> {
json.encode(githubAccountMap
.map((login, account) => MapEntry(login, account.toJson()))));
setState(() {
loading = false;
});
loading = false;
notifyListeners();
}
Future<void> loginToGitlab(String domain, String token) async {
setState(() {
loading = true;
});
loading = true;
notifyListeners();
try {
var res = await http
@ -191,13 +157,16 @@ class SettingsProviderState extends State<SettingsProvider> {
print(err);
// TODO: show errors
} finally {
setState(() {
loading = false;
});
loading = false;
notifyListeners();
}
}
void _initDataFromPref() async {
void init() async {
_sub = getUriLinksStream().listen(_onSchemeDetected, onError: (err) {
print(err);
});
var prefs = await SharedPreferences.getInstance();
// read GitHub accounts
@ -245,17 +214,15 @@ class SettingsProviderState extends State<SettingsProvider> {
accountMap = {};
}
setState(() {
ready = true;
});
ready = true;
notifyListeners();
}
void setActiveAccount(String platform, String domain, String login) {
setState(() {
activePlatform = platform;
activeDomain = domain;
activeLogin = login;
});
activePlatform = platform;
activeDomain = domain;
activeLogin = login;
notifyListeners();
}
Map<String, String> get _headers =>
@ -274,7 +241,7 @@ class SettingsProviderState extends State<SettingsProvider> {
}
final res = await http
.post(prefix + '/graphql',
.post(_apiPrefix + '/graphql',
headers: {
HttpHeaders.authorizationHeader: 'token $_token',
HttpHeaders.contentTypeHeader: 'application/json'
@ -299,7 +266,7 @@ class SettingsProviderState extends State<SettingsProvider> {
headers[HttpHeaders.contentTypeHeader] = contentType;
}
final res = await http
.get(prefix + url, headers: headers)
.get(_apiPrefix + url, headers: headers)
.timeout(_timeoutDuration);
// print(res.body);
final data = json.decode(res.body);
@ -307,27 +274,29 @@ class SettingsProviderState extends State<SettingsProvider> {
}
Future<void> patchWithCredentials(String url) async {
await http.patch(prefix + url, headers: _headers).timeout(_timeoutDuration);
await http
.patch(_apiPrefix + url, headers: _headers)
.timeout(_timeoutDuration);
}
Future<void> putWithCredentials(String url,
{String contentType, String body}) async {
await http
.put(prefix + url, headers: _headers, body: body ?? {})
.put(_apiPrefix + url, headers: _headers, body: body ?? {})
.timeout(_timeoutDuration);
}
Future<void> postWithCredentials(String url,
{String contentType, String body}) async {
final res = await http
.post(prefix + url, headers: _headers, body: body ?? {})
.post(_apiPrefix + url, headers: _headers, body: body ?? {})
.timeout(_timeoutDuration);
// print(res.body);
}
Future<void> deleteWithCredentials(String url) async {
await http
.delete(prefix + url, headers: _headers)
.delete(_apiPrefix + url, headers: _headers)
.timeout(_timeoutDuration);
}
@ -338,25 +307,4 @@ class SettingsProviderState extends State<SettingsProvider> {
'https://github.com/login/oauth/authorize?client_id=$clientId&redirect_uri=gittouch://login&scope=user%20repo&state=$_oauthState',
);
}
@override
Widget build(BuildContext context) {
return _InheritedSettingsProvider(
data: this,
child: widget.child,
);
}
}
class _InheritedSettingsProvider extends InheritedWidget {
final SettingsProviderState data;
_InheritedSettingsProvider({
Key key,
@required this.data,
@required Widget child,
}) : super(key: key, child: child);
@override
bool updateShouldNotify(_InheritedSettingsProvider old) => true;
}

View File

@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:git_touch/models/settings.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:provider/provider.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:git_touch/providers/settings.dart';
import 'package:git_touch/scaffolds/list.dart';
import 'package:git_touch/widgets/avatar.dart';
import 'package:primer/primer.dart';
@ -18,7 +19,7 @@ class CommitsScreen extends StatelessWidget {
if (cursor != null) {
params += ', after: "$cursor"';
}
var data = await SettingsProvider.of(context).query('''
var data = await Provider.of<SettingsModel>(context).query('''
{
repository(owner: "$owner", name: "$name") {
ref(qualifiedName: "master") {

View File

@ -1,13 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/settings.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
import 'package:share/share.dart';
import 'package:url_launcher/url_launcher.dart';
import '../utils/utils.dart';
import '../scaffolds/long_list.dart';
import '../widgets/timeline_item.dart';
import '../widgets/comment_item.dart';
import '../providers/settings.dart';
import '../widgets/action.dart';
var reactionChunk = emojiMap.entries.map((entry) {
@ -272,7 +273,7 @@ __typename
}
}
var data = await SettingsProvider.of(context).query('''
var data = await Provider.of<SettingsModel>(context).query('''
{
repository(owner: "$owner", name: "$name") {
$resource(number: $number) {
@ -322,7 +323,7 @@ __typename
var id = payload['id'] as String;
var operation = isRemove ? 'remove' : 'add';
await SettingsProvider.of(context).query('''
await Provider.of<SettingsModel>(context).query('''
mutation {
${operation}Reaction(input: {subjectId: "$id", content: $emojiKey}) {
clientMutationId

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import '../scaffolds/list.dart';
import '../providers/settings.dart';
import '../utils/utils.dart';
import '../widgets/link.dart';
import '../screens/issue.dart';
@ -29,7 +30,7 @@ class _IssuesScreenState extends State<IssuesScreen> {
var cursorChunk = cursor == null ? '' : ', after: "$cursor"';
var resource = isPullRequest ? 'pullRequests' : 'issues';
var data = await SettingsProvider.of(context).query('''
var data = await Provider.of<SettingsModel>(context).query('''
{
repository(owner: "$owner", name: "$name") {
$resource(states: OPEN, first: $pageSize$cursorChunk) {

View File

@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import '../scaffolds/simple.dart';
import '../utils/constants.dart';
import '../widgets/link.dart';
import '../widgets/loading.dart';
import '../models/account.dart';
@ -16,7 +15,7 @@ class LoginScreen extends StatefulWidget {
class _LoginScreenState extends State<LoginScreen> {
Widget _buildAccountItem(AccountModel account) {
var settings = SettingsProvider.of(context);
var settings = Provider.of<SettingsModel>(context);
return Link(
onTap: () {
@ -75,7 +74,7 @@ class _LoginScreenState extends State<LoginScreen> {
@override
Widget build(BuildContext context) {
var settings = SettingsProvider.of(context);
var settings = Provider.of<SettingsModel>(context);
List<AccountModel> accounts = [];
settings.accountMap.forEach((platform, v0) {

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import '../scaffolds/simple.dart';
import '../providers/settings.dart';
class LoginGitlabScreen extends StatefulWidget {
@override
@ -32,7 +33,8 @@ class _LoginGitlabScreenState extends State<LoginGitlabScreen> {
MaterialButton(
child: Text('Login'),
onPressed: () {
SettingsProvider.of(context).loginToGitlab(_domain, _token);
Provider.of<SettingsModel>(context)
.loginToGitlab(_domain, _token);
Navigator.of(context).pop();
},
)

View File

@ -1,13 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import '../screens/user.dart';
class MeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return UserScreen(
SettingsProvider.of(context).activeLogin,
Provider.of<SettingsModel>(context).activeLogin,
showSettings: true,
);
}

View File

@ -5,7 +5,7 @@ import 'package:git_touch/utils/utils.dart';
import 'package:provider/provider.dart';
import '../scaffolds/list.dart';
import '../widgets/event_item.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
class NewsFilter {
static const all = 'all';
@ -31,7 +31,7 @@ class NewsScreenState extends State<NewsScreen> {
nextTick(() async {
// Check if there are unread notification items.
// 1 item is enough since count is not displayed for now.
var items = await SettingsProvider.of(context)
var items = await Provider.of<SettingsModel>(context)
.getWithCredentials('/notifications?per_page=1');
if (items is List && items.isNotEmpty) {
@ -48,7 +48,7 @@ class NewsScreenState extends State<NewsScreen> {
}
Future<ListPayload<EventPayload, int>> fetchEvents([int page = 1]) async {
var settings = SettingsProvider.of(context);
var settings = Provider.of<SettingsModel>(context);
var login = settings.activeLogin;
List data = await settings.getWithCredentials(
'/users/$login/received_events?page=$page&per_page=$pageSize');

View File

@ -4,7 +4,7 @@ import 'package:provider/provider.dart';
import '../scaffolds/refresh_stateless.dart';
import 'package:git_touch/models/notification.dart';
import 'package:git_touch/models/theme.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import '../widgets/notification_item.dart';
import '../widgets/list_group.dart';
import '../widgets/link.dart';
@ -29,7 +29,7 @@ class NotificationScreenState extends State<NotificationScreen> {
}
Future<Map<String, NotificationGroup>> fetchNotifications(int index) async {
List items = await SettingsProvider.of(context).getWithCredentials(
List items = await Provider.of<SettingsModel>(context).getWithCredentials(
'/notifications?all=${index == 2}&participating=${index == 1}');
var ns = items.map((item) => NotificationPayload.fromJson(item)).toList();
@ -88,7 +88,7 @@ $key: pullRequest(number: ${item.number}) {
schema += '}';
// print(schema);
var data = await SettingsProvider.of(context).query(schema);
var data = await Provider.of<SettingsModel>(context).query(schema);
_groupMap.forEach((repo, group) {
group.items.forEach((item) {
var groupData = data[group.key];
@ -123,7 +123,7 @@ $key: pullRequest(number: ${item.number}) {
Link(
material: false,
onTap: () async {
await SettingsProvider.of(context)
await Provider.of<SettingsModel>(context)
.putWithCredentials('/repos/$repo/notifications');
await _onSwitchTab();
},
@ -204,7 +204,8 @@ $key: pullRequest(number: ${item.number}) {
var value = await Provider.of<ThemeModel>(context)
.showConfirm(context, 'Mark all as read?');
if (value) {
await SettingsProvider.of(context).putWithCredentials('/notifications');
await Provider.of<SettingsModel>(context)
.putWithCredentials('/notifications');
await _onSwitchTab();
}
}

View File

@ -3,7 +3,8 @@ import 'package:git_touch/screens/image_view.dart';
import 'package:path/path.dart' as path;
import 'package:flutter/material.dart';
import 'package:flutter_highlight/flutter_highlight.dart';
import 'package:git_touch/providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/scaffolds/refresh.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/link.dart';
@ -126,7 +127,7 @@ class ObjectScreen extends StatelessWidget {
return RefreshScaffold(
title: Text(paths.join('/')),
onRefresh: () async {
var data = await SettingsProvider.of(context).query('''{
var data = await Provider.of<SettingsModel>(context).query('''{
repository(owner: "$owner", name: "$name") {
object(expression: "$expression") {
$_subQuery

View File

@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:share/share.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import '../scaffolds/refresh.dart';
import '../widgets/avatar.dart';
import '../widgets/entry_item.dart';
@ -23,7 +24,7 @@ class OrganizationScreen extends StatefulWidget {
class _OrganizationScreenState extends State<OrganizationScreen> {
Future query() async {
var login = widget.login;
var data = await SettingsProvider.of(context).query('''
var data = await Provider.of<SettingsModel>(context).query('''
{
organization(login: "$login") {
name

View File

@ -1,14 +1,14 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/screens/commits.dart';
import 'package:git_touch/screens/object.dart';
import 'package:provider/provider.dart';
import 'package:share/share.dart';
import 'package:url_launcher/url_launcher.dart';
import '../providers/settings.dart';
import '../scaffolds/refresh.dart';
import '../widgets/repo_item.dart';
import '../widgets/entry_item.dart';
@ -33,7 +33,7 @@ class _RepoScreenState extends State<RepoScreen> {
String get name => widget.name;
Future queryRepo(BuildContext context) async {
var data = await SettingsProvider.of(context).query('''
var data = await Provider.of<SettingsModel>(context).query('''
{
repository(owner: "$owner", name: "$name") {
id
@ -85,7 +85,7 @@ class _RepoScreenState extends State<RepoScreen> {
Future fetchReadme(BuildContext context) async {
var owner = widget.owner;
var name = widget.name;
var data = await SettingsProvider.of(context)
var data = await Provider.of<SettingsModel>(context)
.getWithCredentials('/repos/$owner/$name/readme');
if (data['content'] == null) {
@ -132,11 +132,11 @@ class _RepoScreenState extends State<RepoScreen> {
text: payload['viewerHasStarred'] ? 'Unstar' : 'Star',
onPress: () async {
if (payload['viewerHasStarred']) {
await SettingsProvider.of(context)
await Provider.of<SettingsModel>(context)
.deleteWithCredentials('/user/starred/$owner/$name');
payload['viewerHasStarred'] = false;
} else {
SettingsProvider.of(context)
Provider.of<SettingsModel>(context)
.putWithCredentials('/user/starred/$owner/$name');
payload['viewerHasStarred'] = true;
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import '../scaffolds/list.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import '../utils/utils.dart';
import '../widgets/repo_item.dart';
@ -32,7 +33,7 @@ class _ReposScreenState extends State<ReposScreen> {
Future<ListPayload> _queryRepos([String cursor]) async {
var cursorChunk = cursor == null ? '' : ', after: "$cursor"';
var data = await SettingsProvider.of(context).query('''
var data = await Provider.of<SettingsModel>(context).query('''
{
$scope(login: "$login") {
$resource(first: $pageSize$cursorChunk, orderBy: {field: $fieldOrderBy, direction: DESC}) {

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import '../scaffolds/simple.dart';
import '../utils/utils.dart';
import '../widgets/repo_item.dart';
@ -24,7 +24,7 @@ class _SearchScreenState extends State<SearchScreen> {
});
try {
// TODO: search other types
var data = await SettingsProvider.of(context).query('''
var data = await Provider.of<SettingsModel>(context).query('''
{
search(first: $pageSize, type: REPOSITORY, query: "$value") {
nodes {

View File

@ -6,7 +6,8 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:share/share.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:github_contributions/github_contributions.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import '../scaffolds/refresh.dart';
import '../widgets/avatar.dart';
import '../widgets/entry_item.dart';
@ -31,7 +32,7 @@ class UserScreen extends StatefulWidget {
class _UserScreenState extends State<UserScreen> {
Future query() async {
var login = widget.login;
var data = await SettingsProvider.of(context).query('''
var data = await Provider.of<SettingsModel>(context).query('''
{
user(login: "$login") {
name
@ -149,11 +150,11 @@ class _UserScreenState extends State<UserScreen> {
text: payload['viewerIsFollowing'] ? 'Unfollow' : 'Follow',
onPress: () async {
if (payload['viewerIsFollowing']) {
await SettingsProvider.of(context)
await Provider.of<SettingsModel>(context)
.deleteWithCredentials('/user/following/${widget.login}');
payload['viewerIsFollowing'] = false;
} else {
SettingsProvider.of(context)
Provider.of<SettingsModel>(context)
.putWithCredentials('/user/following/${widget.login}');
payload['viewerIsFollowing'] = true;
}

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:primer/primer.dart';
import '../scaffolds/list.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import '../utils/utils.dart';
import '../widgets/link.dart';
import '../screens/user.dart';
@ -38,7 +39,7 @@ class _UsersScreenState extends State<UsersScreen> {
Future<ListPayload> _queryUsers([String cursor]) async {
var cursorChunk = cursor == null ? '' : ', after: "$cursor"';
var data = await SettingsProvider.of(context).query('''
var data = await Provider.of<SettingsModel>(context).query('''
{
$scope(login: "$login") {
$resource(first: $pageSize$cursorChunk) {

View File

@ -3,7 +3,6 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:git_touch/screens/user.dart';
import 'package:primer/primer.dart';
import '../providers/settings.dart';
import '../screens/repo.dart';
export 'package:flutter_vector_icons/flutter_vector_icons.dart';

View File

@ -6,7 +6,8 @@ import 'package:url_launcher/url_launcher.dart';
import '../utils/utils.dart';
import '../screens/issue.dart';
// import '../screens/not_found.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart';
import 'link.dart';
class NotificationItem extends StatefulWidget {
@ -80,7 +81,7 @@ class _NotificationItemState extends State<NotificationItem> {
loading = true;
});
try {
await SettingsProvider.of(context)
await Provider.of<SettingsModel>(context)
.patchWithCredentials('/notifications/threads/' + payload.id);
widget.markAsRead();
} finally {