canal -> brook

This commit is contained in:
Henry Hiles 2025-01-04 12:14:46 -05:00
commit a6f2291309
25 changed files with 169 additions and 129 deletions

View file

@ -1 +1 @@
# Canal # Brook

View file

@ -6,7 +6,7 @@ plugins {
} }
android { android {
namespace = "com.example.canal" namespace = "com.example.brook"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
@ -21,7 +21,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.canal" applicationId = "com.example.brook"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdk = flutter.minSdkVersion minSdk = flutter.minSdkVersion

View file

@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application <application
android:label="canal" android:label="brook"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity

View file

@ -1,4 +1,4 @@
package com.example.canal package com.example.brook
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity

View file

@ -1,4 +1,4 @@
import 'package:canal/widgets/loading.dart'; import 'package:brook/widgets/loading.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';

View file

@ -1,6 +1,6 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:canal/widgets/app.dart'; import 'package:brook/widgets/app.dart';
import 'package:window_size/window_size.dart'; import 'package:window_size/window_size.dart';
import 'package:yaru/yaru.dart'; import 'package:yaru/yaru.dart';

View file

@ -1,7 +1,7 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import "package:riverpod_annotation/riverpod_annotation.dart"; import "package:riverpod_annotation/riverpod_annotation.dart";
import 'package:canal/models/decorations.dart'; import 'package:brook/models/decorations.dart';
import 'package:canal/providers/button_layout_provider.dart'; import 'package:brook/providers/button_layout_provider.dart';
import 'package:yaru/yaru.dart'; import 'package:yaru/yaru.dart';
import "package:collection/collection.dart"; import "package:collection/collection.dart";
part 'decorations_provider.g.dart'; part 'decorations_provider.g.dart';

View file

@ -1,4 +1,4 @@
import 'package:canal/providers/ytmusic_provider.dart'; import 'package:brook/providers/ytmusic_provider.dart';
import 'package:dart_ytmusic_api/dart_ytmusic_api.dart'; import 'package:dart_ytmusic_api/dart_ytmusic_api.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';

View file

@ -1,23 +1,27 @@
import 'package:canal/models/search_type.dart'; import 'package:brook/models/search_type.dart';
import 'package:dart_ytmusic_api/types.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:canal/providers/ytmusic_provider.dart'; import 'package:brook/providers/ytmusic_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart';
part "search_provider.g.dart"; part "search_provider.g.dart";
@riverpod @riverpod
Future<IList<dynamic>> searchProvider( Future<IList<SearchResult>> searchProvider(
Ref ref, { Ref ref, {
required String search, required String search,
required SearchType searchType, required SearchType searchType,
}) async { }) async {
final yt = await ytmusic(ref); final yt = await ytmusic(ref);
return IList(switch (searchType) { return IList(
SearchType.any => await yt.search(search), switch (searchType) {
SearchType.any =>
await yt.search(search).then((search) => search.cast<SearchResult>()),
SearchType.songs => await yt.searchSongs(search), SearchType.songs => await yt.searchSongs(search),
SearchType.albums => await yt.searchAlbums(search), SearchType.albums => await yt.searchAlbums(search),
SearchType.videos => await yt.searchVideos(search), SearchType.videos => await yt.searchVideos(search),
SearchType.artists => await yt.searchAlbums(search), SearchType.artists => await yt.searchAlbums(search),
SearchType.playlists => await yt.searchPlaylists(search), SearchType.playlists => await yt.searchPlaylists(search),
}); },
);
} }

View file

@ -1,4 +1,4 @@
import 'package:canal/widgets/appbar.dart'; import 'package:brook/widgets/appbar.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AlbumPage extends StatelessWidget { class AlbumPage extends StatelessWidget {

View file

@ -1,4 +1,4 @@
import 'package:canal/widgets/appbar.dart'; import 'package:brook/widgets/appbar.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class PlaylistPage extends StatelessWidget { class PlaylistPage extends StatelessWidget {

View file

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:canal/models/tab.dart'; import 'package:brook/models/tab.dart';
class AccountTab extends StatelessWidget implements TabPage { class AccountTab extends StatelessWidget implements TabPage {
const AccountTab({super.key}); const AccountTab({super.key});

View file

@ -1,11 +1,11 @@
import 'package:canal/helpers/extension_helper.dart'; import 'package:brook/helpers/extension_helper.dart';
import 'package:canal/providers/home_sections_provider.dart'; import 'package:brook/providers/home_sections_provider.dart';
import 'package:canal/screens/album_page.dart'; import 'package:brook/screens/album_page.dart';
import 'package:canal/screens/playlist_page.dart'; import 'package:brook/screens/playlist_page.dart';
import 'package:canal/widgets/thumbnail.dart'; import 'package:brook/widgets/thumbnail.dart';
import 'package:dart_ytmusic_api/dart_ytmusic_api.dart'; import 'package:dart_ytmusic_api/dart_ytmusic_api.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:canal/models/tab.dart'; import 'package:brook/models/tab.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:yaru/yaru.dart'; import 'package:yaru/yaru.dart';

View file

@ -1,11 +1,12 @@
import 'package:canal/helpers/extension_helper.dart'; import 'package:brook/helpers/extension_helper.dart';
import 'package:canal/providers/search_provider.dart'; import 'package:collection/collection.dart';
import 'package:canal/widgets/thumbnail.dart'; import 'package:brook/providers/search_provider.dart';
import 'package:brook/widgets/thumbnail.dart';
import 'package:dart_ytmusic_api/types.dart'; import 'package:dart_ytmusic_api/types.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:canal/widgets/select_button.dart'; import 'package:brook/widgets/select_button.dart';
import 'package:canal/models/search_type.dart'; import 'package:brook/models/search_type.dart';
import 'package:canal/models/tab.dart'; import 'package:brook/models/tab.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:yaru/yaru.dart'; import 'package:yaru/yaru.dart';
@ -23,7 +24,6 @@ class SearchTab extends HookConsumerWidget implements TabPage {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final type = useState(SearchType.any); final type = useState(SearchType.any);
final search = useState(""); final search = useState("");
final debouncedSearch = useDebounced(search, Duration(milliseconds: 250));
return ListView( return ListView(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
@ -45,18 +45,37 @@ class SearchTab extends HookConsumerWidget implements TabPage {
SizedBox(height: 8), SizedBox(height: 8),
ref ref
.watch(searchProviderProvider( .watch(searchProviderProvider(
search: search.value, searchType: type.value)) search: search.value,
searchType: type.value,
))
.betterWhen( .betterWhen(
data: (results) => Wrap( data: (results) => Column(
children: results children: results
.map((result) => SizedBox( .mapIndexed((index, result) => switch (result) {
height: 64, SongDetailed _ => Builder(builder: (_) {
width: 64, final padding =
child: switch (result) { EdgeInsets.symmetric(horizontal: 16);
SongDetailed _ => Thumbnail( final leading = Thumbnail(
url: result.thumbnails.first.url, url: result.thumbnails.first.url,
onClick: () {}, onClick: () {},
);
return index == 0
? SizedBox(
height: 128,
child: YaruBanner.tile(
padding: padding,
icon: leading,
title: Text(result.name),
subtitle: Text(result.artist.name),
), ),
)
: YaruTile(
padding: padding,
leading: leading,
title: Text(result.name),
subtitle: Text(result.artist.name),
);
}),
AlbumDetailed _ => Thumbnail( AlbumDetailed _ => Thumbnail(
url: result.thumbnails.first.url, url: result.thumbnails.first.url,
onClick: () {}, onClick: () {},
@ -64,6 +83,7 @@ class SearchTab extends HookConsumerWidget implements TabPage {
VideoDetailed _ => Thumbnail( VideoDetailed _ => Thumbnail(
url: result.thumbnails.first.url, url: result.thumbnails.first.url,
onClick: () {}, onClick: () {},
radius: 0,
), ),
ArtistDetailed _ => Thumbnail( ArtistDetailed _ => Thumbnail(
url: result.thumbnails.first.url, url: result.thumbnails.first.url,
@ -76,8 +96,14 @@ class SearchTab extends HookConsumerWidget implements TabPage {
_ => throw Exception( _ => throw Exception(
"Unknown Detailed Result: ${result.runtimeType}", "Unknown Detailed Result: ${result.runtimeType}",
), ),
})) })
.toList())), .map((element) => Padding(
padding: EdgeInsets.only(bottom: 16),
child: element,
))
.toList(),
),
),
], ],
); );
} }

View file

@ -1,18 +1,18 @@
import 'package:adwaita/adwaita.dart'; import 'package:adwaita/adwaita.dart';
import 'package:canal/helpers/extension_helper.dart'; import 'package:brook/helpers/extension_helper.dart';
import 'package:canal/models/tab.dart'; import 'package:brook/models/tab.dart';
import 'package:canal/providers/ytmusic_provider.dart'; import 'package:brook/providers/ytmusic_provider.dart';
import 'package:canal/screens/tabs/account.dart'; import 'package:brook/screens/tabs/account.dart';
import 'package:canal/screens/tabs/home.dart'; import 'package:brook/screens/tabs/home.dart';
import 'package:canal/screens/tabs/search.dart'; import 'package:brook/screens/tabs/search.dart';
import 'package:canal/widgets/appbar.dart'; import 'package:brook/widgets/appbar.dart';
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:yaru/yaru.dart'; import 'package:yaru/yaru.dart';
import 'package:canal/providers/button_layout_provider.dart'; import 'package:brook/providers/button_layout_provider.dart';
import 'package:canal/providers/warmup_provider.dart'; import 'package:brook/providers/warmup_provider.dart';
const List<TabPage> tabs = [HomeTab(), SearchTab(), AccountTab()]; const List<TabPage> tabs = [HomeTab(), SearchTab(), AccountTab()];
@ -31,7 +31,7 @@ class App extends HookConsumerWidget {
]))) ])))
.betterWhen( .betterWhen(
data: (_) => YaruDetailPage( data: (_) => YaruDetailPage(
appBar: const Appbar(title: "Canal"), appBar: const Appbar(title: "Brook"),
body: tabs[selected.value], body: tabs[selected.value],
bottomNavigationBar: NavigationBar( bottomNavigationBar: NavigationBar(
destinations: tabs destinations: tabs

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:canal/providers/decorations_provider.dart'; import 'package:brook/providers/decorations_provider.dart';
import 'package:yaru/yaru.dart'; import 'package:yaru/yaru.dart';
class Appbar extends ConsumerWidget implements PreferredSizeWidget { class Appbar extends ConsumerWidget implements PreferredSizeWidget {

View file

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:canal/models/package.dart'; import 'package:brook/models/package.dart';
class PackageCard extends StatelessWidget { class PackageCard extends StatelessWidget {
final Package package; final Package package;

View file

@ -1,4 +1,4 @@
import 'package:canal/helpers/extension_helper.dart'; import 'package:brook/helpers/extension_helper.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';

View file

@ -3,11 +3,17 @@ import 'package:flutter/material.dart';
class Thumbnail extends StatelessWidget { class Thumbnail extends StatelessWidget {
final String url; final String url;
final VoidCallback onClick; final VoidCallback onClick;
const Thumbnail({required this.url, required this.onClick, super.key}); final double radius;
const Thumbnail({
super.key,
required this.url,
required this.onClick,
this.radius = 16,
});
@override @override
Widget build(BuildContext context) => ClipRRect( Widget build(BuildContext context) => ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(16)), borderRadius: BorderRadius.all(Radius.circular(radius)),
child: InkWell( child: InkWell(
onTap: onClick, onTap: onClick,
child: Image.network( child: Image.network(

View file

@ -4,10 +4,10 @@ project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change # The name of the executable created for the application. Change this to change
# the on-disk name of your application. # the on-disk name of your application.
set(BINARY_NAME "canal") set(BINARY_NAME "brook")
# The unique GTK application identifier for this application. See: # The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID # https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.canal") set(APPLICATION_ID "com.example.brook")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake. # versions of CMake.

View file

@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) {
if (use_header_bar) { if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar)); gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "canal"); gtk_header_bar_set_title(header_bar, "brook");
gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else { } else {
gtk_window_set_title(window, "canal"); gtk_window_set_title(window, "brook");
} }
gtk_window_set_default_size(window, 1280, 720); gtk_window_set_default_size(window, 1280, 720);

View file

@ -202,10 +202,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: collection name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.18.0" version: "1.19.1"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -281,10 +281,11 @@ packages:
dart_ytmusic_api: dart_ytmusic_api:
dependency: "direct main" dependency: "direct main"
description: description:
name: dart_ytmusic_api path: "."
sha256: "1e35f07c69a36eab672f37815bc0f644328c06cda51404e30fa90a0412023135" ref: HEAD
url: "https://pub.dev" resolved-ref: "2f3c6f7d385cf10827780e03bfaf133f3b8221a3"
source: hosted url: "https://github.com/Henry-Hiles/dart_ytmusic_api"
source: git
version: "1.0.8" version: "1.0.8"
dbus: dbus:
dependency: transitive dependency: transitive
@ -477,10 +478,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: http_parser name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.1.2"
image: image:
dependency: transitive dependency: transitive
description: description:
@ -490,13 +491,13 @@ packages:
source: hosted source: hosted
version: "4.5.2" version: "4.5.2"
intl: intl:
dependency: transitive dependency: "direct overridden"
description: description:
name: intl name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.19.0" version: "0.20.1"
io: io:
dependency: transitive dependency: transitive
description: description:
@ -570,13 +571,13 @@ packages:
source: hosted source: hosted
version: "0.11.1" version: "0.11.1"
meta: meta:
dependency: "direct overridden" dependency: transitive
description: description:
name: meta name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.16.0" version: "1.15.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -765,10 +766,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shelf name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.1" version: "1.4.2"
shelf_web_socket: shelf_web_socket:
dependency: transitive dependency: transitive
description: description:

View file

@ -1,4 +1,4 @@
name: canal name: brook
description: "A music player" description: "A music player"
publish_to: "none" publish_to: "none"
version: 1.0.0+1 version: 1.0.0+1
@ -7,8 +7,8 @@ environment:
sdk: "^3.5.4" sdk: "^3.5.4"
dependencies: dependencies:
collection: ^1.19.1
adwaita: ^1.1.0 adwaita: ^1.1.0
collection: ^1.18.0
fast_immutable_collections: ^11.0.2 fast_immutable_collections: ^11.0.2
flutter: flutter:
sdk: flutter sdk: flutter
@ -25,10 +25,13 @@ dependencies:
git: git:
url: https://github.com/google/flutter-desktop-embedding url: https://github.com/google/flutter-desktop-embedding
path: plugins/window_size path: plugins/window_size
dart_ytmusic_api: ^1.0.8 dart_ytmusic_api:
git:
url: https://github.com/Henry-Hiles/dart_ytmusic_api
dependency_overrides: dependency_overrides:
meta: ^1.15.0 collection: ^1.19.1
intl: ^0.20.1
dev_dependencies: dev_dependencies:
build_runner: ^2.4.11 build_runner: ^2.4.11

View file

@ -14,23 +14,23 @@
This is a placeholder for base href that will be replaced by the value of This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`. the `--base-href` argument provided to `flutter build`.
--> -->
<base href="$FLUTTER_BASE_HREF"> <base href="$FLUTTER_BASE_HREF" />
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible" />
<meta name="description" content="A new Flutter project."> <meta name="description" content="A new Flutter project." />
<!-- iOS meta tags & icons --> <!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="canal"> <meta name="apple-mobile-web-app-title" content="brook" />
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png" />
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png" /> <link rel="icon" type="image/png" href="favicon.png" />
<title>canal</title> <title>brook</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json" />
</head> </head>
<body> <body>
<script src="flutter_bootstrap.js" async></script> <script src="flutter_bootstrap.js" async></script>

View file

@ -1,6 +1,6 @@
{ {
"name": "canal", "name": "brook",
"short_name": "canal", "short_name": "brook",
"start_url": ".", "start_url": ".",
"display": "standalone", "display": "standalone",
"background_color": "#0175C2", "background_color": "#0175C2",