Skip to content

Commit

Permalink
Add basic list sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
xiprox committed Nov 11, 2023
1 parent f1e9734 commit 7c65b37
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 2 deletions.
5 changes: 5 additions & 0 deletions lib/data/local/preferences/schema/lists.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import 'package:aoba/data/model/aliases.dart';
import 'package:isar/isar.dart';
import 'package:aoba/widgets/lists/media_list.dart';
import 'package:aoba/data/remote/gql/schema/schema.gql.dart';

part 'lists.g.dart';

@embedded
class ListsPreferences {
@Enumerated(EnumType.name)
ListDisplayType displayType = ListDisplayType.grid;

@Enumerated(EnumType.name)
MediaListSort sort = MediaListSort.MEDIA_TITLE_ROMAJI;
}
3 changes: 3 additions & 0 deletions lib/data/model/aliases.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'package:aoba/data/remote/gql/schema/schema.gql.dart';

typedef UserTitleLanguage = Enum$UserTitleLanguage;

typedef MediaType = Enum$MediaType;
typedef MediaFormat = Enum$MediaFormat;
typedef MediaStatus = Enum$MediaStatus;

typedef ScoreFormat = Enum$ScoreFormat;
typedef MediaListStatus = Enum$MediaListStatus;
typedef MediaListSort = Enum$MediaListSort;

typedef FuzzyDateInput = Input$FuzzyDateInput;
199 changes: 199 additions & 0 deletions lib/data/model/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,202 @@ extension MediaListStatusExts on MediaListStatus {
}
}
}

extension MediaListSortExts on MediaListSort {
String get displayName {
switch (this) {
case MediaListSort.ADDED_TIME:
return 'Last Added';
case MediaListSort.ADDED_TIME_DESC:
return 'Last Added (Desc)';
case MediaListSort.FINISHED_ON:
return 'Finished On';
case MediaListSort.FINISHED_ON_DESC:
return 'Finished On (Desc)';
case MediaListSort.MEDIA_ID:
return 'Media ID';
case MediaListSort.MEDIA_ID_DESC:
return 'Media ID (Desc)';
case MediaListSort.MEDIA_POPULARITY:
return 'Popularity';
case MediaListSort.MEDIA_POPULARITY_DESC:
return 'Populairty (Desc)';
case MediaListSort.MEDIA_TITLE_ENGLISH:
return 'Title';
case MediaListSort.MEDIA_TITLE_ENGLISH_DESC:
return 'Title (Desc)';
case MediaListSort.MEDIA_TITLE_NATIVE:
return 'Title';
case MediaListSort.MEDIA_TITLE_NATIVE_DESC:
return 'Title (Desc)';
case MediaListSort.MEDIA_TITLE_ROMAJI:
return 'Title';
case MediaListSort.MEDIA_TITLE_ROMAJI_DESC:
return 'Title (Desc)';
case MediaListSort.PRIORITY:
return 'Priority';
case MediaListSort.PRIORITY_DESC:
return 'Priority (Desc)';
case MediaListSort.PROGRESS:
return 'Progress';
case MediaListSort.PROGRESS_DESC:
return 'Progress (Desc)';
case MediaListSort.PROGRESS_VOLUMES:
return 'Progress (Volumes)';
case MediaListSort.PROGRESS_VOLUMES_DESC:
return 'Progress (Volumes, Desc)';
case MediaListSort.REPEAT:
return 'Repeat';
case MediaListSort.REPEAT_DESC:
return 'Repeat (Desc)';
case MediaListSort.SCORE:
return 'Score';
case MediaListSort.SCORE_DESC:
return 'Score (Desc)';
case MediaListSort.STARTED_ON:
return 'Started On';
case MediaListSort.STARTED_ON_DESC:
return 'Started On (Desc)';
case MediaListSort.STATUS:
return 'Status';
case MediaListSort.STATUS_DESC:
return 'Status (Desc)';
case MediaListSort.UPDATED_TIME:
return 'Last Updated';
case MediaListSort.UPDATED_TIME_DESC:
return 'Last Updated (Desc)';
case MediaListSort.$unknown:
return 'Unknown';
}
}

bool get desc => name.contains('_DESC');

MediaListSort withoutOrder() {
switch (this) {
case MediaListSort.ADDED_TIME:
case MediaListSort.ADDED_TIME_DESC:
return MediaListSort.ADDED_TIME;
case MediaListSort.FINISHED_ON:
case MediaListSort.FINISHED_ON_DESC:
return MediaListSort.FINISHED_ON;
case MediaListSort.MEDIA_ID:
case MediaListSort.MEDIA_ID_DESC:
return MediaListSort.MEDIA_ID;
case MediaListSort.MEDIA_POPULARITY:
case MediaListSort.MEDIA_POPULARITY_DESC:
return MediaListSort.MEDIA_POPULARITY;
case MediaListSort.MEDIA_TITLE_ENGLISH:
case MediaListSort.MEDIA_TITLE_ENGLISH_DESC:
return MediaListSort.MEDIA_TITLE_ENGLISH;
case MediaListSort.MEDIA_TITLE_NATIVE:
case MediaListSort.MEDIA_TITLE_NATIVE_DESC:
return MediaListSort.MEDIA_TITLE_NATIVE;
case MediaListSort.MEDIA_TITLE_ROMAJI:
case MediaListSort.MEDIA_TITLE_ROMAJI_DESC:
return MediaListSort.MEDIA_TITLE_ROMAJI;
case MediaListSort.PRIORITY:
case MediaListSort.PRIORITY_DESC:
return MediaListSort.PRIORITY;
case MediaListSort.PROGRESS:
case MediaListSort.PROGRESS_DESC:
return MediaListSort.PROGRESS;
case MediaListSort.PROGRESS_VOLUMES:
case MediaListSort.PROGRESS_VOLUMES_DESC:
return MediaListSort.PROGRESS_VOLUMES;
case MediaListSort.REPEAT:
case MediaListSort.REPEAT_DESC:
return MediaListSort.REPEAT;
case MediaListSort.SCORE:
case MediaListSort.SCORE_DESC:
return MediaListSort.SCORE;
case MediaListSort.STARTED_ON:
case MediaListSort.STARTED_ON_DESC:
return MediaListSort.STARTED_ON;
case MediaListSort.STATUS:
case MediaListSort.STATUS_DESC:
return MediaListSort.STATUS;
case MediaListSort.UPDATED_TIME:
case MediaListSort.UPDATED_TIME_DESC:
return MediaListSort.UPDATED_TIME;
case MediaListSort.$unknown:
return this;
}
}

MediaListSort withOrder({required bool descending}) {
switch (this) {
case MediaListSort.ADDED_TIME:
case MediaListSort.ADDED_TIME_DESC:
return descending
? MediaListSort.ADDED_TIME_DESC
: MediaListSort.ADDED_TIME;
case MediaListSort.FINISHED_ON:
case MediaListSort.FINISHED_ON_DESC:
return descending
? MediaListSort.FINISHED_ON_DESC
: MediaListSort.FINISHED_ON;
case MediaListSort.MEDIA_ID:
case MediaListSort.MEDIA_ID_DESC:
return descending
? MediaListSort.MEDIA_ID_DESC
: MediaListSort.MEDIA_ID;
case MediaListSort.MEDIA_POPULARITY:
case MediaListSort.MEDIA_POPULARITY_DESC:
return descending
? MediaListSort.MEDIA_POPULARITY_DESC
: MediaListSort.MEDIA_POPULARITY;
case MediaListSort.MEDIA_TITLE_ENGLISH:
case MediaListSort.MEDIA_TITLE_ENGLISH_DESC:
return descending
? MediaListSort.MEDIA_TITLE_ENGLISH_DESC
: MediaListSort.MEDIA_TITLE_ENGLISH;
case MediaListSort.MEDIA_TITLE_NATIVE:
case MediaListSort.MEDIA_TITLE_NATIVE_DESC:
return descending
? MediaListSort.MEDIA_TITLE_NATIVE_DESC
: MediaListSort.MEDIA_TITLE_NATIVE;
case MediaListSort.MEDIA_TITLE_ROMAJI:
case MediaListSort.MEDIA_TITLE_ROMAJI_DESC:
return descending
? MediaListSort.MEDIA_TITLE_ROMAJI_DESC
: MediaListSort.MEDIA_TITLE_ROMAJI;
case MediaListSort.PRIORITY:
case MediaListSort.PRIORITY_DESC:
return descending
? MediaListSort.PRIORITY_DESC
: MediaListSort.PRIORITY;
case MediaListSort.PROGRESS:
case MediaListSort.PROGRESS_DESC:
return descending
? MediaListSort.PROGRESS_DESC
: MediaListSort.PROGRESS;
case MediaListSort.PROGRESS_VOLUMES:
case MediaListSort.PROGRESS_VOLUMES_DESC:
return descending
? MediaListSort.PROGRESS_VOLUMES_DESC
: MediaListSort.PROGRESS_VOLUMES;
case MediaListSort.REPEAT:
case MediaListSort.REPEAT_DESC:
return descending ? MediaListSort.REPEAT_DESC : MediaListSort.REPEAT;
case MediaListSort.SCORE:
case MediaListSort.SCORE_DESC:
return descending ? MediaListSort.SCORE_DESC : MediaListSort.SCORE;
case MediaListSort.STARTED_ON:
case MediaListSort.STARTED_ON_DESC:
return descending
? MediaListSort.STARTED_ON_DESC
: MediaListSort.STARTED_ON;
case MediaListSort.STATUS:
case MediaListSort.STATUS_DESC:
return descending ? MediaListSort.STATUS_DESC : MediaListSort.STATUS;
case MediaListSort.UPDATED_TIME:
case MediaListSort.UPDATED_TIME_DESC:
return descending
? MediaListSort.UPDATED_TIME_DESC
: MediaListSort.UPDATED_TIME;
case MediaListSort.$unknown:
return this;
}
}
}
7 changes: 7 additions & 0 deletions lib/features/lists/content/options/popup_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:aoba/widgets/popup_on_position/popup_on_position_container.dart'
import 'package:flutter/material.dart';

import 'display_type_option.dart';
import 'sort_option.dart';

class ListsOptionsPopupContent extends StatelessWidget {
static const kWidth = 240.0;
Expand Down Expand Up @@ -32,6 +33,12 @@ class ListsOptionsPopupContent extends StatelessWidget {
onChange: vm.onOptionDisplayTypeChange,
),
const SizedBox(height: 16),
SortOption(
titleSortOption: vm.titleSortOption,
value: vm.optionSort,
onChange: vm.onOptionSortChange,
),
const SizedBox(height: 16),
Material(
borderRadius: BorderRadius.circular(8),
child: const Padding(
Expand Down
91 changes: 91 additions & 0 deletions lib/features/lists/content/options/sort_option.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'package:aoba/data/model/aliases.dart';
import 'package:aoba/data/model/extensions.dart';
import 'package:flextensions/extensions/build_context.dart';
import 'package:flutter/material.dart';

const _kAvailableSortOptions = [
MediaListSort.SCORE,
MediaListSort.PROGRESS,
MediaListSort.UPDATED_TIME,
MediaListSort.ADDED_TIME,
MediaListSort.STARTED_ON,
MediaListSort.FINISHED_ON,
MediaListSort.MEDIA_POPULARITY,
MediaListSort.REPEAT,
];

class SortOption extends StatefulWidget {
final MediaListSort titleSortOption;
final MediaListSort value;
final ValueChanged<MediaListSort> onChange;

const SortOption({
super.key,
required this.titleSortOption,
required this.value,
required this.onChange,
});

@override
State<SortOption> createState() => _SortOptionState();
}

class _SortOptionState extends State<SortOption> {
late bool descending = widget.value.desc;

void _onSortChange(MediaListSort value) {
widget.onChange(value.withOrder(descending: descending));
}

@override
Widget build(BuildContext context) {
final theme = context.theme;
final inputDecorationPadding = theme.inputDecorationTheme.contentPadding
?.resolve(Directionality.of(context));
return Row(
children: [
Expanded(
child: DropdownButtonFormField<MediaListSort>(
value: widget.value.withoutOrder(),
elevation: 0,
borderRadius: BorderRadius.circular(8),
decoration: InputDecoration(
contentPadding: EdgeInsets.only(
left: 0,
right: 8,
top: inputDecorationPadding?.top ?? 16,
bottom: inputDecorationPadding?.bottom ?? 16,
),
),
items:
[widget.titleSortOption, ..._kAvailableSortOptions].map((it) {
return DropdownMenuItem<MediaListSort>(
value: it,
child: Text(it.displayName),
);
}).toList(),
onChanged: (value) {
if (value != null) {
_onSortChange(value);
}
},
),
),
const SizedBox(width: 8),
IconButton.outlined(
onPressed: () {
// No need to set state as the widget will be rebuilt due to [value]
// change.
descending = !descending;
_onSortChange(widget.value);
},
icon: Icon(
descending
? Icons.arrow_downward_rounded
: Icons.arrow_upward_rounded,
),
),
],
);
}
}
7 changes: 5 additions & 2 deletions lib/features/lists/data/lists.gql
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ fragment mediaListEntry on MediaList {
}
}

query FetchData($userId: Int, $type: MediaType) {
MediaListCollection(userId: $userId, sort: [SCORE_DESC], type: $type, forceSingleCompletedList: true) {
query FetchData($userId: Int, $type: MediaType, $sort: [MediaListSort]) {
MediaListCollection(userId: $userId, sort: $sort, type: $type, forceSingleCompletedList: true) {
lists {
name
isCustomList
Expand All @@ -47,6 +47,9 @@ query FetchData($userId: Int, $type: MediaType) {
}
user {
name
options {
titleLanguage
}
mediaListOptions {
scoreFormat
rowOrder
Expand Down
3 changes: 3 additions & 0 deletions lib/features/lists/data/lists_repo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ abstract class ListsRepo {
Future<Resource<ListsData>> getData({
required int userId,
required MediaType type,
List<MediaListSort>? sort,
bool forceNetwork,
});
}
Expand All @@ -53,6 +54,7 @@ class ListsRepoImpl implements ListsRepo {
Future<Resource<ListsData>> getData({
required int userId,
required MediaType type,
List<MediaListSort>? sort,
bool forceNetwork = false,
}) async {
final result = await GqlRequest.query(
Expand All @@ -61,6 +63,7 @@ class ListsRepoImpl implements ListsRepo {
variables: {
'userId': userId,
'type': type.name,
'sort': sort?.map((it) => it.name).toList(),
},
fetchPolicy: forceNetwork
? FetchPolicy.networkOnly
Expand Down

0 comments on commit 7c65b37

Please sign in to comment.