Vaani/lib/features/onboarding/view/user_login.dart

204 lines
6.6 KiB
Dart
Raw Normal View History

2024-05-08 05:03:49 -04:00
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
2024-09-06 15:10:00 -04:00
import 'package:shelfsdk/audiobookshelf_api.dart';
import 'package:vaani/api/api_provider.dart';
import 'package:vaani/api/server_provider.dart';
import 'package:vaani/features/onboarding/view/user_login_with_open_id.dart';
import 'package:vaani/features/onboarding/view/user_login_with_password.dart';
import 'package:vaani/features/onboarding/view/user_login_with_token.dart';
2024-08-23 04:21:46 -04:00
import 'package:vaani/hacks/fix_autofill_losing_focus.dart';
2024-09-06 15:10:00 -04:00
import 'package:vaani/models/error_response.dart';
import 'package:vaani/settings/api_settings_provider.dart';
import 'package:vaani/settings/models/models.dart' as model;
2024-05-08 05:03:49 -04:00
2024-09-06 15:10:00 -04:00
class UserLoginWidget extends HookConsumerWidget {
UserLoginWidget({
2024-05-08 05:03:49 -04:00
super.key,
2024-09-06 15:10:00 -04:00
required this.server,
});
final Uri server;
final serverStatusError = ErrorResponseHandler();
2024-09-06 15:10:00 -04:00
@override
Widget build(BuildContext context, WidgetRef ref) {
final serverStatus =
ref.watch(serverStatusProvider(server, serverStatusError.storeError));
final api = ref.watch(audiobookshelfApiProvider(server));
return serverStatus.when(
data: (value) {
if (value == null) {
// check the error message
return Text(serverStatusError.response.body);
}
// check available authentication methods and return the correct widget
return UserLoginMultipleAuth(
server: server,
localAvailable:
value.authMethods?.contains(AuthMethod.local) ?? false,
openIDAvailable:
2024-09-06 15:10:00 -04:00
value.authMethods?.contains(AuthMethod.openid) ?? false,
openIDButtonText: value.authFormData?.authOpenIDButtonText,
2024-09-06 15:10:00 -04:00
);
},
loading: () {
return const Center(
child: CircularProgressIndicator(),
);
},
error: (error, _) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Server is not reachable: $error'),
ElevatedButton(
onPressed: () {
ref.invalidate(
serverStatusProvider(
server,
serverStatusError.storeError,
),
);
},
child: const Text('Try again'),
),
],
),
);
},
);
}
}
enum AuthMethodChoice {
local,
openid,
authToken,
}
class UserLoginMultipleAuth extends HookConsumerWidget {
const UserLoginMultipleAuth({
super.key,
required this.server,
this.localAvailable = false,
this.openIDAvailable = false,
2024-05-08 05:03:49 -04:00
this.onPressed,
this.openIDButtonText,
2024-05-08 05:03:49 -04:00
});
2024-09-06 15:10:00 -04:00
final Uri server;
final bool localAvailable;
final bool openIDAvailable;
2024-05-08 05:03:49 -04:00
final void Function()? onPressed;
final String? openIDButtonText;
2024-05-08 05:03:49 -04:00
@override
Widget build(BuildContext context, WidgetRef ref) {
2024-09-06 15:10:00 -04:00
// will show choice chips for the available authentication methods
// authToken method is always available
final methodChoice = useState<AuthMethodChoice>(
// ! TODO revert to local when openID debugging is done
localAvailable ? AuthMethodChoice.openid : AuthMethodChoice.authToken,
2024-09-06 15:10:00 -04:00
);
final apiSettings = ref.watch(apiSettingsProvider);
model.AudiobookShelfServer addServer() {
var newServer = model.AudiobookShelfServer(
serverUrl: server,
);
try {
// add the server to the list of servers
ref.read(audiobookShelfServerProvider.notifier).addServer(
newServer,
);
} on ServerAlreadyExistsException catch (e) {
newServer = e.server;
} finally {
ref.read(apiSettingsProvider.notifier).updateState(
apiSettings.copyWith(
activeServer: newServer,
),
);
}
return newServer;
}
return Center(
child: InactiveFocusScopeObserver(
child: AutofillGroup(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Wrap(
// mainAxisAlignment: MainAxisAlignment.center,
spacing: 10,
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)
2024-09-06 15:10:00 -04:00
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;
}
},
),
],
),
const SizedBox.square(
dimension: 8,
),
switch (methodChoice.value) {
AuthMethodChoice.authToken => UserLoginWithToken(
2024-09-06 15:10:00 -04:00
server: server,
addServer: addServer,
),
AuthMethodChoice.local => UserLoginWithPassword(
2024-09-06 15:10:00 -04:00
server: server,
addServer: addServer,
),
AuthMethodChoice.openid => UserLoginWithOpenID(
2024-09-06 15:10:00 -04:00
server: server,
addServer: addServer,
openIDButtonText: openIDButtonText,
2024-09-06 15:10:00 -04:00
),
},
],
),
),
),
),
);
}
}