Skip to content

feat: add local audio metadata writer UI #1227

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 24, 2025
140 changes: 140 additions & 0 deletions lib/app/view/create_master_items.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import 'package:flutter/material.dart';
import 'package:watch_it/watch_it.dart';

import '../../app_config.dart';
import '../../common/data/audio_type.dart';
import '../../common/page_ids.dart';
import '../../common/view/icons.dart';
import '../../common/view/side_bar_fall_back_image.dart';
import '../../common/view/theme.dart';
import '../../custom_content/view/custom_content_page.dart';
import '../../extensions/string_x.dart';
import '../../home/home_page.dart';
import '../../l10n/l10n.dart';
import '../../library/library_model.dart';
import '../../local_audio/view/album_page.dart';
import '../../local_audio/view/local_audio_page.dart';
import '../../playlists/view/liked_audio_page.dart';
import '../../playlists/view/playlist_page.dart';
import '../../podcasts/view/lazy_podcast_page.dart';
import '../../podcasts/view/podcast_page_side_bar_icon.dart';
import '../../podcasts/view/podcast_page_title.dart';
import '../../podcasts/view/podcasts_page.dart';
import '../../radio/view/radio_page.dart';
import '../../radio/view/station_page.dart';
import '../../radio/view/station_page_icon.dart';
import '../../radio/view/station_title.dart';
import '../../search/view/search_page.dart';
import '../../settings/view/settings_page.dart';
import 'main_page_icon.dart';
import 'master_items.dart';

List<MasterItem> createMasterItems() {
final libraryModel = di<LibraryModel>();
return [
MasterItem(
titleBuilder: (context) => Text(context.l10n.search),
pageBuilder: (_) => const SearchPage(),
iconBuilder: (_) => Icon(Iconz.search),
pageId: PageIDs.searchPage,
),
MasterItem(
titleBuilder: (context) => Text(context.l10n.local),
pageBuilder: (_) => const LocalAudioPage(),
iconBuilder: (selected) => MainPageIcon(
audioType: AudioType.local,
selected: selected,
),
pageId: PageIDs.localAudio,
),
MasterItem(
titleBuilder: (context) => Text(context.l10n.radio),
pageBuilder: (_) => const RadioPage(),
iconBuilder: (selected) => MainPageIcon(
audioType: AudioType.radio,
selected: selected,
),
pageId: PageIDs.radio,
),
MasterItem(
titleBuilder: (context) => Text(context.l10n.podcasts),
pageBuilder: (_) => const PodcastsPage(),
iconBuilder: (selected) => MainPageIcon(
audioType: AudioType.podcast,
selected: selected,
),
pageId: PageIDs.podcasts,
),
if (AppConfig.isMobilePlatform)
MasterItem(
titleBuilder: (context) => Text(context.l10n.settings),
iconBuilder: (selected) =>
Icon(selected ? Iconz.settingsFilled : Iconz.settings),
pageBuilder: (context) => const SettingsPage(),
pageId: PageIDs.settings,
),
if (AppConfig.isMobilePlatform)
MasterItem(
titleBuilder: (context) => Text(context.l10n.home),
iconBuilder: (selected) =>
Icon(selected ? Iconz.homeFilled : Iconz.home),
pageBuilder: (context) => const HomePage(),
pageId: PageIDs.homePage,
),
MasterItem(
iconBuilder: (selected) => Icon(Iconz.plus),
titleBuilder: (context) => Text(context.l10n.add),
pageBuilder: (_) => const CustomContentPage(),
pageId: PageIDs.customContent,
),
MasterItem(
titleBuilder: (context) => Text(context.l10n.likedSongs),
pageId: PageIDs.likedAudios,
pageBuilder: (_) => const LikedAudioPage(),
subtitleBuilder: (context) => Text(context.l10n.playlist),
iconBuilder: (selected) => LikedAudioPageIcon(selected: selected),
),
for (final id in libraryModel.playlistIDs)
MasterItem(
titleBuilder: (context) => Text(id),
subtitleBuilder: (context) => Text(context.l10n.playlist),
pageId: id,
pageBuilder: (_) => PlaylistPage(pageId: id),
iconBuilder: (selected) => SideBarFallBackImage(
color: getAlphabetColor(id),
child: Icon(Iconz.playlist),
),
),
for (final feedUrl in libraryModel.podcastFeedUrls)
MasterItem(
titleBuilder: (_) => PodcastPageTitle(
feedUrl: feedUrl,
),
subtitleBuilder: (context) => PodcastPageSubTitle(feedUrl: feedUrl),
pageId: feedUrl,
pageBuilder: (_) => LazyPodcastPage(feedUrl: feedUrl),
iconBuilder: (selected) => PodcastPageSideBarIcon(feedUrl: feedUrl),
),
for (final id in libraryModel.favoriteAlbums)
MasterItem(
titleBuilder: (context) => Text(id.albumOfId),
subtitleBuilder: (context) => Text(id.artistOfId),
pageId: id,
pageBuilder: (_) => AlbumPage(id: id),
iconBuilder: (selected) => AlbumPageSideBarIcon(
albumId: id,
),
),
for (final uuid in libraryModel.starredStations.where((e) => e.isNotEmpty))
MasterItem(
titleBuilder: (context) => StationTitle(uuid: uuid),
subtitleBuilder: (context) => Text(context.l10n.station),
pageId: uuid,
pageBuilder: (_) => StationPage(uuid: uuid),
iconBuilder: (selected) => StationPageIcon(
uuid: uuid,
selected: selected,
),
),
];
}
4 changes: 2 additions & 2 deletions lib/app/view/desktop_home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class _DesktopHomePageState extends State<DesktopHomePage> {
builder: (_) => PatchNotesDialog(
onClose: () {
if ((di<LocalAudioModel>().audios?.isNotEmpty ?? false) &&
di<LibraryModel>().playlists.isNotEmpty &&
di<LibraryModel>().pinnedAlbums.isNotEmpty &&
di<LibraryModel>().playlistIDs.isNotEmpty &&
di<LibraryModel>().favoriteAlbums.isNotEmpty &&
di<AppModel>().isBackupScreenNeeded &&
!di<AppModel>().wasBackupSaved &&
mounted) {
Expand Down
84 changes: 7 additions & 77 deletions lib/app/view/master_detail_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,30 @@ import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:watch_it/watch_it.dart';
import 'package:yaru/yaru.dart';

import '../../app_config.dart';
import '../../common/page_ids.dart';
import '../../common/view/back_gesture.dart';
import '../../common/view/global_keys.dart';
import '../../common/view/header_bar.dart';
import '../../common/view/icons.dart';
import '../../common/view/ui_constants.dart';
import '../../extensions/build_context_x.dart';
import '../../library/library_model.dart';
import '../../settings/view/settings_action.dart';
import 'master_items.dart';
import 'master_tile.dart';
import 'create_master_items.dart';
import 'master_panel.dart';

class MasterDetailPage extends StatelessWidget with WatchItMixin {
class MasterDetailPage extends StatelessWidget {
const MasterDetailPage({super.key});

@override
Widget build(BuildContext context) {
final libraryModel = watchIt<LibraryModel>();
final masterItems = createMasterItems(libraryModel: libraryModel);
final libraryModel = di<LibraryModel>();

final drawer = Drawer(
width: kMasterDetailSideBarWidth,
child: Stack(
children: [
MasterPanel(masterItems: masterItems, libraryModel: libraryModel),
const MasterPanel(),
Positioned(
left: Platform.isMacOS ? 5 : null,
top: 5,
Expand All @@ -55,11 +51,7 @@ class MasterDetailPage extends StatelessWidget with WatchItMixin {
drawer: Platform.isMacOS ? null : drawer,
body: Row(
children: [
if (context.showMasterPanel)
MasterPanel(
masterItems: masterItems,
libraryModel: libraryModel,
),
if (context.showMasterPanel) const MasterPanel(),
if (context.showMasterPanel) const VerticalDivider(),
Expanded(
child: Navigator(
Expand All @@ -68,6 +60,7 @@ class MasterDetailPage extends StatelessWidget with WatchItMixin {
key: libraryModel.masterNavigatorKey,
observers: [libraryModel],
onGenerateRoute: (settings) {
final masterItems = createMasterItems();
final page = (masterItems.firstWhereOrNull(
(e) => e.pageId == settings.name,
) ??
Expand All @@ -88,66 +81,3 @@ class MasterDetailPage extends StatelessWidget with WatchItMixin {
);
}
}

class MasterPanel extends StatelessWidget {
const MasterPanel({
super.key,
required this.masterItems,
required this.libraryModel,
});

final List<MasterItem> masterItems;
final LibraryModel libraryModel;

@override
Widget build(BuildContext context) {
return SizedBox(
width: kMasterDetailSideBarWidth,
child: Column(
children: [
const HeaderBar(
includeBackButton: false,
includeSidebarButton: false,
backgroundColor: Colors.transparent,
style: YaruTitleBarStyle.undecorated,
adaptive: false,
title: Text(AppConfig.appTitle),
),
Expanded(
child: ListView.separated(
itemCount: masterItems.length,
itemBuilder: (context, index) {
final item = masterItems.elementAt(index);
return MasterTile(
key: ValueKey(item.pageId),
onTap: () {
libraryModel.push(pageId: item.pageId);

if (!context.showMasterPanel) {
if (Platform.isMacOS) {
masterScaffoldKey.currentState?.closeEndDrawer();
} else {
masterScaffoldKey.currentState?.closeDrawer();
}
}
},
pageId: item.pageId,
libraryModel: libraryModel,
leading: item.iconBuilder
?.call(libraryModel.selectedPageId == item.pageId),
title: item.titleBuilder(context),
subtitle: item.subtitleBuilder?.call(context),
selected: libraryModel.selectedPageId == item.pageId,
);
},
separatorBuilder: (_, __) => const SizedBox(
height: 5,
),
),
),
const SettingsButton.tile(),
],
),
);
}
}
Loading