#42 navigation menu customization
This commit is contained in:
parent
690ebce80e
commit
8669f34bad
21 changed files with 720 additions and 204 deletions
|
@ -24,12 +24,20 @@
|
|||
"@hideButtonLabel": {},
|
||||
"continueButtonLabel": "CONTINUE",
|
||||
"@continueButtonLabel": {},
|
||||
"changeTooltip": "Change",
|
||||
"@changeTooltip": {},
|
||||
"clearTooltip": "Clear",
|
||||
"@clearTooltip": {},
|
||||
"previousTooltip": "Previous",
|
||||
"@previousTooltip": {},
|
||||
"nextTooltip": "Next",
|
||||
"@nextTooltip": {},
|
||||
"showTooltip": "Show",
|
||||
"@showTooltip": {},
|
||||
"hideTooltip": "Hide",
|
||||
"@hideTooltip": {},
|
||||
"removeTooltip": "Remove",
|
||||
"@removeTooltip": {},
|
||||
|
||||
"doubleBackExitMessage": "Tap “back” again to exit.",
|
||||
"@doubleBackExitMessage": {},
|
||||
|
@ -476,10 +484,18 @@
|
|||
|
||||
"drawerCollectionAll": "All collection",
|
||||
"@drawerCollectionAll": {},
|
||||
"drawerCollectionVideos": "Videos",
|
||||
"@drawerCollectionVideos": {},
|
||||
"drawerCollectionFavourites": "Favourites",
|
||||
"@drawerCollectionFavourites": {},
|
||||
"drawerCollectionImages": "Images",
|
||||
"@drawerCollectionImages": {},
|
||||
"drawerCollectionVideos": "Videos",
|
||||
"@drawerCollectionVideos": {},
|
||||
"drawerCollectionMotionPhotos": "Motion photos",
|
||||
"@drawerCollectionMotionPhotos": {},
|
||||
"drawerCollectionPanoramas": "Panoramas",
|
||||
"@drawerCollectionPanoramas": {},
|
||||
"drawerCollectionSphericalVideos": "360° Videos",
|
||||
"@drawerCollectionSphericalVideos": {},
|
||||
|
||||
"chipSortTitle": "Sort",
|
||||
"@chipSortTitle": {},
|
||||
|
@ -505,6 +521,8 @@
|
|||
"@albumPickPageTitleExport": {},
|
||||
"albumPickPageTitleMove": "Move to Album",
|
||||
"@albumPickPageTitleMove": {},
|
||||
"albumPickPageTitlePick": "Pick Album",
|
||||
"@albumPickPageTitlePick": {},
|
||||
|
||||
"albumCamera": "Camera",
|
||||
"@albumCamera": {},
|
||||
|
@ -572,6 +590,21 @@
|
|||
"settingsDoubleBackExit": "Tap “back” twice to exit",
|
||||
"@settingsDoubleBackExit": {},
|
||||
|
||||
"settingsNavigationDrawerTile": "Navigation menu",
|
||||
"@settingsNavigationDrawerTile": {},
|
||||
"settingsNavigationDrawerEditorTitle": "Navigation Menu",
|
||||
"@settingsNavigationDrawerEditorTitle": {},
|
||||
"settingsNavigationDrawerBanner": "Touch and hold to move and reorder menu items.",
|
||||
"@settingsNavigationDrawerBanner": {},
|
||||
"settingsNavigationDrawerTabTypes": "Types",
|
||||
"@settingsNavigationDrawerTabTypes": {},
|
||||
"settingsNavigationDrawerTabAlbums": "Albums",
|
||||
"@settingsNavigationDrawerTabAlbums": {},
|
||||
"settingsNavigationDrawerTabPages": "Pages",
|
||||
"@settingsNavigationDrawerTabPages": {},
|
||||
"settingsNavigationDrawerAddAlbum": "Add album",
|
||||
"@settingsNavigationDrawerAddAlbum": {},
|
||||
|
||||
"settingsSectionThumbnails": "Thumbnails",
|
||||
"@settingsSectionThumbnails": {},
|
||||
"settingsThumbnailShowLocationIcon": "Show location icon",
|
||||
|
@ -683,8 +716,6 @@
|
|||
"@settingsHiddenPathsBanner": {},
|
||||
"settingsHiddenPathsEmpty": "No hidden paths",
|
||||
"@settingsHiddenPathsEmpty": {},
|
||||
"settingsHiddenPathsRemoveTooltip": "Remove",
|
||||
"@settingsHiddenPathsRemoveTooltip": {},
|
||||
"addPathTooltip": "Add path",
|
||||
"@addPathTooltip": {},
|
||||
|
||||
|
|
|
@ -10,9 +10,13 @@
|
|||
"showButtonLabel": "보기",
|
||||
"hideButtonLabel": "숨기기",
|
||||
"continueButtonLabel": "다음",
|
||||
"changeTooltip": "변경",
|
||||
"clearTooltip": "초기화",
|
||||
"previousTooltip": "이전",
|
||||
"nextTooltip": "다음",
|
||||
"showTooltip": "보기",
|
||||
"hideTooltip": "숨기기",
|
||||
"removeTooltip": "제거",
|
||||
|
||||
"doubleBackExitMessage": "종료하려면 한번 더 누르세요.",
|
||||
|
||||
|
@ -212,8 +216,12 @@
|
|||
"collectionDeselectSectionTooltip": "묶음 선택 해제",
|
||||
|
||||
"drawerCollectionAll": "모든 미디어",
|
||||
"drawerCollectionVideos": "동영상",
|
||||
"drawerCollectionFavourites": "즐겨찾기",
|
||||
"drawerCollectionImages": "사진",
|
||||
"drawerCollectionVideos": "동영상",
|
||||
"drawerCollectionMotionPhotos": "모션 포토",
|
||||
"drawerCollectionPanoramas": "파노라마",
|
||||
"drawerCollectionSphericalVideos": "360° 동영상",
|
||||
|
||||
"chipSortTitle": "정렬",
|
||||
"chipSortDate": "날짜",
|
||||
|
@ -228,6 +236,7 @@
|
|||
"albumPickPageTitleCopy": "앨범으로 복사",
|
||||
"albumPickPageTitleExport": "앨범으로 내보내기",
|
||||
"albumPickPageTitleMove": "앨범으로 이동",
|
||||
"albumPickPageTitlePick": "앨범 선택",
|
||||
|
||||
"albumCamera": "카메라",
|
||||
"albumDownload": "다운로드",
|
||||
|
@ -266,6 +275,14 @@
|
|||
"settingsKeepScreenOnTitle": "화면 자동 꺼짐 방지",
|
||||
"settingsDoubleBackExit": "뒤로가기 두번 눌러 앱 종료하기",
|
||||
|
||||
"settingsNavigationDrawerTile": "탐색 메뉴",
|
||||
"settingsNavigationDrawerEditorTitle": "탐색 메뉴",
|
||||
"settingsNavigationDrawerBanner": "항목을 길게 누른 후 이동하여 탐색 메뉴에 표시될 항목의 순서를 수정하세요.",
|
||||
"settingsNavigationDrawerTabTypes": "유형",
|
||||
"settingsNavigationDrawerTabAlbums": "앨범",
|
||||
"settingsNavigationDrawerTabPages": "페이지",
|
||||
"settingsNavigationDrawerAddAlbum": "앨범 추가",
|
||||
|
||||
"settingsSectionThumbnails": "섬네일",
|
||||
"settingsThumbnailShowLocationIcon": "위치 아이콘 표시",
|
||||
"settingsThumbnailShowRawIcon": "Raw 아이콘 표시",
|
||||
|
@ -325,7 +342,6 @@
|
|||
"settingsHiddenPathsTitle": "숨겨진 경로",
|
||||
"settingsHiddenPathsBanner": "이 경로에 있는 사진과 동영상이 숨겨지고 있으며 이 앱에서 보여지지 않을 것입니다.",
|
||||
"settingsHiddenPathsEmpty": "숨겨진 경로가 없습니다",
|
||||
"settingsHiddenPathsRemoveTooltip": "제거",
|
||||
"addPathTooltip": "경로 추가",
|
||||
|
||||
"settingsStorageAccessTile": "저장공간 접근",
|
||||
|
|
|
@ -78,7 +78,7 @@ extension ExtraChipSetAction on ChipSetAction {
|
|||
case ChipSetAction.stats:
|
||||
return AIcons.stats;
|
||||
case ChipSetAction.createAlbum:
|
||||
return AIcons.createAlbum;
|
||||
return AIcons.add;
|
||||
// single/multiple filters
|
||||
case ChipSetAction.delete:
|
||||
return AIcons.delete;
|
||||
|
|
|
@ -3,7 +3,9 @@ import 'dart:math';
|
|||
|
||||
import 'package:aves/model/actions/entry_actions.dart';
|
||||
import 'package:aves/model/actions/video_actions.dart';
|
||||
import 'package:aves/model/filters/favourite.dart';
|
||||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/filters/mime.dart';
|
||||
import 'package:aves/model/settings/enums.dart';
|
||||
import 'package:aves/model/settings/map_style.dart';
|
||||
import 'package:aves/model/settings/screen_on.dart';
|
||||
|
@ -11,11 +13,13 @@ import 'package:aves/model/source/enums.dart';
|
|||
import 'package:aves/services/device_service.dart';
|
||||
import 'package:aves/services/services.dart';
|
||||
import 'package:aves/utils/pedantic.dart';
|
||||
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/countries_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/tags_page.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
final Settings settings = Settings._private();
|
||||
|
@ -47,6 +51,11 @@ class Settings extends ChangeNotifier {
|
|||
static const catalogTimeZoneKey = 'catalog_time_zone';
|
||||
static const tileExtentPrefixKey = 'tile_extent_';
|
||||
|
||||
// drawer
|
||||
static const drawerTypeBookmarksKey = 'drawer_type_bookmarks';
|
||||
static const drawerAlbumBookmarksKey = 'drawer_album_bookmarks';
|
||||
static const drawerPageBookmarksKey = 'drawer_page_bookmarks';
|
||||
|
||||
// collection
|
||||
static const collectionGroupFactorKey = 'collection_group_factor';
|
||||
static const collectionSortFactorKey = 'collection_sort_factor';
|
||||
|
@ -100,6 +109,16 @@ class Settings extends ChangeNotifier {
|
|||
static const lastVersionCheckDateKey = 'last_version_check_date';
|
||||
|
||||
// defaults
|
||||
static final drawerTypeBookmarksDefault = [
|
||||
null,
|
||||
MimeFilter.video,
|
||||
FavouriteFilter.instance,
|
||||
];
|
||||
static final drawerPageBookmarksDefault = [
|
||||
AlbumListPage.routeName,
|
||||
CountryListPage.routeName,
|
||||
TagListPage.routeName,
|
||||
];
|
||||
static const viewerQuickActionsDefault = [
|
||||
EntryAction.toggleFavourite,
|
||||
EntryAction.share,
|
||||
|
@ -209,6 +228,25 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
void setTileExtent(String routeName, double newValue) => setAndNotify(tileExtentPrefixKey + routeName, newValue);
|
||||
|
||||
// drawer
|
||||
|
||||
List<CollectionFilter?> get drawerTypeBookmarks =>
|
||||
(_prefs!.getStringList(drawerTypeBookmarksKey))?.map((v) {
|
||||
if (v.isEmpty) return null;
|
||||
return CollectionFilter.fromJson(v);
|
||||
}).toList() ??
|
||||
drawerTypeBookmarksDefault;
|
||||
|
||||
set drawerTypeBookmarks(List<CollectionFilter?> newValue) => setAndNotify(drawerTypeBookmarksKey, newValue.map((filter) => filter?.toJson() ?? '').toList());
|
||||
|
||||
List<String>? get drawerAlbumBookmarks => _prefs!.getStringList(drawerAlbumBookmarksKey);
|
||||
|
||||
set drawerAlbumBookmarks(List<String>? newValue) => setAndNotify(drawerAlbumBookmarksKey, newValue);
|
||||
|
||||
List<String> get drawerPageBookmarks => _prefs!.getStringList(drawerPageBookmarksKey) ?? drawerPageBookmarksDefault;
|
||||
|
||||
set drawerPageBookmarks(List<String> newValue) => setAndNotify(drawerPageBookmarksKey, newValue);
|
||||
|
||||
// collection
|
||||
|
||||
EntryGroupFactor get collectionSectionFactor => getEnumOrDefault(collectionGroupFactorKey, EntryGroupFactor.month, EntryGroupFactor.values);
|
||||
|
@ -447,7 +485,9 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
// apply user modifications
|
||||
jsonMap.forEach((key, value) {
|
||||
if (key.startsWith(tileExtentPrefixKey)) {
|
||||
if (value == null) {
|
||||
_prefs!.remove(key);
|
||||
} else if (key.startsWith(tileExtentPrefixKey)) {
|
||||
if (value is double) {
|
||||
_prefs!.setDouble(key, value);
|
||||
} else {
|
||||
|
@ -511,6 +551,9 @@ class Settings extends ChangeNotifier {
|
|||
debugPrint('failed to import key=$key, value=$value is not a string');
|
||||
}
|
||||
break;
|
||||
case drawerTypeBookmarksKey:
|
||||
case drawerAlbumBookmarksKey:
|
||||
case drawerPageBookmarksKey:
|
||||
case pinnedFiltersKey:
|
||||
case hiddenFiltersKey:
|
||||
case viewerQuickActionsKey:
|
||||
|
|
|
@ -30,7 +30,7 @@ class AIcons {
|
|||
static const IconData tagOff = MdiIcons.tagOffOutline;
|
||||
|
||||
// actions
|
||||
static const IconData addPath = Icons.add_circle_outline;
|
||||
static const IconData add = Icons.add_circle_outline;
|
||||
static const IconData addShortcut = Icons.add_to_home_screen_outlined;
|
||||
static const IconData replay10 = Icons.replay_10_outlined;
|
||||
static const IconData skip10 = Icons.forward_10_outlined;
|
||||
|
@ -38,7 +38,6 @@ class AIcons {
|
|||
static const IconData clear = Icons.clear_outlined;
|
||||
static const IconData clipboard = Icons.content_copy_outlined;
|
||||
static const IconData copy = Icons.file_copy_outlined;
|
||||
static const IconData createAlbum = Icons.add_circle_outline;
|
||||
static const IconData debug = Icons.whatshot_outlined;
|
||||
static const IconData delete = Icons.delete_outlined;
|
||||
static const IconData export = MdiIcons.fileExportOutline;
|
||||
|
@ -70,6 +69,7 @@ class AIcons {
|
|||
static const IconData select = Icons.select_all_outlined;
|
||||
static const IconData setCover = MdiIcons.imageEditOutline;
|
||||
static const IconData share = Icons.share_outlined;
|
||||
static const IconData show = Icons.visibility_outlined;
|
||||
static const IconData sort = Icons.sort_outlined;
|
||||
static const IconData speed = Icons.speed_outlined;
|
||||
static const IconData stats = Icons.pie_chart_outlined;
|
||||
|
|
|
@ -15,7 +15,7 @@ class DebugSettingsSection extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(
|
||||
builder: (context, settings, child) {
|
||||
String toMultiline(Iterable l) => l.isNotEmpty ? '\n${l.join('\n')}' : '$l';
|
||||
String toMultiline(Iterable? l) => l != null && l.isNotEmpty ? '\n${l.join('\n')}' : '$l';
|
||||
return AvesExpansionTile(
|
||||
title: 'Settings',
|
||||
children: [
|
||||
|
@ -54,6 +54,9 @@ class DebugSettingsSection extends StatelessWidget {
|
|||
'infoMapZoom': '${settings.infoMapZoom}',
|
||||
'viewerQuickActions': '${settings.viewerQuickActions}',
|
||||
'videoQuickActions': '${settings.videoQuickActions}',
|
||||
'drawerTypeBookmarks': toMultiline(settings.drawerTypeBookmarks),
|
||||
'drawerAlbumBookmarks': toMultiline(settings.drawerAlbumBookmarks),
|
||||
'drawerPageBookmarks': toMultiline(settings.drawerPageBookmarks),
|
||||
'pinnedFilters': toMultiline(settings.pinnedFilters),
|
||||
'hiddenFilters': toMultiline(settings.hiddenFilters),
|
||||
'searchHistory': toMultiline(settings.searchHistory),
|
||||
|
|
|
@ -77,9 +77,9 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
|
|||
title,
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
onPressed: _isCustom ? _pickEntry : null,
|
||||
tooltip: 'Change',
|
||||
icon: const Icon(AIcons.setCover),
|
||||
onPressed: _isCustom ? _pickEntry : null,
|
||||
tooltip: context.l10n.changeTooltip,
|
||||
),
|
||||
])
|
||||
: title,
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import 'package:aves/model/filters/album.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/widgets/common/identity/aves_icons.dart';
|
||||
import 'package:aves/widgets/drawer/collection_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AlbumTile extends StatelessWidget {
|
||||
final String album;
|
||||
|
||||
const AlbumTile({
|
||||
Key? key,
|
||||
required this.album,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final source = context.read<CollectionSource>();
|
||||
final displayName = source.getAlbumDisplayName(context, album);
|
||||
return CollectionNavTile(
|
||||
leading: IconUtils.getAlbumIcon(
|
||||
context: context,
|
||||
albumPath: album,
|
||||
),
|
||||
title: displayName,
|
||||
trailing: androidFileUtils.isOnRemovableStorage(album)
|
||||
? const Icon(
|
||||
AIcons.removableStorage,
|
||||
size: 16,
|
||||
color: Colors.grey,
|
||||
)
|
||||
: null,
|
||||
filter: AlbumFilter(album, displayName),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:aves/model/filters/favourite.dart';
|
||||
import 'package:aves/model/filters/mime.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/album.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
|
@ -16,8 +14,8 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
|||
import 'package:aves/widgets/common/extensions/media_query.dart';
|
||||
import 'package:aves/widgets/common/identity/aves_logo.dart';
|
||||
import 'package:aves/widgets/debug/app_debug_page.dart';
|
||||
import 'package:aves/widgets/drawer/album_tile.dart';
|
||||
import 'package:aves/widgets/drawer/collection_tile.dart';
|
||||
import 'package:aves/widgets/drawer/collection_nav_tile.dart';
|
||||
import 'package:aves/widgets/drawer/page_nav_tile.dart';
|
||||
import 'package:aves/widgets/drawer/tile.dart';
|
||||
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/countries_page.dart';
|
||||
|
@ -32,6 +30,16 @@ class AppDrawer extends StatefulWidget {
|
|||
|
||||
@override
|
||||
_AppDrawerState createState() => _AppDrawerState();
|
||||
|
||||
static List<String> getDefaultAlbums(BuildContext context) {
|
||||
final source = context.read<CollectionSource>();
|
||||
final specialAlbums = source.rawAlbums.where((album) {
|
||||
final type = androidFileUtils.getAlbumType(album);
|
||||
return [AlbumType.camera, AlbumType.screenshots].contains(type);
|
||||
}).toList()
|
||||
..sort(source.compareAlbumsByName);
|
||||
return specialAlbums;
|
||||
}
|
||||
}
|
||||
|
||||
class _AppDrawerState extends State<AppDrawer> {
|
||||
|
@ -47,19 +55,11 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hiddenFilters = settings.hiddenFilters;
|
||||
final showVideos = !hiddenFilters.contains(MimeFilter.video);
|
||||
final showFavourites = !hiddenFilters.contains(FavouriteFilter.instance);
|
||||
final drawerItems = <Widget>[
|
||||
_buildHeader(context),
|
||||
allCollectionTile,
|
||||
if (showVideos) videoTile,
|
||||
if (showFavourites) favouriteTile,
|
||||
_buildSpecialAlbumSection(),
|
||||
const Divider(),
|
||||
albumListTile,
|
||||
countryListTile,
|
||||
tagListTile,
|
||||
..._buildTypeLinks(),
|
||||
_buildAlbumLinks(),
|
||||
..._buildPageLinks(),
|
||||
if (!kReleaseMode) ...[
|
||||
const Divider(),
|
||||
debugTile,
|
||||
|
@ -192,82 +192,77 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildSpecialAlbumSection() {
|
||||
List<Widget> _buildTypeLinks() {
|
||||
final hiddenFilters = settings.hiddenFilters;
|
||||
final typeBookmarks = settings.drawerTypeBookmarks;
|
||||
return typeBookmarks
|
||||
.where((filter) => !hiddenFilters.contains(filter))
|
||||
.map((filter) => CollectionNavTile(
|
||||
leading: DrawerFilterIcon(filter: filter),
|
||||
title: DrawerFilterTitle(filter: filter),
|
||||
filter: filter,
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Widget _buildAlbumLinks() {
|
||||
return StreamBuilder(
|
||||
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
||||
builder: (context, snapshot) {
|
||||
final specialAlbums = source.rawAlbums.where((album) {
|
||||
final type = androidFileUtils.getAlbumType(album);
|
||||
return [AlbumType.camera, AlbumType.screenshots].contains(type);
|
||||
}).toList()
|
||||
..sort(source.compareAlbumsByName);
|
||||
|
||||
if (specialAlbums.isEmpty) return const SizedBox.shrink();
|
||||
final albums = settings.drawerAlbumBookmarks ?? AppDrawer.getDefaultAlbums(context);
|
||||
if (albums.isEmpty) return const SizedBox.shrink();
|
||||
return Column(
|
||||
children: [
|
||||
const Divider(),
|
||||
...specialAlbums.map((album) => AlbumTile(album: album)),
|
||||
...albums.map((album) => AlbumNavTile(album: album)),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// tiles
|
||||
List<Widget> _buildPageLinks() {
|
||||
final pageBookmarks = settings.drawerPageBookmarks;
|
||||
if (pageBookmarks.isEmpty) return [];
|
||||
|
||||
Widget get allCollectionTile => CollectionNavTile(
|
||||
leading: const Icon(AIcons.allCollection),
|
||||
title: context.l10n.drawerCollectionAll,
|
||||
filter: null,
|
||||
);
|
||||
return [
|
||||
const Divider(),
|
||||
...pageBookmarks.map((route) {
|
||||
WidgetBuilder? pageBuilder;
|
||||
Widget? trailing;
|
||||
switch (route) {
|
||||
case AlbumListPage.routeName:
|
||||
pageBuilder = (_) => const AlbumListPage();
|
||||
trailing = StreamBuilder(
|
||||
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
||||
builder: (context, _) => Text('${source.rawAlbums.length}'),
|
||||
);
|
||||
break;
|
||||
case CountryListPage.routeName:
|
||||
pageBuilder = (_) => const CountryListPage();
|
||||
trailing = StreamBuilder(
|
||||
stream: source.eventBus.on<CountriesChangedEvent>(),
|
||||
builder: (context, _) => Text('${source.sortedCountries.length}'),
|
||||
);
|
||||
break;
|
||||
case TagListPage.routeName:
|
||||
pageBuilder = (_) => const TagListPage();
|
||||
trailing = StreamBuilder(
|
||||
stream: source.eventBus.on<TagsChangedEvent>(),
|
||||
builder: (context, _) => Text('${source.sortedTags.length}'),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
Widget get videoTile => CollectionNavTile(
|
||||
leading: const Icon(AIcons.video),
|
||||
title: context.l10n.drawerCollectionVideos,
|
||||
filter: MimeFilter.video,
|
||||
);
|
||||
return PageNavTile(
|
||||
trailing: trailing,
|
||||
routeName: route,
|
||||
pageBuilder: pageBuilder ?? (_) => const SizedBox(),
|
||||
);
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
Widget get favouriteTile => CollectionNavTile(
|
||||
leading: const Icon(AIcons.favourite),
|
||||
title: context.l10n.drawerCollectionFavourites,
|
||||
filter: FavouriteFilter.instance,
|
||||
);
|
||||
|
||||
Widget get albumListTile => NavTile(
|
||||
icon: AIcons.album,
|
||||
title: context.l10n.albumPageTitle,
|
||||
trailing: StreamBuilder(
|
||||
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
||||
builder: (context, _) => Text('${source.rawAlbums.length}'),
|
||||
),
|
||||
routeName: AlbumListPage.routeName,
|
||||
pageBuilder: (_) => const AlbumListPage(),
|
||||
);
|
||||
|
||||
Widget get countryListTile => NavTile(
|
||||
icon: AIcons.location,
|
||||
title: context.l10n.countryPageTitle,
|
||||
trailing: StreamBuilder(
|
||||
stream: source.eventBus.on<CountriesChangedEvent>(),
|
||||
builder: (context, _) => Text('${source.sortedCountries.length}'),
|
||||
),
|
||||
routeName: CountryListPage.routeName,
|
||||
pageBuilder: (_) => const CountryListPage(),
|
||||
);
|
||||
|
||||
Widget get tagListTile => NavTile(
|
||||
icon: AIcons.tag,
|
||||
title: context.l10n.tagPageTitle,
|
||||
trailing: StreamBuilder(
|
||||
stream: source.eventBus.on<TagsChangedEvent>(),
|
||||
builder: (context, _) => Text('${source.sortedTags.length}'),
|
||||
),
|
||||
routeName: TagListPage.routeName,
|
||||
pageBuilder: (_) => const TagListPage(),
|
||||
);
|
||||
|
||||
Widget get debugTile => NavTile(
|
||||
icon: AIcons.debug,
|
||||
title: 'Debug',
|
||||
Widget get debugTile => PageNavTile(
|
||||
topLevel: false,
|
||||
routeName: AppDebugPage.routeName,
|
||||
pageBuilder: (_) => const AppDebugPage(),
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import 'package:aves/model/filters/album.dart';
|
||||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/widgets/collection/collection_page.dart';
|
||||
import 'package:aves/widgets/drawer/tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CollectionNavTile extends StatelessWidget {
|
||||
final Widget? leading;
|
||||
final String title;
|
||||
final Widget title;
|
||||
final Widget? trailing;
|
||||
final bool dense;
|
||||
final CollectionFilter? filter;
|
||||
|
@ -29,7 +33,7 @@ class CollectionNavTile extends StatelessWidget {
|
|||
bottom: false,
|
||||
child: ListTile(
|
||||
leading: leading,
|
||||
title: Text(title),
|
||||
title: title,
|
||||
trailing: trailing,
|
||||
dense: dense,
|
||||
onTap: () => _goToCollection(context),
|
||||
|
@ -54,3 +58,30 @@ class CollectionNavTile extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AlbumNavTile extends StatelessWidget {
|
||||
final String album;
|
||||
|
||||
const AlbumNavTile({
|
||||
Key? key,
|
||||
required this.album,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final source = context.read<CollectionSource>();
|
||||
var filter = AlbumFilter(album, source.getAlbumDisplayName(context, album));
|
||||
return CollectionNavTile(
|
||||
leading: DrawerFilterIcon(filter: filter),
|
||||
title: DrawerFilterTitle(filter: filter),
|
||||
trailing: androidFileUtils.isOnRemovableStorage(album)
|
||||
? const Icon(
|
||||
AIcons.removableStorage,
|
||||
size: 16,
|
||||
color: Colors.grey,
|
||||
)
|
||||
: null,
|
||||
filter: filter,
|
||||
);
|
||||
}
|
||||
}
|
64
lib/widgets/drawer/page_nav_tile.dart
Normal file
64
lib/widgets/drawer/page_nav_tile.dart
Normal file
|
@ -0,0 +1,64 @@
|
|||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/drawer/tile.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PageNavTile extends StatelessWidget {
|
||||
final Widget? trailing;
|
||||
final bool topLevel;
|
||||
final String routeName;
|
||||
final WidgetBuilder? pageBuilder;
|
||||
|
||||
const PageNavTile({
|
||||
Key? key,
|
||||
this.trailing,
|
||||
this.topLevel = true,
|
||||
required this.routeName,
|
||||
required this.pageBuilder,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _pageBuilder = pageBuilder;
|
||||
return SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: ListTile(
|
||||
key: Key('$routeName-tile'),
|
||||
leading: DrawerPageIcon(route: routeName),
|
||||
title: DrawerPageTitle(route: routeName),
|
||||
trailing: trailing != null
|
||||
? Builder(
|
||||
builder: (context) => DefaultTextStyle.merge(
|
||||
style: TextStyle(
|
||||
color: IconTheme.of(context).color!.withOpacity(.6),
|
||||
),
|
||||
child: trailing!,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
onTap: _pageBuilder != null
|
||||
? () {
|
||||
Navigator.pop(context);
|
||||
if (routeName != context.currentRouteName) {
|
||||
final route = MaterialPageRoute(
|
||||
settings: RouteSettings(name: routeName),
|
||||
builder: _pageBuilder,
|
||||
);
|
||||
if (topLevel) {
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
route,
|
||||
(route) => false,
|
||||
);
|
||||
} else {
|
||||
Navigator.push(context, route);
|
||||
}
|
||||
}
|
||||
}
|
||||
: null,
|
||||
selected: context.currentRouteName == routeName,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,64 +1,111 @@
|
|||
import 'package:aves/model/filters/favourite.dart';
|
||||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/filters/mime.dart';
|
||||
import 'package:aves/model/filters/type.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:aves/widgets/debug/app_debug_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/countries_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/tags_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NavTile extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String title;
|
||||
final Widget? trailing;
|
||||
final bool topLevel;
|
||||
final String routeName;
|
||||
final WidgetBuilder pageBuilder;
|
||||
class DrawerFilterIcon extends StatelessWidget {
|
||||
final CollectionFilter? filter;
|
||||
|
||||
const NavTile({
|
||||
const DrawerFilterIcon({
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
this.trailing,
|
||||
this.topLevel = true,
|
||||
required this.routeName,
|
||||
required this.pageBuilder,
|
||||
required this.filter,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: ListTile(
|
||||
key: Key('$title-tile'),
|
||||
leading: Icon(icon),
|
||||
title: Text(title),
|
||||
trailing: trailing != null
|
||||
? Builder(
|
||||
builder: (context) => DefaultTextStyle.merge(
|
||||
style: TextStyle(
|
||||
color: IconTheme.of(context).color!.withOpacity(.6),
|
||||
),
|
||||
child: trailing!,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
if (routeName != context.currentRouteName) {
|
||||
final route = MaterialPageRoute(
|
||||
settings: RouteSettings(name: routeName),
|
||||
builder: pageBuilder,
|
||||
);
|
||||
if (topLevel) {
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
route,
|
||||
(route) => false,
|
||||
);
|
||||
} else {
|
||||
Navigator.push(context, route);
|
||||
}
|
||||
}
|
||||
},
|
||||
selected: context.currentRouteName == routeName,
|
||||
),
|
||||
);
|
||||
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
|
||||
final iconSize = 24 * textScaleFactor;
|
||||
|
||||
final _filter = filter;
|
||||
if (_filter == null) return Icon(AIcons.allCollection, size: iconSize);
|
||||
return _filter.iconBuilder(context, iconSize) ?? const SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
class DrawerFilterTitle extends StatelessWidget {
|
||||
final CollectionFilter? filter;
|
||||
|
||||
const DrawerFilterTitle({
|
||||
Key? key,
|
||||
required this.filter,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String _getString(CollectionFilter? filter) {
|
||||
final l10n = context.l10n;
|
||||
if (filter == null) return l10n.drawerCollectionAll;
|
||||
if (filter == FavouriteFilter.instance) return l10n.drawerCollectionFavourites;
|
||||
if (filter == MimeFilter.image) return l10n.drawerCollectionImages;
|
||||
if (filter == MimeFilter.video) return l10n.drawerCollectionVideos;
|
||||
if (filter == TypeFilter.motionPhoto) return l10n.drawerCollectionMotionPhotos;
|
||||
if (filter == TypeFilter.panorama) return l10n.drawerCollectionPanoramas;
|
||||
if (filter == TypeFilter.sphericalVideo) return l10n.drawerCollectionSphericalVideos;
|
||||
return filter.getLabel(context);
|
||||
}
|
||||
|
||||
return Text(_getString(filter));
|
||||
}
|
||||
}
|
||||
|
||||
class DrawerPageIcon extends StatelessWidget {
|
||||
final String route;
|
||||
|
||||
const DrawerPageIcon({
|
||||
Key? key,
|
||||
required this.route,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (route) {
|
||||
case AlbumListPage.routeName:
|
||||
return const Icon(AIcons.album);
|
||||
case CountryListPage.routeName:
|
||||
return const Icon(AIcons.location);
|
||||
case TagListPage.routeName:
|
||||
return const Icon(AIcons.tag);
|
||||
case AppDebugPage.routeName:
|
||||
return const Icon(AIcons.debug);
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DrawerPageTitle extends StatelessWidget {
|
||||
final String route;
|
||||
|
||||
const DrawerPageTitle({
|
||||
Key? key,
|
||||
required this.route,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String _getString() {
|
||||
final l10n = context.l10n;
|
||||
switch (route) {
|
||||
case AlbumListPage.routeName:
|
||||
return l10n.albumPageTitle;
|
||||
case CountryListPage.routeName:
|
||||
return l10n.countryPageTitle;
|
||||
case TagListPage.routeName:
|
||||
return l10n.tagPageTitle;
|
||||
case AppDebugPage.routeName:
|
||||
return 'Debug';
|
||||
default:
|
||||
return route;
|
||||
}
|
||||
}
|
||||
|
||||
return Text(_getString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class AlbumPickPage extends StatefulWidget {
|
|||
static const routeName = '/album_pick';
|
||||
|
||||
final CollectionSource source;
|
||||
final MoveType moveType;
|
||||
final MoveType? moveType;
|
||||
|
||||
const AlbumPickPage({
|
||||
Key? key,
|
||||
|
@ -92,7 +92,7 @@ class _AlbumPickPageState extends State<AlbumPickPage> {
|
|||
|
||||
class AlbumPickAppBar extends StatelessWidget {
|
||||
final CollectionSource source;
|
||||
final MoveType moveType;
|
||||
final MoveType? moveType;
|
||||
final AlbumChipSetActionDelegate actionDelegate;
|
||||
final ValueNotifier<String> queryNotifier;
|
||||
|
||||
|
@ -117,7 +117,7 @@ class AlbumPickAppBar extends StatelessWidget {
|
|||
case MoveType.move:
|
||||
return context.l10n.albumPickPageTitleMove;
|
||||
default:
|
||||
return moveType.toString();
|
||||
return context.l10n.albumPickPageTitlePick;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,19 +131,20 @@ class AlbumPickAppBar extends StatelessWidget {
|
|||
filterNotifier: queryNotifier,
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(AIcons.createAlbum),
|
||||
onPressed: () async {
|
||||
final newAlbum = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) => const CreateAlbumDialog(),
|
||||
);
|
||||
if (newAlbum != null && newAlbum.isNotEmpty) {
|
||||
Navigator.pop<String>(context, newAlbum);
|
||||
}
|
||||
},
|
||||
tooltip: context.l10n.createAlbumTooltip,
|
||||
),
|
||||
if (moveType != null)
|
||||
IconButton(
|
||||
icon: const Icon(AIcons.add),
|
||||
onPressed: () async {
|
||||
final newAlbum = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) => const CreateAlbumDialog(),
|
||||
);
|
||||
if (newAlbum != null && newAlbum.isNotEmpty) {
|
||||
Navigator.pop<String>(context, newAlbum);
|
||||
}
|
||||
},
|
||||
tooltip: context.l10n.createAlbumTooltip,
|
||||
),
|
||||
PopupMenuButton<ChipSetAction>(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
|
|
133
lib/widgets/settings/navigation/drawer.dart
Normal file
133
lib/widgets/settings/navigation/drawer.dart
Normal file
|
@ -0,0 +1,133 @@
|
|||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/drawer/app_drawer.dart';
|
||||
import 'package:aves/widgets/drawer/tile.dart';
|
||||
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/countries_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/tags_page.dart';
|
||||
import 'package:aves/widgets/search/search_delegate.dart';
|
||||
import 'package:aves/widgets/settings/navigation/drawer_tab_albums.dart';
|
||||
import 'package:aves/widgets/settings/navigation/drawer_tab_fixed.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class NavigationDrawerTile extends StatelessWidget {
|
||||
const NavigationDrawerTile({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: Text(context.l10n.settingsNavigationDrawerTile),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
settings: const RouteSettings(name: NavigationDrawerEditorPage.routeName),
|
||||
builder: (context) => const NavigationDrawerEditorPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NavigationDrawerEditorPage extends StatefulWidget {
|
||||
static const routeName = '/settings/navigation_drawer';
|
||||
|
||||
const NavigationDrawerEditorPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_NavigationDrawerEditorPageState createState() => _NavigationDrawerEditorPageState();
|
||||
}
|
||||
|
||||
class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage> {
|
||||
final List<CollectionFilter?> _typeItems = [];
|
||||
final Set<CollectionFilter?> _visibleTypes = {};
|
||||
final List<String> _albumItems = [];
|
||||
final List<String> _pageItems = [];
|
||||
final Set<String> _visiblePages = {};
|
||||
|
||||
static final Set<CollectionFilter?> _typeOptions = {
|
||||
null,
|
||||
...CollectionSearchDelegate.typeFilters,
|
||||
};
|
||||
static const Set<String> _pageOptions = {
|
||||
AlbumListPage.routeName,
|
||||
CountryListPage.routeName,
|
||||
TagListPage.routeName,
|
||||
};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final userTypeLinks = settings.drawerTypeBookmarks;
|
||||
_visibleTypes.addAll(userTypeLinks);
|
||||
_typeItems.addAll(userTypeLinks);
|
||||
_typeItems.addAll(_typeOptions.where((v) => !userTypeLinks.contains(v)));
|
||||
|
||||
_albumItems.addAll(settings.drawerAlbumBookmarks ?? AppDrawer.getDefaultAlbums(context));
|
||||
|
||||
final userPageLinks = settings.drawerPageBookmarks;
|
||||
_visiblePages.addAll(userPageLinks);
|
||||
_pageItems.addAll(userPageLinks);
|
||||
_pageItems.addAll(_pageOptions.where((v) => !userPageLinks.contains(v)));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final tabs = <Tuple2<Tab, Widget>>[
|
||||
Tuple2(
|
||||
Tab(text: l10n.settingsNavigationDrawerTabTypes),
|
||||
DrawerFixedListTab<CollectionFilter?>(
|
||||
items: _typeItems,
|
||||
visibleItems: _visibleTypes,
|
||||
leading: (item) => DrawerFilterIcon(filter: item),
|
||||
title: (item) => DrawerFilterTitle(filter: item),
|
||||
),
|
||||
),
|
||||
Tuple2(
|
||||
Tab(text: l10n.settingsNavigationDrawerTabAlbums),
|
||||
DrawerAlbumTab(
|
||||
items: _albumItems,
|
||||
),
|
||||
),
|
||||
Tuple2(
|
||||
Tab(text: l10n.settingsNavigationDrawerTabPages),
|
||||
DrawerFixedListTab<String>(
|
||||
items: _pageItems,
|
||||
visibleItems: _visiblePages,
|
||||
leading: (item) => DrawerPageIcon(route: item),
|
||||
title: (item) => DrawerPageTitle(route: item),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
return DefaultTabController(
|
||||
length: tabs.length,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.settingsNavigationDrawerEditorTitle),
|
||||
bottom: TabBar(
|
||||
tabs: tabs.map((t) => t.item1).toList(),
|
||||
),
|
||||
),
|
||||
body: WillPopScope(
|
||||
onWillPop: () {
|
||||
settings.drawerTypeBookmarks = _typeItems.where(_visibleTypes.contains).toList();
|
||||
settings.drawerAlbumBookmarks = _albumItems;
|
||||
settings.drawerPageBookmarks = _pageItems.where(_visiblePages.contains).toList();
|
||||
return SynchronousFuture(true);
|
||||
},
|
||||
child: SafeArea(
|
||||
child: TabBarView(
|
||||
children: tabs.map((t) => t.item2).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
22
lib/widgets/settings/navigation/drawer_editor_banner.dart
Normal file
22
lib/widgets/settings/navigation/drawer_editor_banner.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DrawerEditorBanner extends StatelessWidget {
|
||||
const DrawerEditorBanner({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(AIcons.info),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(child: Text(context.l10n.settingsNavigationDrawerBanner)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
89
lib/widgets/settings/navigation/drawer_tab_albums.dart
Normal file
89
lib/widgets/settings/navigation/drawer_tab_albums.dart
Normal file
|
@ -0,0 +1,89 @@
|
|||
import 'package:aves/model/filters/album.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/drawer/tile.dart';
|
||||
import 'package:aves/widgets/filter_grids/album_pick.dart';
|
||||
import 'package:aves/widgets/settings/navigation/drawer_editor_banner.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class DrawerAlbumTab extends StatefulWidget {
|
||||
final List<String> items;
|
||||
|
||||
const DrawerAlbumTab({
|
||||
Key? key,
|
||||
required this.items,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_DrawerAlbumTabState createState() => _DrawerAlbumTabState();
|
||||
}
|
||||
|
||||
class _DrawerAlbumTabState extends State<DrawerAlbumTab> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final source = context.read<CollectionSource>();
|
||||
return Column(
|
||||
children: [
|
||||
const DrawerEditorBanner(),
|
||||
const Divider(height: 0),
|
||||
Flexible(
|
||||
child: ReorderableListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final album = widget.items[index];
|
||||
final filter = AlbumFilter(album, source.getAlbumDisplayName(context, album));
|
||||
return ListTile(
|
||||
key: ValueKey(album),
|
||||
leading: DrawerFilterIcon(filter: filter),
|
||||
title: DrawerFilterTitle(filter: filter),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(AIcons.clear),
|
||||
onPressed: () {
|
||||
setState(() => widget.items.remove(album));
|
||||
},
|
||||
tooltip: context.l10n.removeTooltip,
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: widget.items.length,
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
setState(() {
|
||||
if (oldIndex < newIndex) newIndex -= 1;
|
||||
widget.items.insert(newIndex, widget.items.removeAt(oldIndex));
|
||||
});
|
||||
},
|
||||
shrinkWrap: true,
|
||||
),
|
||||
),
|
||||
const Divider(height: 0),
|
||||
const SizedBox(height: 8),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final source = context.read<CollectionSource>();
|
||||
final album = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute<String>(
|
||||
settings: const RouteSettings(name: AlbumPickPage.routeName),
|
||||
builder: (context) => AlbumPickPage(source: source, moveType: null),
|
||||
),
|
||||
);
|
||||
|
||||
if (album == null || album.isEmpty) return;
|
||||
|
||||
setState(() {
|
||||
widget.items.add(album);
|
||||
});
|
||||
},
|
||||
style: ButtonStyle(
|
||||
side: MaterialStateProperty.all<BorderSide>(BorderSide(color: Theme.of(context).accentColor)),
|
||||
foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
|
||||
),
|
||||
icon: const Icon(AIcons.add),
|
||||
label: Text(context.l10n.settingsNavigationDrawerAddAlbum),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
75
lib/widgets/settings/navigation/drawer_tab_fixed.dart
Normal file
75
lib/widgets/settings/navigation/drawer_tab_fixed.dart
Normal file
|
@ -0,0 +1,75 @@
|
|||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/settings/navigation/drawer_editor_banner.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
typedef ItemWidgetBuilder<T> = Widget Function(T item);
|
||||
|
||||
class DrawerFixedListTab<T> extends StatefulWidget {
|
||||
final List<T> items;
|
||||
final Set<T> visibleItems;
|
||||
final ItemWidgetBuilder<T> leading;
|
||||
final ItemWidgetBuilder<T> title;
|
||||
|
||||
const DrawerFixedListTab({
|
||||
Key? key,
|
||||
required this.items,
|
||||
required this.visibleItems,
|
||||
required this.leading,
|
||||
required this.title,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_DrawerFixedListTabState<T> createState() => _DrawerFixedListTabState<T>();
|
||||
}
|
||||
|
||||
class _DrawerFixedListTabState<T> extends State<DrawerFixedListTab<T>> {
|
||||
Set<T> get visibleItems => widget.visibleItems;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
const DrawerEditorBanner(),
|
||||
const Divider(height: 0),
|
||||
Flexible(
|
||||
child: ReorderableListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final filter = widget.items[index];
|
||||
final visible = visibleItems.contains(filter);
|
||||
return Opacity(
|
||||
key: ValueKey(filter),
|
||||
opacity: visible ? 1 : .4,
|
||||
child: ListTile(
|
||||
leading: widget.leading(filter),
|
||||
title: widget.title(filter),
|
||||
trailing: IconButton(
|
||||
icon: Icon(visible ? AIcons.hide : AIcons.show),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
if (visible) {
|
||||
visibleItems.remove(filter);
|
||||
} else {
|
||||
visibleItems.add(filter);
|
||||
}
|
||||
});
|
||||
},
|
||||
tooltip: visible ? context.l10n.hideTooltip : context.l10n.showTooltip,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: widget.items.length,
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
setState(() {
|
||||
if (oldIndex < newIndex) newIndex -= 1;
|
||||
widget.items.insert(newIndex, widget.items.removeAt(oldIndex));
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
|||
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
|
||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
||||
import 'package:aves/widgets/settings/common/tile_leading.dart';
|
||||
import 'package:aves/widgets/settings/navigation/drawer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -51,6 +52,7 @@ class NavigationSection extends StatelessWidget {
|
|||
}
|
||||
},
|
||||
),
|
||||
const NavigationDrawerTile(),
|
||||
ListTile(
|
||||
title: Text(context.l10n.settingsKeepScreenOnTile),
|
||||
subtitle: Text(currentKeepScreenOn.getName(context)),
|
|
@ -40,7 +40,7 @@ class HiddenPathPage extends StatelessWidget {
|
|||
title: Text(context.l10n.settingsHiddenPathsTitle),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(AIcons.addPath),
|
||||
icon: const Icon(AIcons.add),
|
||||
onPressed: () async {
|
||||
final path = await storageService.selectDirectory();
|
||||
if (path != null && path.isNotEmpty) {
|
||||
|
@ -87,7 +87,7 @@ class HiddenPathPage extends StatelessWidget {
|
|||
onPressed: () {
|
||||
context.read<CollectionSource>().changeFilterVisibility({pathFilter}, true);
|
||||
},
|
||||
tooltip: context.l10n.settingsHiddenPathsRemoveTooltip,
|
||||
tooltip: context.l10n.removeTooltip,
|
||||
),
|
||||
)),
|
||||
],
|
||||
|
|
|
@ -12,7 +12,7 @@ import 'package:aves/widgets/common/basic/menu_row.dart';
|
|||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/settings/language/language.dart';
|
||||
import 'package:aves/widgets/settings/navigation.dart';
|
||||
import 'package:aves/widgets/settings/navigation/navigation.dart';
|
||||
import 'package:aves/widgets/settings/privacy/privacy.dart';
|
||||
import 'package:aves/widgets/settings/thumbnails.dart';
|
||||
import 'package:aves/widgets/settings/video/video.dart';
|
||||
|
|
|
@ -122,12 +122,14 @@ void selectFirstAlbum() {
|
|||
await driver.tap(find.byValueKey('appbar-leading-button'));
|
||||
await driver.waitUntilNoTransientCallbacks();
|
||||
|
||||
await driver.tap(find.byValueKey('Albums-tile'));
|
||||
// prefix must match `AlbumListPage.routeName`
|
||||
await driver.tap(find.byValueKey('/albums-tile'));
|
||||
await driver.waitUntilNoTransientCallbacks();
|
||||
|
||||
// wait for collection loading
|
||||
await driver.waitForCondition(const NoPendingPlatformMessages());
|
||||
|
||||
// TODO TLAD fix finder
|
||||
await driver.tap(find.descendant(
|
||||
of: find.byValueKey('filter-grid-page'),
|
||||
matching: find.byType('CoveredFilterChip'),
|
||||
|
|
Loading…
Reference in a new issue