mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2025-12-10 21:19:29 +00:00
responsive home_page
This commit is contained in:
parent
a720c977c2
commit
ebc14a0448
9 changed files with 171 additions and 85 deletions
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
|
|
@ -4,6 +4,7 @@
|
|||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "whispering_pages",
|
||||
"request": "launch",
|
||||
|
|
|
|||
|
|
@ -20,11 +20,17 @@ class CoverImage extends _$CoverImage {
|
|||
Stream<Uint8List> build(LibraryItem libraryItem) async* {
|
||||
final api = ref.watch(authenticatedApiProvider);
|
||||
|
||||
// ! artifical delay for testing
|
||||
// await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
// try to get the image from the cache
|
||||
final file = await imageCacheManager.getFileFromCache(libraryItem.id);
|
||||
|
||||
if (file != null) {
|
||||
// if the image is in the cache, yield it
|
||||
debugPrint(
|
||||
'cover image found in cache for ${libraryItem.id} at ${file.file.path}',
|
||||
);
|
||||
yield await file.file.readAsBytes();
|
||||
// return if no need to fetch from the server
|
||||
if (libraryItem.updatedAt.isBefore(await file.file.lastModified())) {
|
||||
|
|
@ -39,7 +45,7 @@ class CoverImage extends _$CoverImage {
|
|||
// check if the image is in the cache
|
||||
final coverImage = await api.items.getCover(
|
||||
libraryItemId: libraryItem.id,
|
||||
parameters: const GetImageReqParams(width: 500),
|
||||
parameters: const GetImageReqParams(width: 1000),
|
||||
);
|
||||
// save the image to the cache
|
||||
final newFile = await imageCacheManager.putFile(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'image_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$coverImageHash() => r'34c6aaf6831fea198984d22ecdf2c5b74e110891';
|
||||
String _$coverImageHash() => r'57a164772b0350cd451535ed9d6347ff74671d2e';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -38,19 +38,16 @@ class HomePage extends HookConsumerWidget {
|
|||
child: views.when(
|
||||
data: (data) {
|
||||
final shelvesToDisplay = data
|
||||
.where((element) => !element.id.contains('discover'))
|
||||
.map(
|
||||
(shelf) => HomeShelf(
|
||||
title: Text(shelf.label),
|
||||
shelf: shelf,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
// .where((element) => !element.id.contains('discover'))
|
||||
.map((shelf) {
|
||||
debugPrint('building shelf ${shelf.label}');
|
||||
return HomeShelf(
|
||||
title: shelf.label,
|
||||
shelf: shelf,
|
||||
);
|
||||
}).toList();
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
// await ref
|
||||
// .read(personalizedViewProvider.notifier)
|
||||
// .forceRefresh();
|
||||
return ref.refresh(personalizedViewProvider);
|
||||
},
|
||||
child: ListView.separated(
|
||||
|
|
@ -65,7 +62,7 @@ class HomePage extends HookConsumerWidget {
|
|||
),
|
||||
);
|
||||
},
|
||||
loading: () => const CircularProgressIndicator(),
|
||||
loading: () => const HomePageSkeleton(),
|
||||
error: (error, stack) {
|
||||
return Text('Error: $error');
|
||||
},
|
||||
|
|
@ -74,3 +71,17 @@ class HomePage extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class HomePageSkeleton extends StatelessWidget {
|
||||
const HomePageSkeleton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class AuthorHomeShelf extends HookConsumerWidget {
|
|||
required this.title,
|
||||
});
|
||||
|
||||
final Widget title;
|
||||
final String title;
|
||||
final AuthorShelf shelf;
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:auto_scroll_text/auto_scroll_text.dart';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||
import 'package:shimmer/shimmer.dart' show Shimmer, ShimmerDirection;
|
||||
import 'package:whispering_pages/api/image_provider.dart';
|
||||
import 'package:whispering_pages/widgets/shelves/home_shelf.dart';
|
||||
|
||||
|
|
@ -13,7 +15,7 @@ class BookHomeShelf extends HookConsumerWidget {
|
|||
required this.title,
|
||||
});
|
||||
|
||||
final Widget title;
|
||||
final String title;
|
||||
final LibraryItemShelf shelf;
|
||||
|
||||
@override
|
||||
|
|
@ -49,69 +51,94 @@ class BookOnShelf extends HookConsumerWidget {
|
|||
final book = BookMinified.fromJson(item.media.toJson());
|
||||
final metadata = BookMetadataMinified.fromJson(book.metadata.toJson());
|
||||
final coverImage = ref.watch(coverImageProvider(item));
|
||||
const coverSize = 150.0;
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(right: 10, bottom: 10),
|
||||
constraints: const BoxConstraints(maxWidth: coverSize),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: coverSize),
|
||||
color: Colors.grey[800],
|
||||
child: coverImage.when(
|
||||
data: (image) {
|
||||
if (image.isEmpty) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final height = min(constraints.maxHeight, 500);
|
||||
final width = height * 0.75;
|
||||
return SizedBox(
|
||||
width: width,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// the cover image of the book
|
||||
// take up remaining space
|
||||
Expanded(
|
||||
// border radius
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: coverImage.when(
|
||||
data: (image) {
|
||||
// return const BookCoverSkeleton();
|
||||
if (image.isEmpty) {
|
||||
return const Icon(Icons.error);
|
||||
}
|
||||
// cover 80% of parent height
|
||||
return Image.memory(
|
||||
image,
|
||||
fit: BoxFit.cover,
|
||||
cacheWidth:
|
||||
(height * MediaQuery.of(context).devicePixelRatio)
|
||||
.round(),
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return const Center(child: BookCoverSkeleton());
|
||||
},
|
||||
error: (error, stack) {
|
||||
return const Icon(Icons.error);
|
||||
}
|
||||
return Image.memory(
|
||||
image,
|
||||
fit: BoxFit.cover,
|
||||
cacheWidth:
|
||||
(coverSize * MediaQuery.of(context).devicePixelRatio)
|
||||
.round(),
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
error: (error, stack) {
|
||||
return const Icon(Icons.error);
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// the title and author of the book
|
||||
// AutoScrollText(
|
||||
Text(
|
||||
metadata.title ?? '',
|
||||
// mode: AutoScrollTextMode.bouncing,
|
||||
// curve: Curves.easeInOut,
|
||||
// velocity: const Velocity(pixelsPerSecond: Offset(15, 0)),
|
||||
// delayBefore: const Duration(seconds: 2),
|
||||
// pauseBetween: const Duration(seconds: 2),
|
||||
// numberOfReps: 15,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
metadata.authorName ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.all(5),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoScrollText(
|
||||
metadata.title ?? '',
|
||||
mode: AutoScrollTextMode.bouncing,
|
||||
curve: Curves.easeInOut,
|
||||
velocity: const Velocity(pixelsPerSecond: Offset(15, 0)),
|
||||
delayBefore: const Duration(seconds: 2),
|
||||
pauseBetween: const Duration(seconds: 2),
|
||||
numberOfReps: 15,
|
||||
// maxLines: 1,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
metadata.authorName ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// a skeleton for the book cover
|
||||
class BookCoverSkeleton extends StatelessWidget {
|
||||
const BookCoverSkeleton({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: SizedBox(
|
||||
width: 150,
|
||||
child: Shimmer.fromColors(
|
||||
baseColor: Theme.of(context).colorScheme.surface.withOpacity(0.3),
|
||||
highlightColor:
|
||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.1),
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||
|
|
@ -14,7 +16,7 @@ class HomeShelf extends HookConsumerWidget {
|
|||
required this.title,
|
||||
});
|
||||
|
||||
final Widget title;
|
||||
final String title;
|
||||
final Shelf shelf;
|
||||
|
||||
@override
|
||||
|
|
@ -33,30 +35,46 @@ class HomeShelf extends HookConsumerWidget {
|
|||
}
|
||||
}
|
||||
|
||||
/// A shelf that displays books on the home page
|
||||
/// A shelf that displays children on the home page
|
||||
class SimpleHomeShelf extends HookConsumerWidget {
|
||||
const SimpleHomeShelf({
|
||||
super.key,
|
||||
required this.children,
|
||||
required this.title,
|
||||
this.height,
|
||||
});
|
||||
|
||||
final Widget title;
|
||||
/// the title of the shelf
|
||||
final String title;
|
||||
|
||||
/// the children to display on the shelf
|
||||
final List<Widget> children;
|
||||
final double? height;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// if height is null take up 30% of the smallest screen dimension
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
title,
|
||||
Text(title, style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 16),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: children,
|
||||
SizedBox(
|
||||
height: max(
|
||||
min(
|
||||
height ?? 0.3 * MediaQuery.of(context).size.shortestSide,
|
||||
200.0,
|
||||
),
|
||||
150.0,
|
||||
),
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) => children[index],
|
||||
separatorBuilder: (context, index) => const SizedBox(width: 16),
|
||||
itemCount: children.length,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
21
pubspec.lock
21
pubspec.lock
|
|
@ -379,6 +379,11 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
freezed:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
|
@ -411,6 +416,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
go_router:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: go_router
|
||||
sha256: "9e0f7d1a3e7dc5010903e330fbc5497872c4c3cf6626381d69083cc1d5113c1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.0.2"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -786,6 +799,14 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
shimmer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shimmer
|
||||
sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
|
|
|||
|
|
@ -44,10 +44,11 @@ dependencies:
|
|||
flutter_hooks: ^0.20.5
|
||||
flutter_settings_ui: ^3.0.1
|
||||
freezed_annotation: ^2.4.1
|
||||
go_router: ^14.0.2
|
||||
hive: ^4.0.0-dev.2
|
||||
hooks_riverpod: ^2.5.1
|
||||
isar: *isar_version
|
||||
isar_flutter_libs: *isar_version # contains Isar Core
|
||||
isar: ^4.0.0-dev.13
|
||||
isar_flutter_libs: ^4.0.0-dev.13
|
||||
json_annotation: ^4.9.0
|
||||
lottie: ^3.1.0
|
||||
path: ^1.9.0
|
||||
|
|
@ -56,6 +57,7 @@ dependencies:
|
|||
scroll_loop_auto_scroll: ^0.0.5
|
||||
shelfsdk:
|
||||
path: ../../_dart/shelfsdk
|
||||
shimmer: ^3.0.0
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.9
|
||||
custom_lint: ^0.6.4
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue