/** Sign in and create account, on one switchable card. */
import { h } from '../dom.js';
import { api } from '../api.js';
import { setUser, refreshSubscriptions } from '../store.js';
import { navigate, currentQuery } from '../router.js';
import { toast } from '../ui.js';
export function authView(mode) {
return async () => {
const next = currentQuery().get('next') || '/';
const isSignup = mode === 'signup';
document.title = isSignup ? 'Create account · MeTube' : 'Sign in · MeTube';
const error = h('div.formerror', { hidden: true, role: 'alert' });
const submit = h('button.btn.btn--primary.btn--full', { type: 'submit' },
isSignup ? 'Create account' : 'Sign in');
const fields = isSignup
? {
username: h('input.input', { name: 'username', autocomplete: 'username', required: true,
placeholder: 'yourhandle', maxlength: '24', pattern: '[a-zA-Z0-9_.]{3,24}' }),
displayName: h('input.input', { name: 'displayName', autocomplete: 'name',
placeholder: 'How your name appears', maxlength: '40' }),
email: h('input.input', { name: 'email', type: 'email', autocomplete: 'email', required: true,
placeholder: '[email protected]' }),
password: h('input.input', { name: 'password', type: 'password', autocomplete: 'new-password',
required: true, minlength: '8', placeholder: 'At least 8 characters' }),
}
: {
identifier: h('input.input', { name: 'identifier', autocomplete: 'username', required: true,
placeholder: 'Username or email' }),
password: h('input.input', { name: 'password', type: 'password', autocomplete: 'current-password',
required: true, placeholder: 'Your password' }),
};
const form = h('form.panel', {
novalidate: true,
onsubmit: async (event) => {
event.preventDefault();
error.hidden = true;
submit.disabled = true;
submit.textContent = isSignup ? 'Creating account…' : 'Signing in…';
try {
const payload = Object.fromEntries(
Object.entries(fields).map(([key, input]) => [key, input.value.trim()]),
);
const { user } = isSignup ? await api.signup(payload) : await api.login(payload);
setUser(user);
await refreshSubscriptions();
toast(isSignup ? `Welcome to MeTube, ${user.displayName}` : `Signed in as ${user.displayName}`, 'good');
navigate(next.startsWith('/') ? next : '/', { replace: true });
} catch (err) {
error.textContent = err.message;
error.hidden = false;
submit.disabled = false;
submit.textContent = isSignup ? 'Create account' : 'Sign in';
}
},
},
error,
...(isSignup
? [
field('Username', fields.username, 'Letters, numbers, dot and underscore. This becomes your @handle.'),
field('Display name', fields.displayName, 'Optional — defaults to your username.'),
field('Email', fields.email),
field('Password', fields.password, 'At least 8 characters.'),
]
: [
field('Username or email', fields.identifier),
field('Password', fields.password),
]),
submit,
);
return h('div.auth', {},
h('div.auth__card', {},
h('div.auth__head', {},
h('h1.auth__title', { text: isSignup ? 'Create your channel' : 'Welcome back' }),
h('div.auth__sub', {
text: isSignup
? 'One account to upload, comment and subscribe.'
: 'Sign in to pick up where you left off.',
}),
),
form,
h('div.auth__switch', {},
isSignup ? 'Already have an account? ' : 'New to MeTube? ',
h('button', {
type: 'button',
onclick: () => navigate(`/${isSignup ? 'signin' : 'signup'}?next=${encodeURIComponent(next)}`),
}, isSignup ? 'Sign in' : 'Create an account'),
),
isSignup ? null : h('div.auth__demo', {},
h('b', {}, 'Trying the demo?'), ' Sign in as ',
h('code', {}, 'pixelforge'), ', ', h('code', {}, 'quietkitchen'), ' or ', h('code', {}, 'marta'),
' with the password ', h('code', {}, 'password123'), '.',
),
),
);
};
}
const field = (label, input, hint) =>
h('div.field', {},
h('label.field__label', { text: label }),
input,
hint ? h('div.field__hint', { text: hint }) : null,
);