git-touch-android-ios-app/lib/providers/settings.dart

356 lines
9.0 KiB
Dart
Raw Normal View History

import 'dart:io';
2019-02-07 07:35:19 +01:00
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:uni_links/uni_links.dart';
import 'package:nanoid/nanoid.dart';
import 'package:url_launcher/url_launcher.dart';
2019-02-07 07:35:19 +01:00
// import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
2019-02-07 07:35:19 +01:00
import 'package:shared_preferences/shared_preferences.dart';
2019-02-21 14:21:16 +01:00
// import '../utils/utils.dart';
import '../utils/constants.dart';
import '../models/account.dart';
2019-02-21 14:21:16 +01:00
class PlatformType {
static const github = 0;
static const gitlab = 1;
}
// 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);
// }
2019-01-30 07:46:18 +01:00
2019-02-07 07:35:19 +01:00
class ThemeMap {
2019-01-30 07:46:18 +01:00
static const material = 0;
static const cupertino = 1;
2019-02-21 14:21:16 +01:00
static const values = [0, 1];
2019-02-07 07:35:19 +01:00
}
class SettingsProvider extends StatefulWidget {
final Widget child;
SettingsProvider({@required this.child});
static SettingsProviderState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(_InheritedSettingsProvider)
as _InheritedSettingsProvider)
.data;
2019-01-30 07:46:18 +01:00
}
@override
SettingsProviderState createState() => new SettingsProviderState();
2019-01-30 07:46:18 +01:00
}
class SettingsProviderState extends State<SettingsProvider> {
2019-02-07 07:35:19 +01:00
bool ready = false;
int theme;
Map<String, AccountModel> githubAccountMap;
2019-02-21 14:21:16 +01:00
Map<String, AccountModel> gitlabAccountMap;
2019-02-21 14:21:16 +01:00
int activePlatform;
2019-02-07 07:35:19 +01:00
String activeLogin;
2019-02-21 14:21:16 +01:00
2019-02-07 07:35:19 +01:00
StreamSubscription<Uri> _sub;
bool loading = false;
2019-02-07 07:35:19 +01:00
// PlatformType platformType;
String prefix = 'https://api.github.com';
2019-02-08 16:20:28 +01:00
Future<void> setTheme(int _theme) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
theme = _theme;
await prefs.setInt('theme', theme);
print('write theme: $theme');
2019-02-08 16:20:28 +01:00
setState(() {});
}
2019-02-21 14:21:16 +01:00
String get token {
if (activeLogin == null) return null;
switch (activePlatform) {
case PlatformType.github:
return githubAccountMap[activeLogin].token;
case PlatformType.gitlab:
return gitlabAccountMap[activeLogin].token;
default:
return null;
2019-02-07 07:35:19 +01:00
}
}
2019-01-30 07:46:18 +01:00
@override
void initState() {
super.initState();
2019-02-07 07:35:19 +01:00
_initDataFromPref();
2019-02-08 12:44:10 +01:00
_sub = getUriLinksStream().listen(_onSchemeDetected, onError: (err) {
2019-02-07 07:35:19 +01:00
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 {
try {
// FIXME:
await closeWebView();
} catch (err) {}
setState(() {
loading = true;
});
2019-02-07 07:35:19 +01:00
// get token by code
var code = uri.queryParameters['code'];
// print(code);
var res = await http.post(
'https://github.com/login/oauth/access_token',
headers: {
HttpHeaders.acceptHeader: 'application/json',
HttpHeaders.contentTypeHeader: 'application/json',
},
body: json.encode({
'client_id': clientId,
'client_secret': clientSecret,
'code': code,
'state': randomString,
}),
);
2019-02-08 16:20:28 +01:00
// print(res.body);
2019-02-07 07:35:19 +01:00
var data = json.decode(res.body);
_loginWithToken(data['access_token']);
}
2019-02-07 07:35:19 +01:00
Future<void> _loginWithToken(String token) async {
2019-02-07 07:35:19 +01:00
// get login and avatar url
var queryData = await query('''
{
viewer {
login
avatarUrl
}
}
''', token);
2019-02-07 07:35:19 +01:00
String login = queryData['viewer']['login'];
String avatarUrl = queryData['viewer']['avatarUrl'];
githubAccountMap[login] = AccountModel(avatarUrl: avatarUrl, token: token);
2019-02-07 07:35:19 +01:00
// write
SharedPreferences prefs = await SharedPreferences.getInstance();
2019-02-21 14:21:16 +01:00
await prefs.setString(
'github',
json.encode(githubAccountMap
.map((login, account) => MapEntry(login, account.toJson()))));
2019-02-07 07:35:19 +01:00
setState(() {
loading = false;
});
2019-02-07 07:35:19 +01:00
}
2019-02-21 14:21:16 +01:00
Future<void> loginToGitlab(String domain, String token) async {
setState(() {
loading = true;
});
try {
var res = await http
.get('$domain/api/v4/user', headers: {'Private-Token': token});
var info = json.decode(res.body);
if (info['message'] != null) {
throw info['message'];
}
String login = info['username'];
String avatarUrl = info['avatar_url'];
gitlabAccountMap[login] =
AccountModel(token: token, avatarUrl: avatarUrl, domain: domain);
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(
'gitlab',
json.encode(gitlabAccountMap
.map((login, account) => MapEntry(login, account.toJson()))));
} catch (err) {
print(err);
// TODO: show errors
} finally {
setState(() {
loading = false;
});
}
}
2019-02-07 07:35:19 +01:00
void _initDataFromPref() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// read GitHub accounts
try {
2019-02-21 14:21:16 +01:00
String str = prefs.getString('github');
print('read github: $str');
Map<String, dynamic> data = json.decode(str ?? '{}');
githubAccountMap = data.map<String, AccountModel>((login, _accountMap) =>
MapEntry(login, AccountModel.fromJson(_accountMap)));
2019-02-07 07:35:19 +01:00
} catch (err) {
print(err);
githubAccountMap = {};
}
2019-02-21 14:21:16 +01:00
try {
String str = prefs.getString('gitlab');
print('read gitlab: $str');
Map<String, dynamic> data = json.decode(str ?? '{}');
gitlabAccountMap = data.map<String, AccountModel>((login, _accountMap) =>
MapEntry(login, AccountModel.fromJson(_accountMap)));
} catch (err) {
print(err);
gitlabAccountMap = {};
}
2019-02-07 07:35:19 +01:00
int _theme = prefs.getInt('theme');
print('read theme: $_theme');
2019-02-21 14:21:16 +01:00
if (ThemeMap.values.contains(_theme)) {
2019-02-07 07:35:19 +01:00
theme = _theme;
} else if (Platform.isIOS) {
theme = ThemeMap.cupertino;
}
2019-02-07 07:35:19 +01:00
setState(() {
ready = true;
});
}
2019-02-21 14:21:16 +01:00
void setActiveAccount(String login, int type) {
2019-02-07 07:35:19 +01:00
setState(() {
activeLogin = login;
2019-02-21 14:21:16 +01:00
activePlatform = type;
2019-02-08 16:20:28 +01:00
});
2019-02-07 07:35:19 +01:00
}
// http timeout
var _timeoutDuration = Duration(seconds: 10);
// var _timeoutDuration = Duration(seconds: 1);
2019-02-07 07:35:19 +01:00
Future<dynamic> query(String query, [String _token]) async {
if (_token == null) {
_token = token;
}
if (_token == null) {
throw Exception('token is null');
}
final res = await http
.post(prefix + '/graphql',
headers: {
HttpHeaders.authorizationHeader: 'token $_token',
HttpHeaders.contentTypeHeader: 'application/json'
},
body: json.encode({'query': query}))
.timeout(_timeoutDuration);
2019-02-07 07:35:19 +01:00
final data = json.decode(res.body);
2019-02-10 12:15:50 +01:00
// print(data);
2019-02-07 07:35:19 +01:00
if (data['errors'] != null) {
throw new Exception(data['errors'].toString());
}
2019-02-10 12:15:50 +01:00
2019-02-07 07:35:19 +01:00
return data['data'];
}
Future<dynamic> getWithCredentials(String url, {String contentType}) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
if (contentType != null) {
// https://developer.github.com/v3/repos/contents/#custom-media-types
headers[HttpHeaders.contentTypeHeader] = contentType;
}
final res = await http
.get(prefix + url, headers: headers)
.timeout(_timeoutDuration);
2019-02-08 16:20:28 +01:00
// print(res.body);
2019-02-07 07:35:19 +01:00
final data = json.decode(res.body);
return data;
}
Future<dynamic> patchWithCredentials(String url) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
await http.patch(prefix + url, headers: headers).timeout(_timeoutDuration);
2019-02-07 07:35:19 +01:00
return true;
}
Future<dynamic> putWithCredentials(String url,
{String contentType, String body}) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
final res = await http
.put(prefix + url, headers: headers, body: body ?? {})
.timeout(_timeoutDuration);
2019-02-10 12:15:50 +01:00
// print(res.body);
2019-02-07 13:28:48 +01:00
return true;
2019-02-07 07:35:19 +01:00
}
Future<dynamic> deleteWithCredentials(String url) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
final res = await http
.delete(prefix + url, headers: headers)
.timeout(_timeoutDuration);
2019-02-10 12:15:50 +01:00
// print(res.body);
return true;
}
2019-02-07 07:35:19 +01:00
String randomString;
generateRandomString() {
randomString = nanoid();
return randomString;
}
2019-01-30 07:46:18 +01:00
@override
Widget build(BuildContext context) {
return new _InheritedSettingsProvider(
data: this,
child: widget.child,
);
}
}
class _InheritedSettingsProvider extends InheritedWidget {
final SettingsProviderState data;
2019-01-30 07:46:18 +01:00
_InheritedSettingsProvider({
Key key,
@required this.data,
@required Widget child,
}) : super(key: key, child: child);
@override
bool updateShouldNotify(_InheritedSettingsProvider old) => true;
2019-01-30 07:46:18 +01:00
}