mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
feat(mobile): pull to refresh support in all refreshable list views
This commit is contained in:
parent
025c1ae204
commit
9f959ce77c
@ -3,13 +3,15 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|||||||
|
|
||||||
abstract class Env {
|
abstract class Env {
|
||||||
static final String pocketbaseUrl =
|
static final String pocketbaseUrl =
|
||||||
dotenv.get('POCKETBASE_URL', fallback: 'http://localhost:8090');
|
dotenv.get('POCKETBASE_URL', fallback: 'http://127.0.0.1:8090');
|
||||||
static final String username = dotenv.get('USERNAME', fallback: 'root');
|
static final String username = dotenv.get('USERNAME', fallback: 'root');
|
||||||
static final String password = dotenv.get('PASSWORD', fallback: '12345678');
|
static final String password = dotenv.get('PASSWORD', fallback: '12345678');
|
||||||
|
|
||||||
static configure() async {
|
static configure() async {
|
||||||
if (kReleaseMode) {
|
if (kReleaseMode) {
|
||||||
await dotenv.load(fileName: ".env");
|
await dotenv.load(fileName: ".env");
|
||||||
|
} else {
|
||||||
|
dotenv.testLoad();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,33 +62,39 @@ class UserAlbums extends HookConsumerWidget {
|
|||||||
return const Center(child: ShimmerPlaybuttonCard(count: 7));
|
return const Center(child: ShimmerPlaybuttonCard(count: 7));
|
||||||
}
|
}
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return RefreshIndicator(
|
||||||
child: Material(
|
onRefresh: () async {
|
||||||
type: MaterialType.transparency,
|
await albumsQuery.refetch();
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
},
|
||||||
color: PlatformTheme.of(context).scaffoldBackgroundColor,
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
padding: const EdgeInsets.all(8.0),
|
child: Material(
|
||||||
child: Column(
|
type: MaterialType.transparency,
|
||||||
children: [
|
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
||||||
PlatformTextField(
|
color: PlatformTheme.of(context).scaffoldBackgroundColor,
|
||||||
onChanged: (value) => searchText.value = value,
|
child: Padding(
|
||||||
prefixIcon: SpotubeIcons.filter,
|
padding: const EdgeInsets.all(8.0),
|
||||||
placeholder: 'Filter Albums...',
|
child: Column(
|
||||||
),
|
children: [
|
||||||
const SizedBox(height: 20),
|
PlatformTextField(
|
||||||
Wrap(
|
onChanged: (value) => searchText.value = value,
|
||||||
spacing: spacing, // gap between adjacent chips
|
prefixIcon: SpotubeIcons.filter,
|
||||||
runSpacing: 20, // gap between lines
|
placeholder: 'Filter Albums...',
|
||||||
alignment: WrapAlignment.center,
|
),
|
||||||
children: albums
|
const SizedBox(height: 20),
|
||||||
.map((album) => AlbumCard(
|
Wrap(
|
||||||
viewType: viewType,
|
spacing: spacing, // gap between adjacent chips
|
||||||
TypeConversionUtils.simpleAlbum_X_Album(album),
|
runSpacing: 20, // gap between lines
|
||||||
))
|
alignment: WrapAlignment.center,
|
||||||
.toList(),
|
children: albums
|
||||||
),
|
.map((album) => AlbumCard(
|
||||||
],
|
viewType: viewType,
|
||||||
|
TypeConversionUtils.simpleAlbum_X_Album(album),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -83,30 +83,36 @@ class UserArtists extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: GridView.builder(
|
: RefreshIndicator(
|
||||||
itemCount: filteredArtists.length,
|
onRefresh: () async {
|
||||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
await artistQuery.refetchPages();
|
||||||
maxCrossAxisExtent: 200,
|
|
||||||
mainAxisExtent: 250,
|
|
||||||
crossAxisSpacing: 20,
|
|
||||||
mainAxisSpacing: 20,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return HookBuilder(builder: (context) {
|
|
||||||
if (index == artistQuery.pages.length - 1 && hasNextPage) {
|
|
||||||
return Waypoint(
|
|
||||||
controller: useScrollController(),
|
|
||||||
isGrid: true,
|
|
||||||
onTouchEdge: () {
|
|
||||||
artistQuery.fetchNextPage();
|
|
||||||
},
|
|
||||||
child: ArtistCard(filteredArtists[index]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return ArtistCard(filteredArtists[index]);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
child: GridView.builder(
|
||||||
|
itemCount: filteredArtists.length,
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
maxCrossAxisExtent: 200,
|
||||||
|
mainAxisExtent: 250,
|
||||||
|
crossAxisSpacing: 20,
|
||||||
|
mainAxisSpacing: 20,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return HookBuilder(builder: (context) {
|
||||||
|
if (index == artistQuery.pages.length - 1 && hasNextPage) {
|
||||||
|
return Waypoint(
|
||||||
|
controller: useScrollController(),
|
||||||
|
isGrid: true,
|
||||||
|
onTouchEdge: () {
|
||||||
|
artistQuery.fetchNextPage();
|
||||||
|
},
|
||||||
|
child: ArtistCard(filteredArtists[index]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ArtistCard(filteredArtists[index]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ import 'package:spotube/components/shared/shimmers/shimmer_track_tile.dart';
|
|||||||
import 'package:spotube/components/shared/sort_tracks_dropdown.dart';
|
import 'package:spotube/components/shared/sort_tracks_dropdown.dart';
|
||||||
import 'package:spotube/components/shared/track_table/track_tile.dart';
|
import 'package:spotube/components/shared/track_table/track_tile.dart';
|
||||||
import 'package:spotube/hooks/use_async_effect.dart';
|
import 'package:spotube/hooks/use_async_effect.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
|
||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
import 'package:spotube/provider/playlist_queue_provider.dart';
|
import 'package:spotube/provider/playlist_queue_provider.dart';
|
||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
@ -162,7 +161,6 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
trackSnapshot.value ?? [],
|
trackSnapshot.value ?? [],
|
||||||
);
|
);
|
||||||
final isMounted = useIsMounted();
|
final isMounted = useIsMounted();
|
||||||
final breakpoint = useBreakpoints();
|
|
||||||
|
|
||||||
final searchText = useState<String>("");
|
final searchText = useState<String>("");
|
||||||
|
|
||||||
@ -261,28 +259,34 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
}, [searchText.value, sortedTracks]);
|
}, [searchText.value, sortedTracks]);
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: ListView.builder(
|
child: RefreshIndicator(
|
||||||
itemCount: filteredTracks.length,
|
onRefresh: () async {
|
||||||
itemBuilder: (context, index) {
|
ref.refresh(localTracksProvider);
|
||||||
final track = filteredTracks[index];
|
|
||||||
return TrackTile(
|
|
||||||
playlist,
|
|
||||||
duration:
|
|
||||||
"${track.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.duration?.inSeconds.remainder(60) ?? 0)}",
|
|
||||||
track: MapEntry(index, track),
|
|
||||||
isActive: playlist?.activeTrack.id == track.id,
|
|
||||||
isChecked: false,
|
|
||||||
showCheck: false,
|
|
||||||
isLocal: true,
|
|
||||||
onTrackPlayButtonPressed: (currentTrack) {
|
|
||||||
return playLocalTracks(
|
|
||||||
playlistNotifier,
|
|
||||||
sortedTracks,
|
|
||||||
currentTrack: track,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
child: ListView.builder(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
itemCount: filteredTracks.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final track = filteredTracks[index];
|
||||||
|
return TrackTile(
|
||||||
|
playlist,
|
||||||
|
duration:
|
||||||
|
"${track.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.duration?.inSeconds.remainder(60) ?? 0)}",
|
||||||
|
track: MapEntry(index, track),
|
||||||
|
isActive: playlist?.activeTrack.id == track.id,
|
||||||
|
isChecked: false,
|
||||||
|
showCheck: false,
|
||||||
|
isLocal: true,
|
||||||
|
onTrackPlayButtonPressed: (currentTrack) {
|
||||||
|
return playLocalTracks(
|
||||||
|
playlistNotifier,
|
||||||
|
sortedTracks,
|
||||||
|
currentTrack: track,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -89,34 +89,38 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
];
|
];
|
||||||
return SingleChildScrollView(
|
return RefreshIndicator(
|
||||||
child: Material(
|
onRefresh: () => playlistsQuery.refetch(),
|
||||||
type: MaterialType.transparency,
|
child: SingleChildScrollView(
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
child: Padding(
|
child: Material(
|
||||||
padding: const EdgeInsets.all(8.0),
|
type: MaterialType.transparency,
|
||||||
child: Column(
|
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
||||||
children: [
|
child: Padding(
|
||||||
PlatformTextField(
|
padding: const EdgeInsets.all(8.0),
|
||||||
onChanged: (value) => searchText.value = value,
|
child: Column(
|
||||||
placeholder: "Filter your playlists...",
|
children: [
|
||||||
prefixIcon: SpotubeIcons.filter,
|
PlatformTextField(
|
||||||
),
|
onChanged: (value) => searchText.value = value,
|
||||||
const SizedBox(height: 20),
|
placeholder: "Filter your playlists...",
|
||||||
if (playlistsQuery.isLoading || !playlistsQuery.hasData)
|
prefixIcon: SpotubeIcons.filter,
|
||||||
const Center(child: ShimmerPlaybuttonCard(count: 7))
|
|
||||||
else
|
|
||||||
Center(
|
|
||||||
child: Wrap(
|
|
||||||
spacing: spacing, // gap between adjacent chips
|
|
||||||
runSpacing: 20, // gap between lines
|
|
||||||
alignment: breakpoint.isSm
|
|
||||||
? WrapAlignment.center
|
|
||||||
: WrapAlignment.start,
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
const SizedBox(height: 20),
|
||||||
|
if (playlistsQuery.isLoading || !playlistsQuery.hasData)
|
||||||
|
const Center(child: ShimmerPlaybuttonCard(count: 7))
|
||||||
|
else
|
||||||
|
Center(
|
||||||
|
child: Wrap(
|
||||||
|
spacing: spacing, // gap between adjacent chips
|
||||||
|
runSpacing: 20, // gap between lines
|
||||||
|
alignment: breakpoint.isSm
|
||||||
|
? WrapAlignment.center
|
||||||
|
: WrapAlignment.start,
|
||||||
|
children: children,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -208,142 +208,150 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
body: CustomScrollView(
|
body: RefreshIndicator(
|
||||||
controller: controller,
|
onRefresh: () async {
|
||||||
slivers: [
|
await tracksSnapshot.refetch();
|
||||||
SliverAppBar(
|
},
|
||||||
actions: [
|
child: CustomScrollView(
|
||||||
if (kIsMobile)
|
controller: controller,
|
||||||
CompactSearch(
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
onChanged: (value) => searchText.value = value,
|
slivers: [
|
||||||
placeholder: "Search tracks...",
|
SliverAppBar(
|
||||||
iconColor: color?.titleTextColor,
|
actions: [
|
||||||
),
|
if (kIsMobile)
|
||||||
if (collapsed.value) ...buttons,
|
CompactSearch(
|
||||||
],
|
onChanged: (value) => searchText.value = value,
|
||||||
floating: false,
|
placeholder: "Search tracks...",
|
||||||
pinned: true,
|
iconColor: color?.titleTextColor,
|
||||||
expandedHeight: 400,
|
|
||||||
automaticallyImplyLeading: kIsMobile,
|
|
||||||
leading: kIsMobile
|
|
||||||
? PlatformBackButton(color: color?.titleTextColor)
|
|
||||||
: null,
|
|
||||||
iconTheme: IconThemeData(color: color?.titleTextColor),
|
|
||||||
primary: true,
|
|
||||||
backgroundColor: color?.color,
|
|
||||||
title: collapsed.value
|
|
||||||
? PlatformText.headline(
|
|
||||||
title,
|
|
||||||
style: TextStyle(
|
|
||||||
color: color?.titleTextColor,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
centerTitle: true,
|
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
|
||||||
background: DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [
|
|
||||||
color?.color ?? Colors.transparent,
|
|
||||||
Theme.of(context).canvasColor,
|
|
||||||
],
|
|
||||||
begin: const FractionalOffset(0, 0),
|
|
||||||
end: const FractionalOffset(0, 1),
|
|
||||||
tileMode: TileMode.clamp,
|
|
||||||
),
|
),
|
||||||
),
|
if (collapsed.value) ...buttons,
|
||||||
child: Material(
|
],
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
floating: false,
|
||||||
type: MaterialType.transparency,
|
pinned: true,
|
||||||
child: Padding(
|
expandedHeight: 400,
|
||||||
padding: const EdgeInsets.symmetric(
|
automaticallyImplyLeading: kIsMobile,
|
||||||
horizontal: 20,
|
leading: kIsMobile
|
||||||
|
? PlatformBackButton(color: color?.titleTextColor)
|
||||||
|
: null,
|
||||||
|
iconTheme: IconThemeData(color: color?.titleTextColor),
|
||||||
|
primary: true,
|
||||||
|
backgroundColor: color?.color,
|
||||||
|
title: collapsed.value
|
||||||
|
? PlatformText.headline(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
color: color?.titleTextColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
centerTitle: true,
|
||||||
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
|
background: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
color?.color ?? Colors.transparent,
|
||||||
|
Theme.of(context).canvasColor,
|
||||||
|
],
|
||||||
|
begin: const FractionalOffset(0, 0),
|
||||||
|
end: const FractionalOffset(0, 1),
|
||||||
|
tileMode: TileMode.clamp,
|
||||||
),
|
),
|
||||||
child: Wrap(
|
),
|
||||||
spacing: 20,
|
child: Material(
|
||||||
runSpacing: 20,
|
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
type: MaterialType.transparency,
|
||||||
alignment: WrapAlignment.center,
|
child: Padding(
|
||||||
runAlignment: WrapAlignment.center,
|
padding: const EdgeInsets.symmetric(
|
||||||
children: [
|
horizontal: 20,
|
||||||
Container(
|
),
|
||||||
constraints: const BoxConstraints(maxHeight: 200),
|
child: Wrap(
|
||||||
child: ClipRRect(
|
spacing: 20,
|
||||||
borderRadius: BorderRadius.circular(10),
|
runSpacing: 20,
|
||||||
child: UniversalImage(
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
path: titleImage,
|
alignment: WrapAlignment.center,
|
||||||
placeholder: (context, url) {
|
runAlignment: WrapAlignment.center,
|
||||||
return Assets.albumPlaceholder.image();
|
children: [
|
||||||
},
|
Container(
|
||||||
|
constraints:
|
||||||
|
const BoxConstraints(maxHeight: 200),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: UniversalImage(
|
||||||
|
path: titleImage,
|
||||||
|
placeholder: (context, url) {
|
||||||
|
return Assets.albumPlaceholder.image();
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Column(
|
||||||
Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment:
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
PlatformText.headline(
|
PlatformText.headline(
|
||||||
title,
|
title,
|
||||||
style: TextStyle(
|
|
||||||
color: color?.titleTextColor,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (description != null)
|
|
||||||
PlatformText(
|
|
||||||
description!,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: color?.bodyTextColor,
|
color: color?.titleTextColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
if (description != null)
|
||||||
Row(
|
PlatformText(
|
||||||
mainAxisSize: MainAxisSize.min,
|
description!,
|
||||||
children: buttons,
|
style: TextStyle(
|
||||||
),
|
color: color?.bodyTextColor,
|
||||||
],
|
),
|
||||||
)
|
maxLines: 2,
|
||||||
],
|
overflow: TextOverflow.fade,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: buttons,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
HookBuilder(
|
||||||
HookBuilder(
|
builder: (context) {
|
||||||
builder: (context) {
|
if (tracksSnapshot.isLoading || !tracksSnapshot.hasData) {
|
||||||
if (tracksSnapshot.isLoading || !tracksSnapshot.hasData) {
|
return const ShimmerTrackTile();
|
||||||
return const ShimmerTrackTile();
|
} else if (tracksSnapshot.hasError &&
|
||||||
} else if (tracksSnapshot.hasError &&
|
tracksSnapshot.isError) {
|
||||||
tracksSnapshot.isError) {
|
return SliverToBoxAdapter(
|
||||||
return SliverToBoxAdapter(
|
child: PlatformText("Error ${tracksSnapshot.error}"));
|
||||||
child: PlatformText("Error ${tracksSnapshot.error}"));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return TracksTableView(
|
return TracksTableView(
|
||||||
List.from(
|
List.from(
|
||||||
(filteredTracks ?? []).map(
|
(filteredTracks ?? []).map(
|
||||||
(e) {
|
(e) {
|
||||||
if (e is Track) {
|
if (e is Track) {
|
||||||
return e;
|
return e;
|
||||||
} else {
|
} else {
|
||||||
return TypeConversionUtils.simpleTrack_X_Track(
|
return TypeConversionUtils.simpleTrack_X_Track(
|
||||||
e, album!);
|
e, album!);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
onTrackPlayButtonPressed: onPlay,
|
||||||
onTrackPlayButtonPressed: onPlay,
|
playlistId: id,
|
||||||
playlistId: id,
|
userPlaylist: isOwned,
|
||||||
userPlaylist: isOwned,
|
);
|
||||||
);
|
},
|
||||||
},
|
)
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,12 @@ void main(List<String> rawArgs) async {
|
|||||||
'verbose',
|
'verbose',
|
||||||
abbr: 'v',
|
abbr: 'v',
|
||||||
help: 'Verbose mode',
|
help: 'Verbose mode',
|
||||||
|
defaultsTo: !kReleaseMode,
|
||||||
callback: (verbose) {
|
callback: (verbose) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
Platform.environment['VERBOSE'] = 'true';
|
logEnv['VERBOSE'] = 'true';
|
||||||
Platform.environment['DEBUG'] = 'true';
|
logEnv['DEBUG'] = 'true';
|
||||||
Platform.environment['ERROR'] = 'true';
|
logEnv['ERROR'] = 'true';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -102,7 +103,7 @@ void main(List<String> rawArgs) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Catcher(
|
Catcher(
|
||||||
enableLogger: arguments["verbose"] ?? !kReleaseMode,
|
enableLogger: arguments["verbose"],
|
||||||
debugConfig: CatcherOptions(
|
debugConfig: CatcherOptions(
|
||||||
SilentReportMode(),
|
SilentReportMode(),
|
||||||
[
|
[
|
||||||
|
@ -7,6 +7,9 @@ import 'package:path/path.dart' as path;
|
|||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
final _loggerFactory = SpotubeLogger();
|
final _loggerFactory = SpotubeLogger();
|
||||||
|
final logEnv = {
|
||||||
|
if (!kIsWeb) ...Platform.environment,
|
||||||
|
};
|
||||||
|
|
||||||
SpotubeLogger getLogger<T>(T owner) {
|
SpotubeLogger getLogger<T>(T owner) {
|
||||||
_loggerFactory.owner = owner is String ? owner : owner.toString();
|
_loggerFactory.owner = owner is String ? owner : owner.toString();
|
||||||
@ -60,10 +63,9 @@ class SpotubeLogger extends Logger {
|
|||||||
class _SpotubeLogFilter extends DevelopmentFilter {
|
class _SpotubeLogFilter extends DevelopmentFilter {
|
||||||
@override
|
@override
|
||||||
bool shouldLog(LogEvent event) {
|
bool shouldLog(LogEvent event) {
|
||||||
final env = kIsWeb ? {} : Platform.environment;
|
if ((logEnv["DEBUG"] == "true" && event.level == Level.debug) ||
|
||||||
if ((env["DEBUG"] == "true" && event.level == Level.debug) ||
|
(logEnv["VERBOSE"] == "true" && event.level == Level.verbose) ||
|
||||||
(env["VERBOSE"] == "true" && event.level == Level.verbose) ||
|
(logEnv["ERROR"] == "true" && event.level == Level.error)) {
|
||||||
(env["ERROR"] == "true" && event.level == Level.error)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.shouldLog(event);
|
return super.shouldLog(event);
|
||||||
|
@ -92,23 +92,29 @@ class GenrePage extends HookConsumerWidget {
|
|||||||
placeholder: "Filter categories or genres...",
|
placeholder: "Filter categories or genres...",
|
||||||
);
|
);
|
||||||
|
|
||||||
final list = Waypoint(
|
final list = RefreshIndicator(
|
||||||
onTouchEdge: () async {
|
onRefresh: () async {
|
||||||
if (categoriesQuery.hasNextPage && isMounted()) {
|
await categoriesQuery.refetchPages();
|
||||||
await categoriesQuery.fetchNextPage();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
controller: scrollController,
|
child: Waypoint(
|
||||||
child: ListView.builder(
|
onTouchEdge: () async {
|
||||||
controller: scrollController,
|
if (categoriesQuery.hasNextPage && isMounted()) {
|
||||||
itemCount: categories.length,
|
await categoriesQuery.fetchNextPage();
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final category = categories[index];
|
|
||||||
if (searchText.value.isEmpty && index == categories.length - 1) {
|
|
||||||
return const ShimmerCategories();
|
|
||||||
}
|
}
|
||||||
return CategoryCard(category);
|
|
||||||
},
|
},
|
||||||
|
controller: scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
controller: scrollController,
|
||||||
|
itemCount: categories.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final category = categories[index];
|
||||||
|
if (searchText.value.isEmpty && index == categories.length - 1) {
|
||||||
|
return const ShimmerCategories();
|
||||||
|
}
|
||||||
|
return CategoryCard(category);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return PlatformScaffold(
|
return PlatformScaffold(
|
||||||
|
Loading…
Reference in New Issue
Block a user