chore: run dart format
Some checks are pending
Flutter CI & Release / Test (push) Waiting to run
Flutter CI & Release / Build Android APKs (push) Blocked by required conditions
Flutter CI & Release / build_linux (push) Blocked by required conditions
Flutter CI & Release / Create GitHub Release (push) Blocked by required conditions

This commit is contained in:
Dr.Blank 2026-01-10 16:51:05 +05:30
parent a520136e01
commit e23c0b6c5f
No known key found for this signature in database
GPG key ID: BA5F87FF0560C57B
84 changed files with 1565 additions and 1945 deletions

View file

@ -13,10 +13,7 @@ extension TitleCase on Enum {
String get pascalCase {
// capitalize the first letter of each word
return name
.replaceAllMapped(
RegExp(r'([A-Z])'),
(match) => ' ${match.group(0)}',
)
.replaceAllMapped(RegExp(r'([A-Z])'), (match) => ' ${match.group(0)}')
.trim()
.split(' ')
.map((word) => word[0].toUpperCase() + word.substring(1))

View file

@ -47,8 +47,8 @@ extension ShelfConversion on Shelf {
extension UserConversion on User {
UserWithSessionAndMostRecentProgress
get asUserWithSessionAndMostRecentProgress =>
UserWithSessionAndMostRecentProgress.fromJson(toJson());
get asUserWithSessionAndMostRecentProgress =>
UserWithSessionAndMostRecentProgress.fromJson(toJson());
User get asUser => User.fromJson(toJson());
}

View file

@ -80,9 +80,7 @@ extension ObfuscateServer on AudiobookShelfServer {
if (!kReleaseMode) {
return this;
}
return copyWith(
serverUrl: serverUrl.obfuscate(),
);
return copyWith(serverUrl: serverUrl.obfuscate());
}
}
@ -103,10 +101,7 @@ extension ObfuscateRequest on http.BaseRequest {
if (!kReleaseMode) {
return this;
}
return http.Request(
method,
url.obfuscate(),
);
return http.Request(method, url.obfuscate());
}
}
@ -134,9 +129,11 @@ extension ObfuscateResponse on http.Response {
// token regex is `"token": "..."`
return body
.replaceAll(
RegExp(r'(\b\w+@\w+\.\w+\b)|'
r'(\b\d{3}-\d{3}-\d{4}\b)|'
r'(\bhttps?://\S+\b)'),
RegExp(
r'(\b\w+@\w+\.\w+\b)|'
r'(\b\d{3}-\d{3}-\d{4}\b)|'
r'(\bhttps?://\S+\b)',
),
'obfuscated',
)
.replaceAll(
@ -151,9 +148,7 @@ extension ObfuscateLoginResponse on shelfsdk.LoginResponse {
if (!kReleaseMode) {
return this;
}
return copyWith(
user: user.obfuscate(),
);
return copyWith(user: user.obfuscate());
}
}
@ -162,8 +157,6 @@ extension ObfuscateUser on shelfsdk.User {
if (!kReleaseMode) {
return this;
}
return shelfsdk.User.fromJson(
toJson()..['token'] = 'tokenObfuscated',
);
return shelfsdk.User.fromJson(toJson()..['token'] = 'tokenObfuscated');
}
}

View file

@ -2,10 +2,7 @@ import 'package:flutter/material.dart';
extension ToTimeOfDay on Duration {
TimeOfDay toTimeOfDay() {
return TimeOfDay(
hour: inHours % 24,
minute: inMinutes % 60,
);
return TimeOfDay(hour: inHours % 24, minute: inMinutes % 60);
}
}

View file

@ -7,24 +7,18 @@ void useInterval(VoidCallback callback, Duration delay) {
final savedCallback = useRef(callback);
savedCallback.value = callback;
useEffect(
() {
final timer = Timer.periodic(delay, (_) => savedCallback.value());
return timer.cancel;
},
[delay],
);
useEffect(() {
final timer = Timer.periodic(delay, (_) => savedCallback.value());
return timer.cancel;
}, [delay]);
}
void useTimer(VoidCallback callback, Duration delay) {
final savedCallback = useRef(callback);
savedCallback.value = callback;
useEffect(
() {
final timer = Timer(delay, savedCallback.value);
return timer.cancel;
},
[delay],
);
useEffect(() {
final timer = Timer(delay, savedCallback.value);
return timer.cancel;
}, [delay]);
}

View file

@ -24,46 +24,106 @@ class AbsIcons {
static const _kFontFam = 'AbsIcons';
static const String? _kFontPkg = null;
static const IconData audiobookshelf =
IconData(0xe900, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData microphone_2 =
IconData(0xe901, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData microphone_1 =
IconData(0xe902, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData radio =
IconData(0xe903, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData podcast =
IconData(0xe904, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData books_1 =
IconData(0xe905, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData database_2 =
IconData(0xe906, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData headphones =
IconData(0xe910, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData music =
IconData(0xe911, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData video =
IconData(0xe914, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData microphone_3 =
IconData(0xe91e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData book =
IconData(0xe91f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData books_2 =
IconData(0xe920, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData file_picture =
IconData(0xe927, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData database_1 =
IconData(0xe964, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData rocket =
IconData(0xe9a5, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData power =
IconData(0xe9b5, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData star =
IconData(0xe9d9, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData heart =
IconData(0xe9da, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData rss =
IconData(0xea9b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData audiobookshelf = IconData(
0xe900,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData microphone_2 = IconData(
0xe901,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData microphone_1 = IconData(
0xe902,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData radio = IconData(
0xe903,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData podcast = IconData(
0xe904,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData books_1 = IconData(
0xe905,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData database_2 = IconData(
0xe906,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData headphones = IconData(
0xe910,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData music = IconData(
0xe911,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData video = IconData(
0xe914,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData microphone_3 = IconData(
0xe91e,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData book = IconData(
0xe91f,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData books_2 = IconData(
0xe920,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData file_picture = IconData(
0xe927,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData database_1 = IconData(
0xe964,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData rocket = IconData(
0xe9a5,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData power = IconData(
0xe9b5,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData star = IconData(
0xe9d9,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData heart = IconData(
0xe9da,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static const IconData rss = IconData(
0xea9b,
fontFamily: _kFontFam,
fontPackage: _kFontPkg,
);
static final Map<String, IconData> _iconMap = {
'audiobookshelf': audiobookshelf,

View file

@ -52,7 +52,8 @@ class AddNewServer extends HookConsumerWidget {
// do nothing
appLogger.severe('Error parsing URI: $e');
}
final canSubmit = !readOnly &&
final canSubmit =
!readOnly &&
(isServerAliveValue || (allowEmpty && newServerURI.text.isEmpty));
return TextFormField(
readOnly: readOnly,
@ -71,8 +72,9 @@ class AddNewServer extends HookConsumerWidget {
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.8),
),
border: const OutlineInputBorder(),
prefixText:
myController.text.startsWith(httpUrlRegExp) ? '' : 'https://',
prefixText: myController.text.startsWith(httpUrlRegExp)
? ''
: 'https://',
prefixIcon: ServerAliveIcon(server: parsedUri),
// add server button
@ -101,10 +103,7 @@ class AddNewServer extends HookConsumerWidget {
}
class ServerAliveIcon extends HookConsumerWidget {
const ServerAliveIcon({
super.key,
required this.server,
});
const ServerAliveIcon({super.key, required this.server});
final Uri server;
@ -121,8 +120,8 @@ class ServerAliveIcon extends HookConsumerWidget {
message: server.toString().isEmpty
? 'Server Status'
: isServerAliveValue
? 'Server connected'
: 'Cannot connect to server',
? 'Server connected'
: 'Cannot connect to server',
child: server.toString().isEmpty
? Icon(
Icons.cloud_outlined,

View file

@ -4,9 +4,7 @@ import 'package:vaani/features/you/view/server_manager.dart';
import 'package:vaani/router/router.dart';
class MyDrawer extends StatelessWidget {
const MyDrawer({
super.key,
});
const MyDrawer({super.key});
@override
Widget build(BuildContext context) {
@ -16,10 +14,7 @@ class MyDrawer extends StatelessWidget {
const DrawerHeader(
child: Text(
'Vaani',
style: TextStyle(
fontStyle: FontStyle.italic,
fontSize: 30,
),
style: TextStyle(fontStyle: FontStyle.italic, fontSize: 30),
),
),
ListTile(

View file

@ -55,10 +55,7 @@ class ExpandableDescription extends HookWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// header text
Text(
style: textTheme.titleMedium,
title,
),
Text(style: textTheme.titleMedium, title),
// carrot icon
AnimatedRotation(
turns: isDescExpanded.value ? 0.5 : 0,
@ -79,11 +76,7 @@ class ExpandableDescription extends HookWidget {
child: AnimatedSwitcher(
duration: duration * 3,
child: isDescExpanded.value
? Text(
style: textTheme.bodyMedium,
content,
maxLines: null,
)
? Text(style: textTheme.bodyMedium, content, maxLines: null)
: Text(
style: textTheme.bodyMedium,
content,

View file

@ -2,9 +2,6 @@ import 'package:flutter/material.dart';
void showNotImplementedToast(BuildContext context) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Not implemented"),
showCloseIcon: true,
),
const SnackBar(content: Text("Not implemented"), showCloseIcon: true),
);
}

View file

@ -6,11 +6,7 @@ import 'package:vaani/shared/widgets/shelves/home_shelf.dart';
/// A shelf that displays Authors on the home page
class AuthorHomeShelf extends HookConsumerWidget {
const AuthorHomeShelf({
super.key,
required this.shelf,
required this.title,
});
const AuthorHomeShelf({super.key, required this.shelf, required this.title});
final String title;
final AuthorShelf shelf;
@ -20,9 +16,7 @@ class AuthorHomeShelf extends HookConsumerWidget {
return SimpleHomeShelf(
title: title,
children: shelf.entities
.map(
(item) => AuthorOnShelf(item: item),
)
.map((item) => AuthorOnShelf(item: item))
.toList(),
);
}
@ -30,10 +24,7 @@ class AuthorHomeShelf extends HookConsumerWidget {
// a widget to display a item on the shelf
class AuthorOnShelf extends HookConsumerWidget {
const AuthorOnShelf({
super.key,
required this.item,
});
const AuthorOnShelf({super.key, required this.item});
final Author item;

View file

@ -40,11 +40,11 @@ class BookHomeShelf extends HookConsumerWidget {
.map(
(item) => switch (item.mediaType) {
MediaType.book => BookOnShelf(
item: item,
key: ValueKey(shelf.id + item.id),
heroTagSuffix: shelf.id,
showPlayButton: showPlayButton,
),
item: item,
key: ValueKey(shelf.id + item.id),
heroTagSuffix: shelf.id,
showPlayButton: showPlayButton,
),
_ => Container(),
},
)
@ -83,13 +83,8 @@ class BookOnShelf extends HookConsumerWidget {
// open the book
context.pushNamed(
Routes.libraryItem.name,
pathParameters: {
Routes.libraryItem.pathParamName!: item.id,
},
extra: LibraryItemExtras(
book: book,
heroTagSuffix: heroTagSuffix,
),
pathParameters: {Routes.libraryItem.pathParamName!: item.id},
extra: LibraryItemExtras(book: book, heroTagSuffix: heroTagSuffix),
);
}
@ -99,8 +94,11 @@ class BookOnShelf extends HookConsumerWidget {
onTap: handleTapOnBook,
borderRadius: BorderRadius.circular(10),
child: Padding(
padding:
const EdgeInsets.only(bottom: 8.0, right: 4.0, left: 4.0),
padding: const EdgeInsets.only(
bottom: 8.0,
right: 4.0,
left: 4.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -112,7 +110,8 @@ class BookOnShelf extends HookConsumerWidget {
alignment: Alignment.bottomRight,
children: [
Hero(
tag: HeroTagPrefixes.bookCover +
tag:
HeroTagPrefixes.bookCover +
item.id +
heroTagSuffix,
child: ClipRRect(
@ -128,17 +127,19 @@ class BookOnShelf extends HookConsumerWidget {
var imageWidget = Image.memory(
image,
fit: BoxFit.fill,
cacheWidth: (height *
1.2 *
MediaQuery.of(context)
.devicePixelRatio)
.round(),
cacheWidth:
(height *
1.2 *
MediaQuery.of(
context,
).devicePixelRatio)
.round(),
);
return Container(
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.onPrimaryContainer,
color: Theme.of(
context,
).colorScheme.onPrimaryContainer,
),
child: imageWidget,
);
@ -157,9 +158,7 @@ class BookOnShelf extends HookConsumerWidget {
),
// a play button on the book cover
if (showPlayButton)
_BookOnShelfPlayButton(
libraryItemId: item.id,
),
_BookOnShelfPlayButton(libraryItemId: item.id),
],
),
),
@ -202,9 +201,7 @@ class BookOnShelf extends HookConsumerWidget {
}
class _BookOnShelfPlayButton extends HookConsumerWidget {
const _BookOnShelfPlayButton({
required this.libraryItemId,
});
const _BookOnShelfPlayButton({required this.libraryItemId});
/// the id of the library item of the book
final String libraryItemId;
@ -217,8 +214,9 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
player.book?.libraryItemId == libraryItemId;
final isPlayingThisBook = player.playing && isCurrentBookSetInPlayer;
final userProgress = me.value?.mediaProgress
?.firstWhereOrNull((element) => element.libraryItemId == libraryItemId);
final userProgress = me.value?.mediaProgress?.firstWhereOrNull(
(element) => element.libraryItemId == libraryItemId,
);
final isBookCompleted = userProgress?.isFinished ?? false;
const size = 40.0;
@ -226,8 +224,10 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
// if there is user progress for this book show a circular progress indicator around the play button
var strokeWidth = size / 8;
final useMaterialThemeOnItemPage =
ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage;
final useMaterialThemeOnItemPage = ref
.watch(appSettingsProvider)
.themeSettings
.useMaterialThemeOnItemPage;
AsyncValue<ColorScheme?> coverColorScheme = const AsyncValue.loading();
if (useMaterialThemeOnItemPage && isCurrentBookSetInPlayer) {
@ -242,8 +242,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
return Theme(
// if current book is set in player, get theme from the cover image
data: ThemeData(
colorScheme:
coverColorScheme.value ?? Theme.of(context).colorScheme,
colorScheme: coverColorScheme.value ?? Theme.of(context).colorScheme,
),
child: Padding(
padding: EdgeInsets.all(strokeWidth / 2 + 2),
@ -258,10 +257,9 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
child: CircularProgressIndicator(
value: userProgress.progress,
strokeWidth: strokeWidth,
backgroundColor: Theme.of(context)
.colorScheme
.onPrimary
.withValues(alpha: 0.8),
backgroundColor: Theme.of(
context,
).colorScheme.onPrimary.withValues(alpha: 0.8),
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
@ -272,22 +270,18 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
IconButton(
color: Theme.of(context).colorScheme.primary,
style: ButtonStyle(
padding: WidgetStateProperty.all(
EdgeInsets.zero,
),
minimumSize: WidgetStateProperty.all(
const Size(size, size),
),
padding: WidgetStateProperty.all(EdgeInsets.zero),
minimumSize: WidgetStateProperty.all(const Size(size, size)),
backgroundColor: WidgetStateProperty.all(
Theme.of(context)
.colorScheme
.onPrimary
.withValues(alpha: 0.9),
Theme.of(
context,
).colorScheme.onPrimary.withValues(alpha: 0.9),
),
),
onPressed: () async {
final book =
await ref.watch(libraryItemProvider(libraryItemId).future);
final book = await ref.watch(
libraryItemProvider(libraryItemId).future,
);
libraryItemPlayButtonOnPressed(
ref: ref,
@ -313,9 +307,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
// a skeleton for the book cover
class BookCoverSkeleton extends StatelessWidget {
const BookCoverSkeleton({
super.key,
});
const BookCoverSkeleton({super.key});
@override
Widget build(BuildContext context) {
@ -324,13 +316,13 @@ class BookCoverSkeleton extends StatelessWidget {
child: SizedBox(
width: 150,
child: Shimmer.fromColors(
baseColor:
Theme.of(context).colorScheme.surface.withValues(alpha: 0.3),
highlightColor:
Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.1),
child: Container(
color: Theme.of(context).colorScheme.surface,
),
baseColor: Theme.of(
context,
).colorScheme.surface.withValues(alpha: 0.3),
highlightColor: Theme.of(
context,
).colorScheme.onSurface.withValues(alpha: 0.1),
child: Container(color: Theme.of(context).colorScheme.surface),
),
),
);

View file

@ -26,14 +26,14 @@ class HomeShelf extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
return switch (shelf.type) {
ShelfType.book => BookHomeShelf(
title: title,
shelf: shelf.asLibraryItemShelf,
showPlayButton: showPlayButton,
),
title: title,
shelf: shelf.asLibraryItemShelf,
showPlayButton: showPlayButton,
),
ShelfType.authors => AuthorHomeShelf(
title: title,
shelf: shelf.asAuthorShelf,
),
title: title,
shelf: shelf.asAuthorShelf,
),
_ => Container(),
};
}
@ -75,9 +75,7 @@ class SimpleHomeShelf extends HookConsumerWidget {
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
if (index == 0 || index == children.length + 1) {
return const SizedBox(
width: 8,
);
return const SizedBox(width: 8);
}
return children[index - 1];
},
@ -88,7 +86,8 @@ class SimpleHomeShelf extends HookConsumerWidget {
return const SizedBox(width: 4);
},
itemCount: children.length +
itemCount:
children.length +
2, // add some extra space at the start and end so that the first and last items are not at the edge
),
),