All files / core/blong-browser/src/components/LoginPopup LoginPopup.tsx

36.66% Statements 11/30
7.14% Branches 1/14
33.33% Functions 3/9
29.62% Lines 8/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107                                          16x 16x 4x 4x 4x 4x 4x   4x                                                                                                                                                          
/**
 * LoginPopup — modal re-authentication prompt.
 *
 * Shown when an operation fails with an expired/invalid session (401) and the
 * client-side token renewal could not refresh it (e.g. the session was closed
 * server-side).  The user logs in here and then re-invokes the failed
 * operation — there is deliberately NO automatic retry of the original call.
 *
 * Wired through the store's `loginPrompt` flag, which `wrapHandlerProxy`
 * raises on auth-classified errors.
 */
import './LoginPopup.css';
 
import {Dialog, InputText, Message, Password} from '../../primereact/index.js';
 
import {useState} from 'react';
import {useBlong} from '../../context/BlongContext.js';
import {useAppStore} from '../../state/appStore.js';
import {Button} from '../Button/Button.js';
 
export function LoginPopup() {
    const prompt = useAppStore(s => s.loginPrompt);
    const setLoginPrompt = useAppStore(s => s.setLoginPrompt);
    const {handler} = useBlong();
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');
    const [error, setError] = useState<string | undefined>(undefined);
    const [loading, setLoading] = useState(false);
 
    Eif (!prompt) return null;
 
    const close = () => {
        setLoginPrompt(false);
        setError(undefined);
        setPassword('');
    };
 
    const onSubmit = async () => {
        setLoading(true);
        setError(undefined);
        try {
            const result = (await handler.authLogin(
                {username, password},
                {},
            )) as {step?: string; error?: string};
            if (result.step === 'success') {
                close();
            } else {
                setError(result.error ?? 'Login failed');
            }
        } catch (err) {
            setError((err as {message?: string}).message ?? 'Login failed');
        } finally {
            setLoading(false);
        }
    };
 
    return (
        <Dialog
            visible
            header="Session expired — please log in"
            onHide={close}
            closable
            modal
            className="blong-login-popup"
            style={{width: '380px'}}
        >
            <div className="blong-login-popup__body">
                {error && (
                    <Message
                        severity="error"
                        text={error}
                        className="blong-login-popup__error"
                    />
                )}
                <div className="blong-login-popup__field">
                    <label htmlFor="blong-login-popup-username">Username</label>
                    <InputText
                        id="blong-login-popup-username"
                        name="username"
                        value={username}
                        onChange={e => setUsername(e.target.value)}
                        onKeyDown={e => e.key === 'Enter' && onSubmit()}
                        autoFocus
                    />
                </div>
                <div className="blong-login-popup__field">
                    <label htmlFor="blong-login-popup-password">Password</label>
                    <Password
                        id="blong-login-popup-password"
                        name="password"
                        value={password}
                        onChange={e => setPassword(e.target.value)}
                        onKeyDown={e => e.key === 'Enter' && onSubmit()}
                        feedback={false}
                        toggleMask
                    />
                </div>
            </div>
            <div className="blong-login-popup__footer">
                <Button label="Cancel" icon="pi pi-times" onClick={close} outlined />
                <Button label="Login" icon="pi pi-sign-in" onClick={onSubmit} loading={loading} />
            </div>
        </Dialog>
    );
}