@ -6,11 +6,14 @@ import 'package:hive/hive.dart';
import ' package:hooks_riverpod/hooks_riverpod.dart ' ;
import ' package:immich_mobile/constants/hive_box.dart ' ;
import ' package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart ' ;
import ' package:immich_mobile/modules/login/providers/oauth.provider.dart ' ;
import ' package:immich_mobile/routing/router.dart ' ;
import ' package:immich_mobile/shared/providers/api.provider.dart ' ;
import ' package:immich_mobile/shared/providers/asset.provider.dart ' ;
import ' package:immich_mobile/modules/login/providers/authentication.provider.dart ' ;
import ' package:immich_mobile/modules/backup/providers/backup.provider.dart ' ;
import ' package:immich_mobile/shared/ui/immich_toast.dart ' ;
import ' package:openapi/api.dart ' ;
class LoginForm extends HookConsumerWidget {
const LoginForm ( { Key ? key } ) : super ( key: key ) ;
@ -23,10 +26,47 @@ class LoginForm extends HookConsumerWidget {
useTextEditingController . fromValue ( TextEditingValue . empty ) ;
final serverEndpointController =
useTextEditingController ( text: ' login_form_endpoint_hint ' . tr ( ) ) ;
final apiService = ref . watch ( apiServiceProvider ) ;
final serverEndpointFocusNode = useFocusNode ( ) ;
final isSaveLoginInfo = useState < bool > ( false ) ;
final isLoading = useState < bool > ( false ) ;
final isOauthEnable = useState < bool > ( false ) ;
final oAuthButtonLabel = useState < String > ( ' OAuth ' ) ;
getServeLoginConfig ( ) async {
if ( ! serverEndpointFocusNode . hasFocus ) {
var urlText = serverEndpointController . text . trim ( ) ;
try {
var endpointUrl = Uri . tryParse ( urlText ) ;
if ( endpointUrl ! = null ) {
isLoading . value = true ;
apiService . setEndpoint ( endpointUrl . toString ( ) ) ;
var loginConfig = await apiService . oAuthApi . generateConfig (
OAuthConfigDto ( redirectUri: endpointUrl . toString ( ) ) ,
) ;
if ( loginConfig ! = null ) {
isOauthEnable . value = loginConfig . enabled ;
oAuthButtonLabel . value = loginConfig . buttonText ? ? ' OAuth ' ;
} else {
isOauthEnable . value = false ;
}
isLoading . value = false ;
}
} catch ( _ ) {
isLoading . value = false ;
isOauthEnable . value = false ;
}
}
}
useEffect (
( ) {
serverEndpointFocusNode . addListener ( getServeLoginConfig ) ;
var loginInfo = Hive . box < HiveSavedLoginInfo > ( hiveLoginInfoBox )
. get ( savedLoginInfoKey ) ;
@ -37,6 +77,7 @@ class LoginForm extends HookConsumerWidget {
isSaveLoginInfo . value = loginInfo . isSaveLogin ;
}
getServeLoginConfig ( ) ;
return null ;
} ,
[ ] ,
@ -67,7 +108,10 @@ class LoginForm extends HookConsumerWidget {
) ,
EmailInput ( controller: usernameController ) ,
PasswordInput ( controller: passwordController ) ,
ServerEndpointInput ( controller: serverEndpointController ) ,
ServerEndpointInput (
controller: serverEndpointController ,
focusNode: serverEndpointFocusNode ,
) ,
CheckboxListTile (
activeColor: Theme . of ( context ) . primaryColor ,
contentPadding: const EdgeInsets . symmetric ( horizontal: 8 ) ,
@ -92,12 +136,52 @@ class LoginForm extends HookConsumerWidget {
}
} ,
) ,
LoginButton (
emailController: usernameController ,
passwordController: passwordController ,
serverEndpointController: serverEndpointController ,
isSavedLoginInfo: isSaveLoginInfo . value ,
) ,
if ( isLoading . value )
const SizedBox (
width: 24 ,
height: 24 ,
child: CircularProgressIndicator (
strokeWidth: 2 ,
) ,
) ,
if ( ! isLoading . value )
Column (
crossAxisAlignment: CrossAxisAlignment . stretch ,
mainAxisAlignment: MainAxisAlignment . center ,
children: [
LoginButton (
emailController: usernameController ,
passwordController: passwordController ,
serverEndpointController: serverEndpointController ,
isSavedLoginInfo: isSaveLoginInfo . value ,
) ,
if ( isOauthEnable . value ) . . . [
Padding (
padding: const EdgeInsets . symmetric (
horizontal: 16.0 ,
) ,
child: Divider (
color: Brightness . dark = = Theme . of ( context ) . brightness
? Colors . white
: Colors . black ,
) ,
) ,
OAuthLoginButton (
serverEndpointController: serverEndpointController ,
isSavedLoginInfo: isSaveLoginInfo . value ,
buttonLabel: oAuthButtonLabel . value ,
isLoading: isLoading ,
onLoginSuccess: ( ) {
isLoading . value = false ;
ref . watch ( backupProvider . notifier ) . resumeBackup ( ) ;
AutoRouter . of ( context ) . replace (
const TabControllerRoute ( ) ,
) ;
} ,
) ,
] ,
] ,
)
] ,
) ,
) ,
@ -108,9 +192,12 @@ class LoginForm extends HookConsumerWidget {
class ServerEndpointInput extends StatelessWidget {
final TextEditingController controller ;
const ServerEndpointInput ( { Key ? key , required this . controller } )
: super ( key: key ) ;
final FocusNode focusNode ;
const ServerEndpointInput ( {
Key ? key ,
required this . controller ,
required this . focusNode ,
} ) : super ( key: key ) ;
String ? _validateInput ( String ? url ) {
if ( url ? . startsWith ( RegExp ( r'https?://' ) ) = = true ) {
@ -131,6 +218,7 @@ class ServerEndpointInput extends StatelessWidget {
) ,
validator: _validateInput ,
autovalidateMode: AutovalidateMode . always ,
focusNode: focusNode ,
) ;
}
}
@ -200,13 +288,9 @@ class LoginButton extends ConsumerWidget {
@ override
Widget build ( BuildContext context , WidgetRef ref ) {
return ElevatedButton (
return ElevatedButton .icon (
style: ElevatedButton . styleFrom (
visualDensity: VisualDensity . standard ,
backgroundColor: Theme . of ( context ) . primaryColor ,
foregroundColor: Colors . grey [ 50 ] ,
elevation: 2 ,
padding: const EdgeInsets . symmetric ( vertical: 10 , horizontal: 25 ) ,
padding: const EdgeInsets . symmetric ( vertical: 12 ) ,
) ,
onPressed: ( ) async {
/ / This will remove current cache asset state of previous user login .
@ -238,10 +322,101 @@ class LoginButton extends ConsumerWidget {
) ;
}
} ,
child: const Text (
icon: const Icon ( Icons . login_rounded ) ,
label: const Text (
" login_form_button_text " ,
style: TextStyle ( fontSize: 14 , fontWeight: FontWeight . bold ) ,
) . tr ( ) ,
) ;
}
}
class OAuthLoginButton extends ConsumerWidget {
final TextEditingController serverEndpointController ;
final bool isSavedLoginInfo ;
final ValueNotifier < bool > isLoading ;
final VoidCallback onLoginSuccess ;
final String buttonLabel ;
const OAuthLoginButton ( {
Key ? key ,
required this . serverEndpointController ,
required this . isSavedLoginInfo ,
required this . isLoading ,
required this . onLoginSuccess ,
required this . buttonLabel ,
} ) : super ( key: key ) ;
@ override
Widget build ( BuildContext context , WidgetRef ref ) {
var oAuthService = ref . watch ( OAuthServiceProvider ) ;
void performOAuthLogin ( ) async {
ref . watch ( assetProvider . notifier ) . clearAllAsset ( ) ;
OAuthConfigResponseDto ? oAuthServerConfig ;
try {
oAuthServerConfig = await oAuthService
. getOAuthServerConfig ( serverEndpointController . text ) ;
isLoading . value = true ;
} catch ( e ) {
ImmichToast . show (
context: context ,
msg: " login_form_failed_get_oauth_server_config " . tr ( ) ,
toastType: ToastType . error ,
) ;
isLoading . value = false ;
return ;
}
if ( oAuthServerConfig ! = null & & oAuthServerConfig . enabled ) {
var loginResponseDto =
await oAuthService . oAuthLogin ( oAuthServerConfig . url ! ) ;
if ( loginResponseDto ! = null ) {
var isSuccess = await ref
. watch ( authenticationProvider . notifier )
. setSuccessLoginInfo (
accessToken: loginResponseDto . accessToken ,
isSavedLoginInfo: isSavedLoginInfo ,
) ;
if ( isSuccess ) {
isLoading . value = false ;
onLoginSuccess ( ) ;
} else {
ImmichToast . show (
context: context ,
msg: " login_form_failed_login " . tr ( ) ,
toastType: ToastType . error ,
) ;
}
}
isLoading . value = false ;
} else {
ImmichToast . show (
context: context ,
msg: " login_form_failed_get_oauth_server_disable " . tr ( ) ,
toastType: ToastType . info ,
) ;
isLoading . value = false ;
return ;
}
}
return ElevatedButton . icon (
style: ElevatedButton . styleFrom (
backgroundColor: Theme . of ( context ) . primaryColor . withAlpha ( 230 ) ,
padding: const EdgeInsets . symmetric ( vertical: 12 ) ,
) ,
onPressed: performOAuthLogin ,
icon: const Icon ( Icons . pin_rounded ) ,
label: Text (
buttonLabel ,
style: const TextStyle ( fontSize: 14 , fontWeight: FontWeight . bold ) ,
) ,
) ;
}
}