Skip to content

Commit fb13630

Browse files
authored
Future to Single (#18)
* `Future` to `Single` * codecov * remove flutter_github_search_rx_redux
1 parent b471384 commit fb13630

19 files changed

+674
-332
lines changed

.github/workflows/flutter.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ jobs:
4848
- name: Gen code
4949
run: flutter packages pub run build_runner build --delete-conflicting-outputs
5050

51+
- name: Run tests
52+
run: flutter test --coverage --coverage-path=coverage.lcov
53+
54+
- name: Upload coverage to Codecov
55+
uses: codecov/codecov-action@v3
56+
5157
- name: Build APK
5258
run: flutter build apk --no-shrink
5359

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import 'dart:ui';
22

33
import 'package:built_collection/built_collection.dart';
4+
import 'package:rxdart_ext/single.dart';
45

56
abstract class ColorRemoteSource {
6-
Future<BuiltMap<String, Color>> getColors();
7+
Single<BuiltMap<String, Color>> getColors();
78
}

lib/data/remote/color_remote_source_impl.dart

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,48 @@
1-
import 'dart:convert';
2-
import 'dart:io';
31
import 'dart:ui';
42

53
import 'package:built_collection/built_collection.dart';
6-
import 'package:http/http.dart' as http;
4+
import 'package:http_client_hoc081098/http_client_hoc081098.dart';
75
import 'package:meta/meta.dart';
6+
import 'package:rxdart_ext/rxdart_ext.dart';
87

98
import 'color_remote_source.dart';
109

1110
class ColorRemoteSourceImpl implements ColorRemoteSource {
12-
final http.Client _client;
11+
final SimpleHttpClient _client;
1312
final Uri _url;
1413

1514
BuiltMap<String, Color>? _cachedColors;
1615

1716
ColorRemoteSourceImpl(this._client, this._url);
1817

1918
@override
20-
Future<BuiltMap<String, Color>> getColors() async {
21-
if (_cachedColors != null) {
22-
return _cachedColors!;
23-
}
24-
25-
final response = await _client.get(_url);
26-
27-
if (response.statusCode != HttpStatus.ok) {
28-
throw HttpException(
29-
'Get colors failed failed with status code: ${response.statusCode}',
30-
uri: _url,
31-
);
32-
}
33-
34-
return _cachedColors =
35-
((jsonDecode(response.body) as Map).map((key, value) {
36-
final color = value['color'];
37-
return MapEntry(
38-
key as String,
39-
color is String ? colorFromHex(color) : null,
40-
);
41-
})
42-
..removeWhere((key, value) => value == null))
43-
.cast<String, Color>()
44-
.build();
19+
Single<BuiltMap<String, Color>> getColors() {
20+
return Single.defer(() {
21+
if (_cachedColors != null) {
22+
return Single.value(_cachedColors!);
23+
}
24+
25+
return useCancellationToken((cancelToken) async {
26+
final json = await _client.getJson(_url, cancelToken: cancelToken)
27+
as Map<String, dynamic>;
28+
29+
return _cachedColors = _toColorMap(json);
30+
});
31+
});
4532
}
4633

34+
static BuiltMap<String, Color> _toColorMap(Map<String, dynamic> json) =>
35+
(json.map((key, value) {
36+
final color = value['color'];
37+
return MapEntry(
38+
key,
39+
color is String ? colorFromHex(color) : null,
40+
);
41+
})
42+
..removeWhere((key, value) => value == null))
43+
.cast<String, Color>()
44+
.build();
45+
4746
@visibleForTesting
4847
static Color colorFromHex(String hex) {
4948
hex = hex.replaceAll('#', '');
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import 'package:rxdart_ext/single.dart';
2+
13
import 'search_result.dart';
24

35
abstract class SearchRemoteSource {
4-
Future<SearchResult> search(String term, int page);
6+
Single<SearchResult> search(String term, int page);
57
}
Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,28 @@
1-
import 'dart:io';
2-
31
import 'package:flutter_github_search_rx_redux/data/remote/search_result.dart';
4-
import 'package:http/http.dart' as http;
2+
import 'package:http_client_hoc081098/http_client_hoc081098.dart';
3+
import 'package:rxdart_ext/rxdart_ext.dart';
54

65
import 'search_remote_source.dart';
76

87
class SearchRemoteSourceImpl implements SearchRemoteSource {
9-
final http.Client _client;
8+
final SimpleHttpClient _client;
109
final String _baseUrl;
1110

1211
SearchRemoteSourceImpl(this._client, this._baseUrl);
1312

1413
@override
15-
Future<SearchResult> search(String term, int page) async {
16-
final url = Uri.https(
17-
_baseUrl,
18-
'search/repositories',
19-
{'q': term, 'page': page.toString()},
20-
);
21-
print('SearchRemoteSourceImpl: ${url.queryParameters}');
22-
final response = await _client.get(url);
14+
Single<SearchResult> search(String term, int page) =>
15+
useCancellationToken((cancelToken) async {
16+
final url = Uri.https(
17+
_baseUrl,
18+
'search/repositories',
19+
{'q': term, 'page': page.toString()},
20+
);
21+
print('SearchRemoteSourceImpl: ${url.queryParameters}');
2322

24-
if (response.statusCode != HttpStatus.ok) {
25-
throw HttpException(
26-
'Search for term: $term and page: $page failed with status code: ${response.statusCode}, response body: ${response.body}',
27-
uri: url,
28-
);
29-
}
23+
final json = await _client.getJson(url, cancelToken: cancelToken)
24+
as Map<String, dynamic>;
3025

31-
return SearchResult.fromRawJson(response.body);
32-
}
26+
return SearchResult.fromJson(json);
27+
});
3328
}

lib/data/search_repo_impl.dart

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:ui';
33
import 'package:built_collection/built_collection.dart';
44
import 'package:flutter_github_search_rx_redux/data/remote/color_remote_source.dart';
55
import 'package:flutter_github_search_rx_redux/data/remote/search_result.dart';
6+
import 'package:rxdart_ext/single.dart';
67

78
import '../domain/repo_item.dart';
89
import '../domain/search_repo.dart';
@@ -21,18 +22,13 @@ class SearchRepositoryImpl implements SearchRepository {
2122
);
2223

2324
@override
24-
Future<BuiltList<RepoItem>> searchBy({
25+
Single<BuiltList<RepoItem>> searchBy({
2526
required String term,
2627
required int page,
27-
}) {
28-
return Future.wait([
29-
_searchRemoteSource.search(term, page),
30-
_colorRemoteSource.getColors(),
31-
]).then(
32-
(values) => _searchResultToRepoItems(
33-
values[0] as SearchResult,
34-
values[1] as BuiltMap<String, Color>,
35-
),
36-
);
37-
}
28+
}) =>
29+
RxSingles.forkJoin2(
30+
_searchRemoteSource.search(term, page),
31+
_colorRemoteSource.getColors(),
32+
_searchResultToRepoItems,
33+
);
3834
}

lib/domain/search_repo.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import 'dart:async';
2-
31
import 'package:built_collection/built_collection.dart';
2+
import 'package:rxdart_ext/single.dart';
43

54
import 'repo_item.dart';
65

76
abstract class SearchRepository {
8-
Future<BuiltList<RepoItem>> searchBy({
7+
Single<BuiltList<RepoItem>> searchBy({
98
required String term,
109
required int page,
1110
});

lib/domain/search_usecase.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import 'dart:async';
2-
31
import 'package:built_collection/built_collection.dart';
42
import 'package:flutter_github_search_rx_redux/domain/search_repo.dart';
3+
import 'package:rxdart_ext/single.dart';
54

65
import 'repo_item.dart';
76

@@ -10,7 +9,7 @@ class SearchUseCase {
109

1110
SearchUseCase(this._repository);
1211

13-
Future<BuiltList<RepoItem>> call({
12+
Single<BuiltList<RepoItem>> call({
1413
required String term,
1514
required int page,
1615
}) =>

lib/main.dart

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import 'dart:io';
2+
3+
import 'package:cupertino_http/cupertino_client.dart';
4+
import 'package:flutter/foundation.dart';
15
import 'package:flutter/material.dart';
26
import 'package:flutter_github_search_rx_redux/data/remote/color_remote_source_impl.dart';
37
import 'package:flutter_github_search_rx_redux/data/remote/search_remote_source_impl.dart';
@@ -6,12 +10,34 @@ import 'package:flutter_github_search_rx_redux/domain/search_repo.dart';
610
import 'package:flutter_github_search_rx_redux/domain/search_usecase.dart';
711
import 'package:flutter_provider/flutter_provider.dart';
812
import 'package:http/http.dart' as http;
13+
import 'package:http_client_hoc081098/http_client_hoc081098.dart';
914

1015
import 'data/remote/mapper.dart';
1116
import 'ui/home/home_page.dart';
1217

1318
void main() {
14-
final client = http.Client();
19+
final loggingInterceptor = SimpleLoggingInterceptor(
20+
SimpleLogger(
21+
loggerFunction: print,
22+
level: kReleaseMode ? SimpleLogLevel.none : SimpleLogLevel.body,
23+
),
24+
);
25+
26+
final client = SimpleHttpClient(
27+
client: Platform.isIOS || Platform.isMacOS
28+
? CupertinoClient.defaultSessionConfiguration()
29+
: http.Client(),
30+
timeout: const Duration(seconds: 20),
31+
requestInterceptors: [
32+
// others interceptors above this line
33+
loggingInterceptor.requestInterceptor,
34+
],
35+
responseInterceptors: [
36+
loggingInterceptor.responseInterceptor,
37+
// others interceptors below this line
38+
],
39+
);
40+
1541
const searchBaseUrl = 'api.github.com';
1642
final colorUrl =
1743
Uri.parse('https://github.com/ozh/github-colors/raw/master/colors.json');

lib/ui/home/home_store.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class HomeSideEffects {
8787
..term = term
8888
..nextPage = nextPage);
8989

90-
return Rx.fromCallable(() => _searchUseCase(term: term, page: nextPage))
90+
return _searchUseCase(term: term, page: nextPage)
9191
.map<HomeAction>(
9292
(items) => SearchSuccessAction((b) => b
9393
..term = term

0 commit comments

Comments
 (0)