👽 Update third party login

This commit is contained in:
2025-11-30 21:38:36 +08:00
parent a7960da362
commit 27c7c8f039
3 changed files with 62 additions and 29 deletions

View File

@@ -120,7 +120,7 @@ class _CreateAccountEmailScreen extends HookConsumerWidget {
return null; return null;
}, [isBusy]); }, [isBusy]);
void performNext() { Future<void> performNext() async {
final email = emailController.text.trim(); final email = emailController.text.trim();
if (email.isEmpty) { if (email.isEmpty) {
showErrorAlert('fieldCannotBeEmpty'.tr()); showErrorAlert('fieldCannotBeEmpty'.tr());
@@ -130,7 +130,18 @@ class _CreateAccountEmailScreen extends HookConsumerWidget {
showErrorAlert('fieldEmailAddressMustBeValid'.tr()); showErrorAlert('fieldEmailAddressMustBeValid'.tr());
return; return;
} }
onNext();
// Validate email availability with API
isBusy.value = true;
try {
final client = ref.watch(apiClientProvider);
await client.post('/pass/accounts/validate', data: {'email': email});
onNext();
} catch (err) {
showErrorAlert(err);
} finally {
isBusy.value = false;
}
} }
return Column( return Column(
@@ -343,14 +354,25 @@ class _CreateAccountProfileScreen extends HookConsumerWidget {
return null; return null;
}, [isBusy]); }, [isBusy]);
void performNext() { Future<void> performNext() async {
final username = usernameController.text.trim(); final username = usernameController.text.trim();
final nickname = nicknameController.text.trim(); final nickname = nicknameController.text.trim();
if (username.isEmpty || nickname.isEmpty) { if (username.isEmpty || nickname.isEmpty) {
showErrorAlert('fieldCannotBeEmpty'.tr()); showErrorAlert('fieldCannotBeEmpty'.tr());
return; return;
} }
onNext();
// Validate username availability with API
isBusy.value = true;
try {
final client = ref.watch(apiClientProvider);
await client.post('/pass/accounts/validate', data: {'name': username});
onNext();
} catch (err) {
showErrorAlert(err);
} finally {
isBusy.value = false;
}
} }
return Column( return Column(

View File

@@ -38,6 +38,20 @@ final Map<int, (String, String, IconData)> kFactorTypes = {
4: ('authFactorPin', 'authFactorPinDescription', Symbols.nest_secure_alarm), 4: ('authFactorPin', 'authFactorPinDescription', Symbols.nest_secure_alarm),
}; };
/// Performs post-login tasks including fetching user info, subscribing to push
/// notifications, connecting websocket, and closing the login dialog.
Future<void> performPostLogin(BuildContext context, WidgetRef ref) async {
final userNotifier = ref.read(userInfoProvider.notifier);
await userNotifier.fetchUser();
final apiClient = ref.read(apiClientProvider);
subscribePushNotification(apiClient);
final wsNotifier = ref.read(websocketStateProvider.notifier);
wsNotifier.connect();
if (context.mounted && Navigator.canPop(context)) {
Navigator.pop(context, true);
}
}
class _LoginCheckScreen extends HookConsumerWidget { class _LoginCheckScreen extends HookConsumerWidget {
final SnAuthChallenge? challenge; final SnAuthChallenge? challenge;
final SnAuthFactor? factor; final SnAuthFactor? factor;
@@ -80,14 +94,7 @@ class _LoginCheckScreen extends HookConsumerWidget {
if (!context.mounted) return; if (!context.mounted) return;
// Do post login tasks // Do post login tasks
final userNotifier = ref.read(userInfoProvider.notifier); await performPostLogin(context, ref);
userNotifier.fetchUser().then((_) {
final apiClient = ref.read(apiClientProvider);
subscribePushNotification(apiClient);
final wsNotifier = ref.read(websocketStateProvider.notifier);
wsNotifier.connect();
if (context.mounted) Navigator.pop(context, true);
});
} }
useEffect(() { useEffect(() {
@@ -628,17 +635,13 @@ class _LoginLookupScreen extends HookConsumerWidget {
}, },
); );
final challenge = SnAuthChallenge.fromJson(resp.data); final token = resp.data['token'];
onChallenge(challenge); setToken(ref.watch(sharedPreferencesProvider), token);
final factorResp = await client.get( ref.invalidate(tokenProvider);
'/pass/auth/challenge/${challenge.id}/factors', if (!context.mounted) return;
);
onFactor( // Do post login tasks
List<SnAuthFactor>.from( await performPostLogin(context, ref);
factorResp.data.map((ele) => SnAuthFactor.fromJson(ele)),
),
);
onNext();
} catch (err) { } catch (err) {
if (err is SignInWithAppleAuthorizationException) return; if (err is SignInWithAppleAuthorizationException) return;
showErrorAlert(err); showErrorAlert(err);

View File

@@ -5,10 +5,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:protocol_handler/protocol_handler.dart'; import 'package:protocol_handler/protocol_handler.dart';
import 'package:island/pods/activity/activity_rpc.dart'; import 'package:island/pods/activity/activity_rpc.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/pods/websocket.dart';
import 'package:island/route.dart'; import 'package:island/route.dart';
import 'package:island/screens/auth/login_content.dart';
import 'package:island/screens/tray_manager.dart'; import 'package:island/screens/tray_manager.dart';
import 'package:island/services/event_bus.dart';
import 'package:island/pods/web_auth/web_auth_providers.dart'; import 'package:island/pods/web_auth/web_auth_providers.dart';
import 'package:island/services/notify.dart'; import 'package:island/services/notify.dart';
import 'package:island/services/sharing_intent.dart'; import 'package:island/services/sharing_intent.dart';
@@ -117,14 +119,20 @@ class _AppWrapperState extends ConsumerState<AppWrapper>
TrayService.instance.handleAction(menuItem); TrayService.instance.handleAction(menuItem);
} }
void _handleDeepLink(Uri uri, WidgetRef ref) { void _handleDeepLink(Uri uri, WidgetRef ref) async {
String path = '/${uri.host}${uri.path}'; String path = '/${uri.host}${uri.path}';
// Special handling for OIDC auth callback // Special handling for OIDC auth callback
if (path == '/auth/callback' && if (path == '/auth/callback' && uri.queryParameters.containsKey('token')) {
uri.queryParameters.containsKey('challenge')) { final token = uri.queryParameters['token']!;
final challenge = uri.queryParameters['challenge']!; setToken(ref.read(sharedPreferencesProvider), token);
eventBus.fire(OidcAuthCallbackEvent(challenge)); ref.invalidate(tokenProvider);
// Do post login tasks
if (mounted) {
await performPostLogin(context, ref);
}
if (!kIsWeb && if (!kIsWeb &&
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) { (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
windowManager.show(); windowManager.show();