Skip to content

Commit

Permalink
FEAT: search in settings
Browse files Browse the repository at this point in the history
  • Loading branch information
MSOB7YY committed Nov 22, 2023
1 parent 3953013 commit 204d4da
Show file tree
Hide file tree
Showing 19 changed files with 4,188 additions and 2,975 deletions.
14 changes: 11 additions & 3 deletions lib/controller/navigator_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:namida/controller/folders_controller.dart';
import 'package:namida/controller/miniplayer_controller.dart';
import 'package:namida/controller/scroll_search_controller.dart';
import 'package:namida/controller/settings_controller.dart';
import 'package:namida/controller/settings_search_controller.dart';
import 'package:namida/core/constants.dart';
import 'package:namida/core/dimensions.dart';
import 'package:namida/core/enums.dart';
Expand Down Expand Up @@ -38,6 +39,8 @@ class NamidaNavigator {

bool isInLanscape = false;

static const _defaultRouteAnimationDurMS = 500;

Future<T?> showMenu<T>(Future? menuFunction) async {
_currentMenusNumber++;
_printMenus();
Expand Down Expand Up @@ -155,7 +158,7 @@ class NamidaNavigator {
Widget page, {
bool nested = true,
Transition transition = Transition.cupertino,
int durationInMs = 500,
int durationInMs = _defaultRouteAnimationDurMS,
}) async {
currentWidgetStack.add(page.toNamidaRoute());
_hideEverything();
Expand Down Expand Up @@ -257,7 +260,7 @@ class NamidaNavigator {
Widget page, {
bool nested = true,
Transition transition = Transition.cupertino,
int durationInMs = 500,
int durationInMs = _defaultRouteAnimationDurMS,
}) async {
currentWidgetStack.removeLast();
currentWidgetStack.add(page.toNamidaRoute());
Expand Down Expand Up @@ -298,7 +301,7 @@ class NamidaNavigator {
);
}

Future<void> popPage() async {
Future<void> popPage({bool waitForAnimation = false}) async {
if (innerDrawerKey.currentState?.isOpened ?? false) {
innerDrawerKey.currentState?.close();
return;
Expand All @@ -311,6 +314,10 @@ class NamidaNavigator {
_hideSearchMenuAndUnfocus();
return;
}
if (SettingsSearchController.inst.canShowSearch) {
SettingsSearchController.inst.closeSearch();
return;
}

if (currentRoute?.route == RouteType.PAGE_folders) {
final canIgoBackPls = Folders.inst.onBackButton();
Expand All @@ -327,6 +334,7 @@ class NamidaNavigator {
} else {
await _doubleTapToExit();
}
if (waitForAnimation) await Future.delayed(const Duration(milliseconds: _defaultRouteAnimationDurMS));
currentRoute?.updateColorScheme();
_hideSearchMenuAndUnfocus();
}
Expand Down
270 changes: 270 additions & 0 deletions lib/controller/settings_search_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:get/get.dart';

import 'package:namida/controller/navigator_controller.dart';
import 'package:namida/core/enums.dart';
import 'package:namida/core/extensions.dart';
import 'package:namida/core/icon_fonts/broken_icons.dart';
import 'package:namida/core/translations/language.dart';
import 'package:namida/ui/pages/settings_page.dart';
import 'package:namida/ui/widgets/settings/advanced_settings.dart';
import 'package:namida/ui/widgets/settings/backup_restore_settings.dart';
import 'package:namida/ui/widgets/settings/customization_settings.dart';
import 'package:namida/ui/widgets/settings/extra_settings.dart';
import 'package:namida/ui/widgets/settings/indexer_settings.dart';
import 'package:namida/ui/widgets/settings/playback_settings.dart';
import 'package:namida/ui/widgets/settings/theme_settings.dart';
import 'package:namida/ui/widgets/settings/youtube_settings.dart';

extension _SettSearcherUtils on SettingSubpageEnum {
CustomCollapsedListTile? toSettingSubPageDetails({Enum? initialItem}) {
switch (this) {
case SettingSubpageEnum.theme:
return CustomCollapsedListTile(
title: lang.THEME_SETTINGS,
subtitle: lang.THEME_SETTINGS_SUBTITLE,
icon: Broken.brush_2,
page: ThemeSetting(initialItem: initialItem),
);
case SettingSubpageEnum.indexer:
return CustomCollapsedListTile(
title: lang.INDEXER,
subtitle: lang.INDEXER_SUBTITLE,
icon: Broken.component,
page: IndexerSettings(initialItem: initialItem),
);

case SettingSubpageEnum.playback:
return CustomCollapsedListTile(
title: lang.PLAYBACK_SETTING,
subtitle: lang.PLAYBACK_SETTING_SUBTITLE,
icon: Broken.play_cricle,
page: PlaybackSettings(initialItem: initialItem),
);
case SettingSubpageEnum.customization:
return CustomCollapsedListTile(
title: lang.CUSTOMIZATIONS,
subtitle: lang.CUSTOMIZATIONS_SUBTITLE,
icon: Broken.brush_1,
page: CustomizationSettings(initialItem: initialItem),
);

case SettingSubpageEnum.youtube:
return CustomCollapsedListTile(
title: lang.YOUTUBE,
subtitle: lang.YOUTUBE_SETTINGS_SUBTITLE,
icon: Broken.video,
page: YoutubeSettings(initialItem: initialItem),
);

case SettingSubpageEnum.extra:
return CustomCollapsedListTile(
title: lang.EXTRAS,
subtitle: lang.EXTRAS_SUBTITLE,
icon: Broken.command_square,
page: ExtrasSettings(initialItem: initialItem),
);
case SettingSubpageEnum.backupRestore:
return CustomCollapsedListTile(
title: lang.BACKUP_AND_RESTORE,
subtitle: lang.BACKUP_AND_RESTORE_SUBTITLE,
icon: Broken.refresh_circle,
page: BackupAndRestore(initialItem: initialItem),
);

case SettingSubpageEnum.advanced:
return CustomCollapsedListTile(
title: lang.ADVANCED_SETTINGS,
subtitle: lang.ADVANCED_SETTINGS_SUBTITLE,
icon: Broken.hierarchy_3,
page: AdvancedSettings(initialItem: initialItem),
);
default:
return null;
}
}
}

class SettingSearchResultItem {
final SettingSubpageEnum? page;
final Enum key;
final List<String> titles;

const SettingSearchResultItem({
required this.page,
required this.key,
required this.titles,
});
}

class SettingsSearchController {
static final SettingsSearchController inst = SettingsSearchController._internal();
SettingsSearchController._internal();

final _map = <SettingSubpageEnum, Map<int, GlobalKey>>{};
final _allWidgets = <(SettingSubpageProvider, Map<Enum, List<String>>)>[];
final searchResults = <SettingSubpageEnum, List<SettingSearchResultItem>>{}.obs;
final subpagesDetails = <SettingSubpageEnum, CustomCollapsedListTile?>{};

bool get canShowSearch => _canShowSearch.value;
final _canShowSearch = false.obs;

void closeSearch() {
_allWidgets.clear();
searchResults.clear();
_canShowSearch.value = false;
}

GlobalKey getSettingWidgetGlobalKey(SettingSubpageEnum settingPage, Enum key) {
final keyIndex = key.index;
final c = SettingsSearchController.inst;
final r = settingPage;
c._map[r] ??= {};
c._map[r]![keyIndex] ??= GlobalKey();
return c._map[r]![keyIndex]!;
}

void onSearchTap({required bool isOpen}) {
if (isOpen) {
const theme = ThemeSetting();
const indexer = IndexerSettings();
const playback = PlaybackSettings();
const customization = CustomizationSettings();
const youtube = YoutubeSettings();
const extras = ExtrasSettings();
const backupAndRestore = BackupAndRestore();
const advanced = AdvancedSettings();
_allWidgets
..clear()
..addAll([
(theme, theme.lookupMap),
(indexer, indexer.lookupMap),
(playback, playback.lookupMap),
(customization, customization.lookupMap),
(youtube, youtube.lookupMap),
(extras, extras.lookupMap),
(backupAndRestore, backupAndRestore.lookupMap),
(advanced, advanced.lookupMap),
]);
for (final p in _allWidgets) {
subpagesDetails[p.$1.settingPage] = p.$1.settingPage.toSettingSubPageDetails();
}
_canShowSearch.value = true;
} else {
closeSearch();
}
}

void onSearchChanged(String val) {
final res = <SettingSubpageEnum, List<SettingSearchResultItem>>{};
_allWidgets.loop((widget, index) {
for (final e in widget.$2.entries) {
final match = e.value.any((element) => element.cleanUpForComparison.contains(val.cleanUpForComparison));
if (match) {
final p = widget.$1.settingPage;
res.addForce(
p,
SettingSearchResultItem(
page: p,
key: e.key,
titles: e.value,
),
);
}
}
});
searchResults
..clear()
..addAll(res);
searchResults.refresh();
}

Future<void> onResultTap({
required SettingSubpageEnum? settingPage,
required Enum key,
required BuildContext context,
}) async {
onSearchTap(isOpen: false);

final details = subpagesDetails[settingPage];
final page = settingPage?.toSettingSubPageDetails(initialItem: key)?.page;
if (NamidaNavigator.inst.currentRoute?.route == RouteType.SETTINGS_subpage) {
// -- navigate back if inside subpage.
// -- we can skip and just jump, but we
// -- need the blink animation & bgColor to update.
await NamidaNavigator.inst.popPage(waitForAnimation: true);
}
if (page != null) {
NamidaNavigator.inst.navigateTo(
SettingsSubPage(
title: details?.title ?? '',
child: page,
),
);
}
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
final c = _map[settingPage]?[key.index]?.currentContext;
if (c != null) Scrollable.ensureVisible(c, alignment: 0.3);
});
}
}

abstract class SettingSubpageProvider extends StatelessWidget {
SettingSubpageEnum get settingPage;
Map<Enum, List<String>> get lookupMap;
final Enum? initialItem;

const SettingSubpageProvider({super.key, this.initialItem});

GlobalKey getSettingWidgetGlobalKey(Enum key) {
return SettingsSearchController.inst.getSettingWidgetGlobalKey(settingPage, key);
}

Color? getBgColor(Enum key) {
return key == initialItem ? Colors.grey.withAlpha(80) : null;
}

Widget getItemWrapper({required Enum key, required Widget child}) {
return Stack(
key: getSettingWidgetGlobalKey(key),
children: [
child,
if (key == initialItem)
() {
bool finished = false;
return Positioned.fill(
child: IgnorePointer(
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(18.0.multipliedRadius),
color: Colors.grey.withAlpha(100),
),
).animate(
autoPlay: true,
onComplete: (controller) async {
if (!finished) {
finished = true;
Future<void> oneLap() async {
await controller.animateTo(controller.upperBound);
await controller.animateTo(controller.lowerBound);
}

await oneLap();
await oneLap();
}
},
effects: [
const FadeEffect(
duration: Duration(milliseconds: 200),
delay: Duration(milliseconds: 50),
),
],
),
),
);
}()
],
);
}
}
11 changes: 11 additions & 0 deletions lib/core/enums.dart
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,14 @@ enum KillAppMode {
ifNotPlaying,
never,
}

enum SettingSubpageEnum {
theme,
indexer,
playback,
customization,
youtube,
extra,
backupRestore,
advanced,
}
5 changes: 4 additions & 1 deletion lib/core/namida_converter_ext.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import 'package:namida/ui/pages/subpages/queue_tracks_subpage.dart';
import 'package:namida/ui/pages/tracks_page.dart';
import 'package:namida/ui/widgets/circular_percentages.dart';
import 'package:namida/ui/widgets/custom_widgets.dart';
import 'package:namida/ui/widgets/settings_search_bar.dart';
import 'package:namida/ui/widgets/stats.dart';
import 'package:namida/youtube/class/youtube_id.dart';
import 'package:namida/youtube/controller/youtube_controller.dart';
Expand Down Expand Up @@ -810,7 +811,9 @@ extension RouteUtils on NamidaRoute {

return AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
child: finalWidget ?? ScrollSearchController.inst.searchBarWidget,
child: (route == RouteType.SETTINGS_page || route == RouteType.SETTINGS_subpage)
? NamidaSettingSearchBar(closedChild: finalWidget)
: finalWidget ?? ScrollSearchController.inst.searchBarWidget,
);
}

Expand Down
Loading

0 comments on commit 204d4da

Please sign in to comment.