diff --git a/lib/ios/home.dart b/lib/ios/home.dart index 9f35f02..36fa084 100644 --- a/lib/ios/home.dart +++ b/lib/ios/home.dart @@ -1,6 +1,4 @@ import 'package:flutter/cupertino.dart'; -// import 'package:graphql_flutter/graphql_flutter.dart'; -// import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import '../components/event.dart'; import '../providers/event.dart'; diff --git a/lib/ios/main.dart b/lib/ios/main.dart index 339f59d..90bfeac 100644 --- a/lib/ios/main.dart +++ b/lib/ios/main.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'home.dart'; import 'notification.dart'; +import 'search.dart'; import 'profile.dart'; class IosHomePage extends StatefulWidget { @@ -46,7 +47,7 @@ class _IosHomePageState extends State { case 1: return NotificationScreen(); case 2: - return ProfileScreen(); + return SearchScreen(); case 3: return ProfileScreen(); default: diff --git a/lib/ios/search.dart b/lib/ios/search.dart index e69de29..b6b41cf 100644 --- a/lib/ios/search.dart +++ b/lib/ios/search.dart @@ -0,0 +1,66 @@ +import 'package:flutter/cupertino.dart'; +import '../providers/search.dart'; + +class SearchScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + SearchBloc bloc = SearchProvider.of(context); + + return SafeArea( + child: Column( + children: [ + CupertinoTextField( + placeholder: 'Type to search', + onChanged: (String value) { + bloc.keywordUpdate.add(value); + }, + onSubmitted: (String value) { + bloc.submit.add(value); + }, + ), + CupertinoSegmentedControl( + children: {0: Text('Repos'), 1: Text('Users')}, + onValueChanged: (int value) { + bloc.activeUpdate.add(value); + }, + ), + StreamBuilder( + stream: bloc.loading, + builder: (context, snapshot) { + if (snapshot.data == null || snapshot.data) { + return CupertinoActivityIndicator(); + } + + return StreamBuilder( + stream: bloc.users, + builder: (context, snapshot) { + var users = snapshot.data; + if (users == null) return Text(''); + if (users.length == 0) { + return Text("No result"); + } + + return ListView.builder( + shrinkWrap: true, + itemCount: users.length, + itemBuilder: (context, index) { + var user = users[index]; + return Row( + children: [ + Image.network( + user['avatarUrl'], + ), + Text(user['login']) + ], + ); + }, + ); + }, + ); + }, + ), + ], + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index be2d66c..9e43907 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,49 +1,40 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -// import 'dart:io'; -// import 'package:graphql_flutter/graphql_flutter.dart'; -// import 'android/main.dart'; import 'ios/main.dart'; -// import 'token.dart'; import 'providers/event.dart'; import 'providers/notification.dart'; +import 'providers/search.dart'; class App extends StatelessWidget { final isIos = true; final EventBloc eventBloc; final NotificationBloc notificationBloc; + final SearchBloc searchBloc; - App(this.eventBloc, this.notificationBloc); - - // final ValueNotifier client = ValueNotifier( - // GraphQLClient( - // cache: InMemoryCache(), - // link: HttpLink( - // uri: 'https://api.github.com/graphql', - // headers: {HttpHeaders.authorizationHeader: 'token $token'}, - // ), - // ), - // ); + App(this.eventBloc, this.notificationBloc, this.searchBloc); @override build(context) { - return NotificationProvider( - bloc: notificationBloc, - child: EventProvider( - bloc: eventBloc, - child: MaterialApp( - title: 'GitFlux', - theme: ThemeData( - primarySwatch: Colors.blue, + return SearchProvider( + bloc: searchBloc, + child: NotificationProvider( + bloc: notificationBloc, + child: EventProvider( + bloc: eventBloc, + child: MaterialApp( + title: 'GitFlux', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: DefaultTextStyle( + style: TextStyle(color: CupertinoColors.black), + child: IosHomePage(title: 'GitFlux'), + ), + routes: { + // '/notification': (context) => IosNotificationTab(), + // '/profile': (context) => IosProfileTab(), + }, ), - home: DefaultTextStyle( - style: TextStyle(color: CupertinoColors.black), - child: IosHomePage(title: 'GitFlux'), - ), - routes: { - // '/notification': (context) => IosNotificationTab(), - // '/profile': (context) => IosProfileTab(), - }, ), ), ); @@ -53,6 +44,7 @@ class App extends StatelessWidget { void main() { EventBloc eventBloc = EventBloc(); NotificationBloc notificationBloc = NotificationBloc(); + SearchBloc searchBloc = SearchBloc(); - runApp(App(eventBloc, notificationBloc)); + runApp(App(eventBloc, notificationBloc, searchBloc)); } diff --git a/lib/providers/search.dart b/lib/providers/search.dart new file mode 100644 index 0000000..87d8f8b --- /dev/null +++ b/lib/providers/search.dart @@ -0,0 +1,90 @@ +import 'package:flutter/widgets.dart'; +import 'dart:async'; +import 'package:rxdart/subjects.dart'; +import 'package:rxdart/rxdart.dart'; +import '../utils.dart'; + +Future search(String keyword, String type) async { + var data = await query(''' +{ + search(query: "$keyword", type: $type, first: 10) { + nodes { + ... on User { + avatarUrl + login + } + ... on Repository { + nameWithOwner + url + description + forkCount + stargazers { + totalCount + } + primaryLanguage { + name + color + } + } + } + } +} +'''); + return data['search']['nodes']; +} + +class SearchBloc { + final _keyword = BehaviorSubject(seedValue: ''); + final _active = BehaviorSubject(seedValue: 0); + final _loading = BehaviorSubject(seedValue: false); + final _users = BehaviorSubject(seedValue: []); + final _repos = BehaviorSubject(seedValue: []); + final _submit = StreamController(); + + Stream get keyword => _keyword.stream; + Stream get active => _active.stream; + Stream get loading => _loading.stream; + Stream get users => _users.stream; + Stream get repos => _repos.stream; + + Sink get activeUpdate => _active.sink; + Sink get keywordUpdate => _keyword.sink; + Sink get submit => _submit.sink; + + _getTypeByIndex(int index) { + switch (index) { + case 0: + return 'REPOSITORY'; + case 1: + return 'USER'; + } + } + + _querySearch(_) async { + _loading.add(true); + await search(_keyword.value, _getTypeByIndex(_active.value)); + _loading.add(false); + } + + SearchBloc() { + _submit.stream.listen(_querySearch); + } +} + +class SearchProvider extends InheritedWidget { + final SearchBloc bloc; + + SearchProvider({ + Key key, + Widget child, + @required SearchBloc bloc, + }) : bloc = bloc, + super(key: key, child: child); + + @override + bool updateShouldNotify(InheritedWidget oldWidget) => true; + + static SearchBloc of(BuildContext context) => + (context.inheritFromWidgetOfExactType(SearchProvider) as SearchProvider) + .bloc; +} diff --git a/lib/utils.dart b/lib/utils.dart index 6420d29..5e25344 100644 --- a/lib/utils.dart +++ b/lib/utils.dart @@ -6,6 +6,7 @@ import 'token.dart'; import 'models/user.dart'; final prefix = 'https://api.github.com'; +final endpoint = '/graphql'; Future getWithCredentials(String url) async { final res = await http.get( @@ -13,12 +14,29 @@ Future getWithCredentials(String url) async { headers: {HttpHeaders.authorizationHeader: 'token $token'}, ); final data = json.decode(res.body); - // if (res.body.startsWith('{')) { - // throw data['message']; - // } return data; } +Future postWithCredentials(String url, String body) async { + final res = await http.post( + prefix + url, + headers: {HttpHeaders.authorizationHeader: 'token $token'}, + body: body, + ); + final data = json.decode(res.body); + return data; +} + +Future query(String query) async { + final data = + await postWithCredentials('/graphql', json.encode({'query': query})); + if (data['error'] != null) { + throw new Exception(data['error'].toString()); + } + print(data); + return data['data']; +} + Future fetchUser(String login) async { Map data = await getWithCredentials('/users/$login'); return User.fromJson(data); diff --git a/pubspec.yaml b/pubspec.yaml index ad7b368..62beca9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,7 +20,6 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 http: ^0.11.3 - graphql_flutter: ^1.0.0-alpha rxdart: ^0.20.0 dev_dependencies: