Skip to content

Commit

Permalink
Refactor code search page controller
Browse files Browse the repository at this point in the history
  • Loading branch information
up2code committed Jan 13, 2021
1 parent 55b66c8 commit 3e50c1d
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 123 deletions.
2 changes: 2 additions & 0 deletions lib/controllers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ library controllers;

export 'src/controllers/album_detail_controller.dart';
export 'src/controllers/album_search_controller.dart';
export 'src/controllers/app_page_controller.dart';
export 'src/controllers/artist_detail_controller.dart';
export 'src/controllers/artist_search_controller.dart';
export 'src/controllers/entry_search_controller.dart';
Expand All @@ -11,6 +12,7 @@ export 'src/controllers/favorite_song_controller.dart';
export 'src/controllers/main_page_controller.dart';
export 'src/controllers/release_event_detail_controller.dart';
export 'src/controllers/release_event_search_controller.dart';
export 'src/controllers/search_page_controller.dart';
export 'src/controllers/song_detail_controller.dart';
export 'src/controllers/login_page_controller.dart';
export 'src/controllers/home_page_controller.dart';
Expand Down
15 changes: 15 additions & 0 deletions lib/src/controllers/app_page_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:get/get.dart';
import 'package:vocadb_app/utils.dart';

class AppPageController extends GetxController {
final initialLoading = true.obs;

final errorMessage = ''.obs;

void initialLoadingDone(_) => initialLoading(false);

void onError(Object err) {
initialLoading(false);
errorMessage(ErrorUtils.read(err));
}
}
61 changes: 7 additions & 54 deletions lib/src/controllers/artist_search_controller.dart
Original file line number Diff line number Diff line change
@@ -1,85 +1,38 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:vocadb_app/controllers.dart';
import 'package:vocadb_app/models.dart';
import 'package:vocadb_app/repositories.dart';
import 'package:vocadb_app/services.dart';

class ArtistSearchController extends GetxController {
final int maxResults = 50;

final initialLoading = true.obs;

/// List of results from search
final results = <ArtistModel>[].obs;

/// Query input string
final query = ''.obs;

/// Set to True when user tap search icon.
final openQuery = false.obs;

class ArtistSearchController extends SearchPageController<ArtistModel> {
/// Filter parameter
final artistType = ''.obs;

final sort = 'Name'.obs;

final tags = <TagModel>[].obs;

/// If set to [True], no fetch more data from server. Default is [False].
final noFetchMore = false.obs;

final ArtistRepository artistRepository;

TextEditingController textSearchController;

ArtistSearchController({this.artistRepository});

@override
void onInit() {
initialFetch();
[artistType, sort, tags]
.forEach((element) => ever(element, (_) => initialFetch()));
debounce(query, (_) => initialFetch(), time: Duration(seconds: 1));
textSearchController = TextEditingController();
super.onInit();
}

void initialFetch() {
Future.value(noFetchMore(false))
.then((_) => fetchApi())
.then(verifyShouldFetchMore)
.then(results)
.then(initialLoadingDone);
}

Future<List<ArtistModel>> fetchApi({int start}) =>
artistRepository.findArtists(
@override
Future<List<ArtistModel>> fetchApi({int start}) => artistRepository
.findArtists(
start: (start == null) ? 0 : start,
lang: SharedPreferenceService.lang,
maxResults: maxResults,
query: query.string,
artistType: artistType.string,
sort: sort.string,
tagIds: tags.toList().map((e) => e.id).join(','),
);

clearQuery() {
query('');
textSearchController.clear();
}

initialLoadingDone(_) => initialLoading(false);

List<ArtistModel> verifyShouldFetchMore(List<ArtistModel> items) {
if (items == null || items.isEmpty || items.length < maxResults)
noFetchMore(true);
return items;
}

onReachLastItem() {
if (noFetchMore.value) return;
fetchApi(start: results.length + 1)
.then(verifyShouldFetchMore)
.then(results.addAll);
}
)
.catchError(super.onError);
}
57 changes: 57 additions & 0 deletions lib/src/controllers/search_page_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:vocadb_app/controllers.dart';
import 'package:get/get.dart';

abstract class SearchPageController<T> extends AppPageController {
final int maxResults = 50;

/// List of results from search
final results = <T>[].obs;

/// Query input string
final query = ''.obs;

/// Set to True when user tap search icon.
final openQuery = false.obs;

/// If set to [True], no fetch more data from server. Default is [False].
final noFetchMore = false.obs;

TextEditingController textSearchController;

@override
void onInit() {
initialFetch();
debounce(query, (_) => initialFetch(), time: Duration(seconds: 1));
textSearchController = TextEditingController();
super.onInit();
}

void initialFetch() {
Future.value(noFetchMore(false))
.then((_) => fetchApi())
.then(verifyShouldFetchMore)
.then(results)
.then(initialLoadingDone);
}

void clearQuery() {
query('');
textSearchController.clear();
}

Future<List<T>> fetchApi({int start});

List<T> verifyShouldFetchMore(List<T> items) {
if (items == null || items.isEmpty || items.length < maxResults)
noFetchMore(true);
return items;
}

onReachLastItem() {
if (noFetchMore.value) return;
fetchApi(start: results.length + 1)
.then(verifyShouldFetchMore)
.then(results.addAll);
}
}
71 changes: 13 additions & 58 deletions lib/src/controllers/song_search_controller.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:vocadb_app/controllers.dart';
import 'package:vocadb_app/models.dart';
import 'package:vocadb_app/repositories.dart';
import 'package:vocadb_app/services.dart';

class SongSearchController extends GetxController {
final int maxResults = 50;

final initialLoading = true.obs;

/// List of results from search
final results = <SongModel>[].obs;

/// Query input string
final query = ''.obs;

/// Set to True when user tap search icon.
final openQuery = false.obs;

class SongSearchController extends SearchPageController<SongModel> {
/// Filter parameter
final songType = ''.obs;

Expand All @@ -30,59 +18,26 @@ class SongSearchController extends GetxController {
/// Filter parameter
final tags = <TagModel>[].obs;

/// If set to [True], no fetch more data from server. Default is [False].
final noFetchMore = false.obs;

final SongRepository songRepository;

TextEditingController textSearchController;

SongSearchController({this.songRepository});

@override
void onInit() {
initialFetch();
[songType, sort, artists, tags]
.forEach((element) => ever(element, (_) => initialFetch()));
debounce(query, (_) => initialFetch(), time: Duration(seconds: 1));
textSearchController = TextEditingController();
super.onInit();
}

void initialFetch() {
Future.value(noFetchMore(false))
.then((_) => fetchApi())
.then(verifyShouldFetchMore)
.then(results)
.then(initialLoadingDone);
}

Future<List<SongModel>> fetchApi({int start}) => songRepository.findSongs(
start: (start == null) ? 0 : start,
lang: SharedPreferenceService.lang,
query: query.string,
songType: songType.string,
sort: sort.string,
artistIds: artists.toList().map((e) => e.id).join(','),
tagIds: tags.toList().map((e) => e.id).join(','));

clearQuery() {
query('');
textSearchController.clear();
}

initialLoadingDone(_) => initialLoading(false);

List<SongModel> verifyShouldFetchMore(List<SongModel> items) {
if (items == null || items.isEmpty || items.length < maxResults)
noFetchMore(true);
return items;
}

onReachLastItem() {
if (noFetchMore.value) return;
fetchApi(start: results.length + 1)
.then(verifyShouldFetchMore)
.then(results.addAll);
}
@override
Future<List<SongModel>> fetchApi({int start}) => songRepository
.findSongs(
start: (start == null) ? 0 : start,
lang: SharedPreferenceService.lang,
query: query.string,
songType: songType.string,
sort: sort.string,
artistIds: artists.toList().map((e) => e.id).join(','),
tagIds: tags.toList().map((e) => e.id).join(','))
.catchError(super.onError);
}
12 changes: 7 additions & 5 deletions lib/src/pages/artist_search_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,13 @@ class ArtistSearchPage extends GetView<ArtistSearchController> {
body: Obx(
() => (controller.initialLoading.value)
? CenterLoading()
: ArtistListView(
artists: controller.results.toList(),
onSelect: this._onSelectArtist,
onReachLastItem: controller.onReachLastItem,
),
: (controller.errorMessage.string.isNotEmpty)
? CenterText(controller.errorMessage.string)
: ArtistListView(
artists: controller.results.toList(),
onSelect: this._onSelectArtist,
onReachLastItem: controller.onReachLastItem,
emptyWidget: CenterText('searchResultNotMatched'.tr)),
));
}
}
13 changes: 8 additions & 5 deletions lib/src/pages/song_search_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,14 @@ class SongSearchPage extends GetView<SongSearchController> {
body: Obx(
() => (controller.initialLoading.value)
? CenterLoading()
: SongListView(
songs: controller.results.toList(),
onSelect: this._onSelect,
onReachLastItem: controller.onReachLastItem,
),
: (controller.errorMessage.string.isNotEmpty)
? CenterText(controller.errorMessage.string)
: SongListView(
songs: controller.results(),
onSelect: this._onSelect,
onReachLastItem: controller.onReachLastItem,
emptyWidget: CenterText('searchResultNotMatched'.tr),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => AppPages.openPVPlayListPage(controller.results(),
Expand Down
29 changes: 29 additions & 0 deletions lib/src/utils/error_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'dart:io';

import 'package:dio/dio.dart';

class ErrorUtils {
static String read(Object err) {
if (err is DioError) {
return readDioError(err);
}

return err.toString();
}

static String readDioError(DioError err) {
if (err.type == DioErrorType.DEFAULT) {
return readDynamicError(err.error);
}

return err.message;
}

static String readDynamicError(dynamic err) {
if (err is SocketException) {
return err.message;
}

return err.toString();
}
}
14 changes: 13 additions & 1 deletion lib/src/widgets/artist_list_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import 'package:vocadb_app/widgets.dart';

/// A widget for display list of artists
class ArtistListView extends StatelessWidget {
ArtistListView({Key key, this.artists, this.onSelect, this.onReachLastItem})
ArtistListView(
{Key key,
this.artists,
this.onSelect,
this.onReachLastItem,
this.emptyWidget})
: super(key: key);

/// List of artists to display.
Expand All @@ -15,8 +20,15 @@ class ArtistListView extends StatelessWidget {

final Function(ArtistModel) onSelect;

/// A widget that display when songs is empty
final Widget emptyWidget;

@override
Widget build(BuildContext context) {
if (this.artists.isEmpty && this.emptyWidget != null) {
return emptyWidget;
}

return InfiniteListView(
itemCount: this.artists.length,
itemBuilder: (context, index) => ArtistTile.fromEntry(this.artists[index],
Expand Down
Loading

0 comments on commit 3e50c1d

Please sign in to comment.