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

@ -53,8 +53,10 @@ class OauthFlows extends _$OauthFlows {
}
state = {
...state,
oauthState: state[oauthState]!
.copyWith(isFlowComplete: true, authToken: authToken),
oauthState: state[oauthState]!.copyWith(
isFlowComplete: true,
authToken: authToken,
),
};
}
}

View file

@ -27,9 +27,7 @@ class CallbackPage extends HookConsumerWidget {
// check if the state is in the flows
if (!flows.containsKey(state)) {
return const _SomethingWentWrong(
message: 'State not found',
);
return const _SomethingWentWrong(message: 'State not found');
}
// get the token
@ -45,26 +43,21 @@ class CallbackPage extends HookConsumerWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Contacting server...\nPlease wait\n\nGot:'
'\nState: $state\nCode: $code'),
Text(
'Contacting server...\nPlease wait\n\nGot:'
'\nState: $state\nCode: $code',
),
loginAuthToken.when(
data: (authenticationToken) {
if (authenticationToken == null) {
handleServerError(
context,
serverErrorResponse,
);
handleServerError(context, serverErrorResponse);
return const BackToLoginButton();
}
return Text('Token: $authenticationToken');
},
loading: () => const CircularProgressIndicator(),
error: (error, _) {
handleServerError(
context,
serverErrorResponse,
e: error,
);
handleServerError(context, serverErrorResponse, e: error);
return Column(
children: [
Text('Error with OAuth flow: $error'),
@ -81,9 +74,7 @@ class CallbackPage extends HookConsumerWidget {
}
class BackToLoginButton extends StatelessWidget {
const BackToLoginButton({
super.key,
});
const BackToLoginButton({super.key});
@override
Widget build(BuildContext context) {
@ -97,9 +88,7 @@ class BackToLoginButton extends StatelessWidget {
}
class _SomethingWentWrong extends StatelessWidget {
const _SomethingWentWrong({
this.message = 'Error with OAuth flow',
});
const _SomethingWentWrong({this.message = 'Error with OAuth flow'});
final String message;
@ -109,10 +98,7 @@ class _SomethingWentWrong extends StatelessWidget {
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(message),
const BackToLoginButton(),
],
children: [Text(message), const BackToLoginButton()],
),
),
);

View file

@ -9,9 +9,7 @@ import 'package:vaani/shared/utils.dart';
import 'package:vaani/shared/widgets/add_new_server.dart';
class OnboardingSinglePage extends HookConsumerWidget {
const OnboardingSinglePage({
super.key,
});
const OnboardingSinglePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -23,8 +21,9 @@ class OnboardingSinglePage extends HookConsumerWidget {
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 600,
minWidth:
constraints.maxWidth < 600 ? constraints.maxWidth : 0,
minWidth: constraints.maxWidth < 600
? constraints.maxWidth
: 0,
),
child: const Padding(
padding: EdgeInsets.symmetric(vertical: 20.0),
@ -39,10 +38,7 @@ class OnboardingSinglePage extends HookConsumerWidget {
}
}
Widget fadeSlideTransitionBuilder(
Widget child,
Animation<double> animation,
) {
Widget fadeSlideTransitionBuilder(Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: SlideTransition(
@ -56,9 +52,7 @@ Widget fadeSlideTransitionBuilder(
}
class OnboardingBody extends HookConsumerWidget {
const OnboardingBody({
super.key,
});
const OnboardingBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -81,9 +75,7 @@ class OnboardingBody extends HookConsumerWidget {
style: Theme.of(context).textTheme.headlineSmall,
),
),
const SizedBox.square(
dimension: 16.0,
),
const SizedBox.square(dimension: 16.0),
Padding(
padding: const EdgeInsets.all(8.0),
child: AnimatedSwitcher(
@ -112,21 +104,17 @@ class OnboardingBody extends HookConsumerWidget {
},
),
),
const SizedBox.square(
dimension: 16.0,
),
const SizedBox.square(dimension: 16.0),
AnimatedSwitcher(
duration: 500.ms,
transitionBuilder: fadeSlideTransitionBuilder,
child: canUserLogin.value
? UserLoginWidget(
server: audiobookshelfUri,
)
? UserLoginWidget(server: audiobookshelfUri)
// ).animate().fade(duration: 600.ms).slideY(begin: 0.3, end: 0)
: const RedirectToABS().animate().fadeIn().slideY(
curve: Curves.easeInOut,
duration: 500.ms,
),
curve: Curves.easeInOut,
duration: 500.ms,
),
),
],
);
@ -134,9 +122,7 @@ class OnboardingBody extends HookConsumerWidget {
}
class RedirectToABS extends StatelessWidget {
const RedirectToABS({
super.key,
});
const RedirectToABS({super.key});
@override
Widget build(BuildContext context) {
@ -152,18 +138,14 @@ class RedirectToABS extends StatelessWidget {
isSemanticButton: false,
style: ButtonStyle(
elevation: WidgetStateProperty.all(0),
padding: WidgetStateProperty.all(
const EdgeInsets.all(0),
),
padding: WidgetStateProperty.all(const EdgeInsets.all(0)),
),
onPressed: () async {
// open the github page
// ignore: avoid_print
print('Opening the github page');
await handleLaunchUrl(
Uri.parse(
'https://www.audiobookshelf.org',
),
Uri.parse('https://www.audiobookshelf.org'),
);
},
child: const Text('Click here'),

View file

@ -22,11 +22,7 @@ import 'package:vaani/settings/api_settings_provider.dart'
import 'package:vaani/settings/models/models.dart' as model;
class UserLoginWidget extends HookConsumerWidget {
const UserLoginWidget({
super.key,
required this.server,
this.onSuccess,
});
const UserLoginWidget({super.key, required this.server, this.onSuccess});
final Uri server;
final Function(model.AuthenticatedUser)? onSuccess;
@ -34,8 +30,9 @@ class UserLoginWidget extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final serverStatusError = useMemoized(() => ErrorResponseHandler(), []);
final serverStatus =
ref.watch(serverStatusProvider(server, serverStatusError.storeError));
final serverStatus = ref.watch(
serverStatusProvider(server, serverStatusError.storeError),
);
return serverStatus.when(
data: (value) {
@ -55,9 +52,7 @@ class UserLoginWidget extends HookConsumerWidget {
);
},
loading: () {
return const Center(
child: CircularProgressIndicator(),
);
return const Center(child: CircularProgressIndicator());
},
error: (error, _) {
return Center(
@ -68,10 +63,7 @@ class UserLoginWidget extends HookConsumerWidget {
ElevatedButton(
onPressed: () {
ref.invalidate(
serverStatusProvider(
server,
serverStatusError.storeError,
),
serverStatusProvider(server, serverStatusError.storeError),
);
},
child: const Text('Try again'),
@ -84,11 +76,7 @@ class UserLoginWidget extends HookConsumerWidget {
}
}
enum AuthMethodChoice {
local,
openid,
authToken,
}
enum AuthMethodChoice { local, openid, authToken }
class UserLoginMultipleAuth extends HookConsumerWidget {
const UserLoginMultipleAuth({
@ -117,21 +105,17 @@ class UserLoginMultipleAuth extends HookConsumerWidget {
);
model.AudiobookShelfServer addServer() {
var newServer = model.AudiobookShelfServer(
serverUrl: server,
);
var newServer = model.AudiobookShelfServer(serverUrl: server);
try {
// add the server to the list of servers
ref.read(audiobookShelfServerProvider.notifier).addServer(
newServer,
);
ref.read(audiobookShelfServerProvider.notifier).addServer(newServer);
} on ServerAlreadyExistsException catch (e) {
newServer = e.server;
} finally {
ref.read(apiSettingsProvider.notifier).updateState(
ref.read(apiSettingsProvider).copyWith(
activeServer: newServer,
),
ref
.read(apiSettingsProvider.notifier)
.updateState(
ref.read(apiSettingsProvider).copyWith(activeServer: newServer),
);
}
return newServer;
@ -150,42 +134,49 @@ class UserLoginMultipleAuth extends HookConsumerWidget {
runAlignment: WrapAlignment.center,
runSpacing: 10,
alignment: WrapAlignment.center,
children: [
// a small label to show the user what to do
if (localAvailable)
ChoiceChip(
label: const Text('Local'),
selected: methodChoice.value == AuthMethodChoice.local,
onSelected: (selected) {
if (selected) {
methodChoice.value = AuthMethodChoice.local;
}
},
),
if (openIDAvailable)
ChoiceChip(
label: const Text('OpenID'),
selected: methodChoice.value == AuthMethodChoice.openid,
onSelected: (selected) {
if (selected) {
methodChoice.value = AuthMethodChoice.openid;
}
},
),
ChoiceChip(
label: const Text('Token'),
selected:
methodChoice.value == AuthMethodChoice.authToken,
onSelected: (selected) {
if (selected) {
methodChoice.value = AuthMethodChoice.authToken;
}
},
),
].animate(interval: 100.ms).fadeIn(
duration: 150.ms,
curve: Curves.easeIn,
),
children:
[
// a small label to show the user what to do
if (localAvailable)
ChoiceChip(
label: const Text('Local'),
selected:
methodChoice.value ==
AuthMethodChoice.local,
onSelected: (selected) {
if (selected) {
methodChoice.value = AuthMethodChoice.local;
}
},
),
if (openIDAvailable)
ChoiceChip(
label: const Text('OpenID'),
selected:
methodChoice.value ==
AuthMethodChoice.openid,
onSelected: (selected) {
if (selected) {
methodChoice.value =
AuthMethodChoice.openid;
}
},
),
ChoiceChip(
label: const Text('Token'),
selected:
methodChoice.value ==
AuthMethodChoice.authToken,
onSelected: (selected) {
if (selected) {
methodChoice.value =
AuthMethodChoice.authToken;
}
},
),
]
.animate(interval: 100.ms)
.fadeIn(duration: 150.ms, curve: Curves.easeIn),
),
),
Padding(
@ -195,21 +186,21 @@ class UserLoginMultipleAuth extends HookConsumerWidget {
transitionBuilder: fadeSlideTransitionBuilder,
child: switch (methodChoice.value) {
AuthMethodChoice.authToken => UserLoginWithToken(
server: server,
addServer: addServer,
onSuccess: onSuccess,
),
server: server,
addServer: addServer,
onSuccess: onSuccess,
),
AuthMethodChoice.local => UserLoginWithPassword(
server: server,
addServer: addServer,
onSuccess: onSuccess,
),
server: server,
addServer: addServer,
onSuccess: onSuccess,
),
AuthMethodChoice.openid => UserLoginWithOpenID(
server: server,
addServer: addServer,
openIDButtonText: openIDButtonText,
onSuccess: onSuccess,
),
server: server,
addServer: addServer,
openIDButtonText: openIDButtonText,
onSuccess: onSuccess,
),
},
),
),

View file

@ -54,9 +54,9 @@ class UserLoginWithOpenID extends HookConsumerWidget {
if (openIDLoginEndpoint == null) {
if (responseErrorHandler.response.statusCode == 400 &&
responseErrorHandler.response.body
.toLowerCase()
.contains(RegExp(r'invalid.*redirect.*uri'))) {
responseErrorHandler.response.body.toLowerCase().contains(
RegExp(r'invalid.*redirect.*uri'),
)) {
// show error
handleServerError(
context,
@ -97,16 +97,16 @@ class UserLoginWithOpenID extends HookConsumerWidget {
);
// add the flow to the provider
ref.read(oauthFlowsProvider.notifier).addFlow(
ref
.read(oauthFlowsProvider.notifier)
.addFlow(
oauthState,
verifier: verifier,
serverUri: server,
cookie: Cookie.fromSetCookieValue(authCookie!),
);
await handleLaunchUrl(
openIDLoginEndpoint,
);
await handleLaunchUrl(openIDLoginEndpoint);
}
return Column(

View file

@ -39,17 +39,14 @@ class UserLoginWithPassword extends HookConsumerWidget {
final api = ref.watch(audiobookshelfApiProvider(server));
// forward animation when the password visibility changes
useEffect(
() {
if (isPasswordVisible.value) {
isPasswordVisibleAnimationController.forward();
} else {
isPasswordVisibleAnimationController.reverse();
}
return null;
},
[isPasswordVisible.value],
);
useEffect(() {
if (isPasswordVisible.value) {
isPasswordVisibleAnimationController.forward();
} else {
isPasswordVisibleAnimationController.reverse();
}
return null;
}, [isPasswordVisible.value]);
/// Login to the server and save the user
Future<void> loginAndSave() async {
@ -109,10 +106,9 @@ class UserLoginWithPassword extends HookConsumerWidget {
decoration: InputDecoration(
labelText: 'Username',
labelStyle: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.8),
color: Theme.of(
context,
).colorScheme.onSurface.withValues(alpha: 0.8),
),
border: const OutlineInputBorder(),
),
@ -129,18 +125,16 @@ class UserLoginWithPassword extends HookConsumerWidget {
decoration: InputDecoration(
labelText: 'Password',
labelStyle: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.8),
color: Theme.of(
context,
).colorScheme.onSurface.withValues(alpha: 0.8),
),
border: const OutlineInputBorder(),
suffixIcon: ColorFiltered(
colorFilter: ColorFilter.mode(
Theme.of(context)
.colorScheme
.primary
.withValues(alpha: 0.8),
Theme.of(
context,
).colorScheme.primary.withValues(alpha: 0.8),
BlendMode.srcIn,
),
child: InkWell(
@ -157,9 +151,7 @@ class UserLoginWithPassword extends HookConsumerWidget {
),
),
),
suffixIconConstraints: const BoxConstraints(
maxHeight: 45,
),
suffixIconConstraints: const BoxConstraints(maxHeight: 45),
),
),
const SizedBox(height: 30),
@ -197,10 +189,12 @@ Future<void> handleServerError(
context: context,
builder: (context) => AlertDialog(
title: const Text('Error'),
content: SelectableText('$title\n'
'Got response: ${responseErrorHandler.response.body} (${responseErrorHandler.response.statusCode})\n'
'Stacktrace: $e\n\n'
'$body\n\n'),
content: SelectableText(
'$title\n'
'Got response: ${responseErrorHandler.response.body} (${responseErrorHandler.response.statusCode})\n'
'Stacktrace: $e\n\n'
'$body\n\n',
),
actions: [
if (outLink != null)
TextButton(
@ -214,8 +208,8 @@ Future<void> handleServerError(
// open an issue on the github page
handleLaunchUrl(
AppMetadata.githubRepo
// append the issue url
.replace(
// append the issue url
.replace(
path: '${AppMetadata.githubRepo.path}/issues/new',
),
);

View file

@ -89,10 +89,9 @@ class UserLoginWithToken extends HookConsumerWidget {
decoration: InputDecoration(
labelText: 'API Token',
labelStyle: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.8),
color: Theme.of(
context,
).colorScheme.onSurface.withValues(alpha: 0.8),
),
border: const OutlineInputBorder(),
),
@ -107,10 +106,7 @@ class UserLoginWithToken extends HookConsumerWidget {
},
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: loginAndSave,
child: const Text('Login'),
),
ElevatedButton(onPressed: loginAndSave, child: const Text('Login')),
],
),
);