refactored room and file sorting to use direct comparators instead of temporary sort strings, removed obsolete 'sort' properties from API models, and improved file list sorting with case-insensitive name comparisons and null-safe date handling
This commit is contained in:
@@ -16,26 +16,20 @@ class GetRoomResponse extends ApiResponse {
|
|||||||
Map<String, dynamic> toJson() => _$GetRoomResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetRoomResponseToJson(this);
|
||||||
|
|
||||||
List<GetRoomResponseObject> sortBy({
|
List<GetRoomResponseObject> sortBy({
|
||||||
bool lastActivity = true,
|
|
||||||
required bool favoritesToTop,
|
required bool favoritesToTop,
|
||||||
required bool unreadToTop,
|
required bool unreadToTop,
|
||||||
}) {
|
}) {
|
||||||
for (var chat in data) {
|
return data.toList()..sort((a, b) {
|
||||||
final buffer = StringBuffer();
|
if (favoritesToTop && a.isFavorite != b.isFavorite) {
|
||||||
|
return a.isFavorite ? -1 : 1;
|
||||||
if (favoritesToTop) {
|
|
||||||
buffer.write(chat.isFavorite ? 'b' : 'a');
|
|
||||||
}
|
}
|
||||||
if (unreadToTop) {
|
if (unreadToTop) {
|
||||||
buffer.write(chat.unreadMessages > 0 ? 'b' : 'a');
|
final aUnread = a.unreadMessages > 0;
|
||||||
|
final bUnread = b.unreadMessages > 0;
|
||||||
|
if (aUnread != bUnread) return aUnread ? -1 : 1;
|
||||||
}
|
}
|
||||||
|
return b.lastActivity.compareTo(a.lastActivity);
|
||||||
buffer.write(chat.lastActivity);
|
});
|
||||||
|
|
||||||
chat.sort = buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.toList()..sort((a, b) => b.sort!.compareTo(a.sort!));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +65,6 @@ class GetRoomResponseObject {
|
|||||||
String? status;
|
String? status;
|
||||||
String? statusIcon;
|
String? statusIcon;
|
||||||
String? statusMessage;
|
String? statusMessage;
|
||||||
String? sort;
|
|
||||||
|
|
||||||
GetRoomResponseObject(
|
GetRoomResponseObject(
|
||||||
this.id,
|
this.id,
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ GetRoomResponseObject _$GetRoomResponseObjectFromJson(
|
|||||||
json['status'] as String?,
|
json['status'] as String?,
|
||||||
json['statusIcon'] as String?,
|
json['statusIcon'] as String?,
|
||||||
json['statusMessage'] as String?,
|
json['statusMessage'] as String?,
|
||||||
)..sort = json['sort'] as String?;
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$GetRoomResponseObjectToJson(
|
Map<String, dynamic> _$GetRoomResponseObjectToJson(
|
||||||
GetRoomResponseObject instance,
|
GetRoomResponseObject instance,
|
||||||
@@ -97,7 +97,6 @@ Map<String, dynamic> _$GetRoomResponseObjectToJson(
|
|||||||
'status': instance.status,
|
'status': instance.status,
|
||||||
'statusIcon': instance.statusIcon,
|
'statusIcon': instance.statusIcon,
|
||||||
'statusMessage': instance.statusMessage,
|
'statusMessage': instance.statusMessage,
|
||||||
'sort': instance.sort,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$GetRoomResponseObjectConversationTypeEnumMap = {
|
const _$GetRoomResponseObjectConversationTypeEnumMap = {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ class CacheableFile {
|
|||||||
String? eTag;
|
String? eTag;
|
||||||
DateTime? createdAt;
|
DateTime? createdAt;
|
||||||
DateTime? modifiedAt;
|
DateTime? modifiedAt;
|
||||||
String? sort;
|
|
||||||
|
|
||||||
/// Nextcloud's instance-local file id (`oc:fileid`). Used to address the
|
/// Nextcloud's instance-local file id (`oc:fileid`). Used to address the
|
||||||
/// preview API by id, which is more reliable than the path-based variant
|
/// preview API by id, which is more reliable than the path-based variant
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ CacheableFile _$CacheableFileFromJson(Map<String, dynamic> json) =>
|
|||||||
: DateTime.parse(json['modifiedAt'] as String),
|
: DateTime.parse(json['modifiedAt'] as String),
|
||||||
fileId: (json['fileId'] as num?)?.toInt(),
|
fileId: (json['fileId'] as num?)?.toInt(),
|
||||||
hasPreview: json['hasPreview'] as bool?,
|
hasPreview: json['hasPreview'] as bool?,
|
||||||
)..sort = json['sort'] as String?;
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$CacheableFileToJson(CacheableFile instance) =>
|
Map<String, dynamic> _$CacheableFileToJson(CacheableFile instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
@@ -34,7 +34,6 @@ Map<String, dynamic> _$CacheableFileToJson(CacheableFile instance) =>
|
|||||||
'eTag': instance.eTag,
|
'eTag': instance.eTag,
|
||||||
'createdAt': instance.createdAt?.toIso8601String(),
|
'createdAt': instance.createdAt?.toIso8601String(),
|
||||||
'modifiedAt': instance.modifiedAt?.toIso8601String(),
|
'modifiedAt': instance.modifiedAt?.toIso8601String(),
|
||||||
'sort': instance.sort,
|
|
||||||
'fileId': instance.fileId,
|
'fileId': instance.fileId,
|
||||||
'hasPreview': instance.hasPreview,
|
'hasPreview': instance.hasPreview,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:jiffy/jiffy.dart';
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
import '../../../../../view/pages/files/data/sort_options.dart';
|
import '../../../../../view/pages/files/data/sort_options.dart';
|
||||||
@@ -22,60 +21,19 @@ class ListFilesResponse extends ApiResponse {
|
|||||||
SortOption sortOption = SortOption.name,
|
SortOption sortOption = SortOption.name,
|
||||||
bool reversed = false,
|
bool reversed = false,
|
||||||
}) {
|
}) {
|
||||||
var list = List<CacheableFile>.empty(growable: true);
|
final ascending = SortOptions.getOption(sortOption).compare;
|
||||||
|
// `reversed=true` means user picked "Aufsteigend" (a→z, oldest→newest,
|
||||||
|
// smallest→largest); default is descending.
|
||||||
|
final compare = reversed
|
||||||
|
? ascending
|
||||||
|
: (CacheableFile a, CacheableFile b) => ascending(b, a);
|
||||||
|
|
||||||
if (foldersToTop) {
|
if (!foldersToTop) {
|
||||||
list.addAll(
|
return files.toList()..sort(compare);
|
||||||
_sort(
|
|
||||||
files.where((element) => element.isDirectory).toSet(),
|
|
||||||
reversed: reversed,
|
|
||||||
sortOption: sortOption,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
list.addAll(
|
|
||||||
_sort(
|
|
||||||
files.where((element) => !element.isDirectory).toSet(),
|
|
||||||
reversed: reversed,
|
|
||||||
sortOption: sortOption,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
list.addAll(_sort(files, reversed: reversed, sortOption: sortOption));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
final folders = files.where((f) => f.isDirectory).toList()..sort(compare);
|
||||||
}
|
final regular = files.where((f) => !f.isDirectory).toList()..sort(compare);
|
||||||
|
return [...folders, ...regular];
|
||||||
List<CacheableFile> _sort(
|
|
||||||
Set<CacheableFile> files, {
|
|
||||||
SortOption sortOption = SortOption.name,
|
|
||||||
bool reversed = false,
|
|
||||||
}) {
|
|
||||||
for (var file in files) {
|
|
||||||
final buffer = StringBuffer();
|
|
||||||
|
|
||||||
switch (sortOption) {
|
|
||||||
case SortOption.date:
|
|
||||||
buffer.write(
|
|
||||||
Jiffy.parseFromMillisecondsSinceEpoch(
|
|
||||||
file.modifiedAt?.millisecondsSinceEpoch ?? 0,
|
|
||||||
).format(pattern: 'yyyyMMddhhmmss'),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SortOption.name:
|
|
||||||
buffer.write(file.name.toLowerCase());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SortOption.size:
|
|
||||||
buffer.write(file.size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.sort = buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = files.toList()..sort((a, b) => b.sort!.compareTo(a.sort!));
|
|
||||||
return reversed ? list.reversed.toList() : list;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,16 @@ class SortOptions {
|
|||||||
SortOption.name: BetterSortOption(
|
SortOption.name: BetterSortOption(
|
||||||
displayName: 'Name',
|
displayName: 'Name',
|
||||||
icon: Icons.sort_by_alpha_outlined,
|
icon: Icons.sort_by_alpha_outlined,
|
||||||
compare: (a, b) => a.name.compareTo(b.name),
|
compare: (a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()),
|
||||||
),
|
),
|
||||||
SortOption.date: BetterSortOption(
|
SortOption.date: BetterSortOption(
|
||||||
displayName: 'Datum',
|
displayName: 'Datum',
|
||||||
icon: Icons.history_outlined,
|
icon: Icons.history_outlined,
|
||||||
compare: (a, b) => a.modifiedAt!.compareTo(b.modifiedAt!),
|
compare: (a, b) {
|
||||||
|
final aMs = a.modifiedAt?.millisecondsSinceEpoch ?? 0;
|
||||||
|
final bMs = b.modifiedAt?.millisecondsSinceEpoch ?? 0;
|
||||||
|
return aMs.compareTo(bMs);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
SortOption.size: BetterSortOption(
|
SortOption.size: BetterSortOption(
|
||||||
displayName: 'Größe',
|
displayName: 'Größe',
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ class ShareChatPicker extends StatelessWidget {
|
|||||||
if (rooms == null) return const SizedBox.shrink();
|
if (rooms == null) return const SizedBox.shrink();
|
||||||
final sorted = rooms
|
final sorted = rooms
|
||||||
.sortBy(
|
.sortBy(
|
||||||
lastActivity: true,
|
|
||||||
favoritesToTop: talkSettings.sortFavoritesToTop,
|
favoritesToTop: talkSettings.sortFavoritesToTop,
|
||||||
unreadToTop: talkSettings.sortUnreadToTop,
|
unreadToTop: talkSettings.sortUnreadToTop,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ class _ChatListViewState extends State<_ChatListView> {
|
|||||||
.val()
|
.val()
|
||||||
.talkSettings;
|
.talkSettings;
|
||||||
final sorted = rooms.sortBy(
|
final sorted = rooms.sortBy(
|
||||||
lastActivity: true,
|
|
||||||
favoritesToTop: talkSettings.sortFavoritesToTop,
|
favoritesToTop: talkSettings.sortFavoritesToTop,
|
||||||
unreadToTop: talkSettings.sortUnreadToTop,
|
unreadToTop: talkSettings.sortUnreadToTop,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user