gitoria
All repositories: gitoria
5.8 KB
// components/main.hl — THE SHELL: the application header (brand; top right the login: ident's// "Log in with ident" button, or who you are + Log out), the display-name prompt of a first login,// and the slot every page renders into. All CSS is in styles.hl.// THE SELECTOR (gitoria#14): `<ident-selector key=IDENT_API_KEY>` from <ident>/selector.js, as in tickets.// Choosing an identity fires `ident-login` (one-time code); /login.js hands it to the hidden input// #identcode, whose `change` goes to the face gitoriaLogin (server-side exchange, no reload). The element's// class is `in` / `out`; /login.js mirrors it into the selector's `loggedIn`. ident only answers a REGISTERED// origin (the main address), so on a repo address (the request's host, `host = null`, hybriel#74) the selector// carries the class `onrepo` and styles.hl hides it — the button stays there.// LOGIN: a plain link to <ident>/login?key=&return=<public url>/login/callback (project.hl// loginCallback); /login.js adds `?next=<this page>` at the click so the login returns here.// FIRST LOGIN: no display name yet → the name prompt; creating a repo waits for it.import { siteName } from '../project.hl'import { identKey, selectorScript, loginHref, infoOfSession, exchangeCode, ensureUser, userInfo, setUserName, userOfSession, slugOfHost } from '../users.hl'import { randomBytes } from 'hl:crypto'slot = nullsession = nullhost = nullme = infoOfSession(session)loggedIn = me != nullloggedOut = me == nullneedsName = me != null && !me.namedmyName = me != null && me.named ? me.name : 'you'buttonHref = loginHrefonRepoHost = slugOfHost(host) != ''selectorClass = (me != null ? 'in' : 'out') + (onRepoHost ? ' onrepo' : '')selectorKey = identKeyidentScript = selectorScriptloginError = ''hasLoginError = falsenameDraft = ''nameError = ''// OFFLINE (mission 046, as tracker.worldapi.org#10): the service worker opens `/` from its cache without a network; the// shell then says so. hl:web gives a page no connection state and no "mounted" hook, so a tick asks the browser:// `netProbe` runs an endless 1 s CSS animation (styles.hl), and each `animationiteration` reads navigator.onLine — a// write only when it changed.offline = falseView {body {applicationHeader {a { class = "brand" href = "/" siteName }userBox { id = "userbox"identSelector { id = "selector" key = selectorKey class = selectorClass }if (loggedIn) {span { id = "whoami" myName }button { id = "logout" type = "button" class = "quiet" "Log out" on click(e) { emit doLogout(e) } }}if (loggedOut) {a { id = "loginbutton" class = "button" href = buttonHref "Log in with ident" }}input { id = "identcode" type = "hidden" on change(e) { emit gotCode(e) } }}}if (hasLoginError) { p { id = "loginerror" class = "message banner" loginError } }if (offline) { offlineNote { id = "offline" "You are offline. Repositories and code need the network." } }netProbe { "aria-hidden" = "true" on animationiteration(e) { emit netTick(e) } }if (needsName) {namePrompt {form { id = "nameform"on submit(e) {e.preventDefault()emit saveName(e)}label { "Welcome! Choose the display name others see as the owner of your repositories"input { id = "displayname" name = "displayname" required = "required" maxlength = "60" value = nameDraft on input(e) { emit setNameDraft(e.target.value) } }}button { id = "namesave" type = "submit" "Save" }p { id = "nameerror" class = "message" nameError }}}}main { slot }script { src = identScript }script { src = "/login.js" }}}showMe = (info) => {loggedIn = trueloggedOut = falseneedsName = !info.namedmyName = info.named ? info.name : 'you'selectorClass = onRepoHost ? 'in onrepo' : 'in'loginError = ''hasLoginError = false}showOut = () => {loggedIn = falseloggedOut = trueneedsName = falsemyName = 'you'selectorClass = onRepoHost ? 'out onrepo' : 'out'}// the selector's code (via /login.js and the hidden input) → the server exchanges iton gotCode(e) {let code = e.target.valuee.target.value = ''if (code == null || code == '') { return null }let r = emit server gitoriaLogin(code)if (r == null || r.error != null) {loginError = r != null ? r.error : 'the login failed'hasLoginError = truereturn null}showMe(r)}on client gitoriaSignedIn(tag, info) { showMe(info) }on client gitoriaSignedOut(tag) { showOut() }on setNameDraft(v) { nameDraft = v }on netTick(e) {let down = window.navigator.onLine == falseif (down != offline) { offline = down }}on saveName(e) {let r = emit server gitoriaSaveName(nameDraft)if (r == null || r.error != null) {nameError = r != null ? r.error : 'could not save the name'return null}nameError = ''showMe(r)}on doLogout(e) {emit server gitoriaLogOut()showOut()}// ---- the faces (the trailing `session` is always the server's: hybriel #16) ----on server gitoriaLogin(code, session) {if (session == null) { return { error = 'no session — reload the page' } }let x = exchangeCode(code)if (x.error != null) { return { error = x.error } }let u = ensureUser(x.identity)if (u == null) { return { error = 'could not store the user' } }let tag = randomBytes(16)session.user = { id = u.id }session.data.tag = taglet info = userInfo(u)emit client gitoriaSignedIn(tag, info)return info}on server gitoriaSaveName(name, session) {let u = userOfSession(session)if (u == null) { return { error = 'log in with ident first' } }let r = setUserName(u.id, name)if (r.error != null) { return { error = r.error } }let info = userInfo(r.user)emit client gitoriaSignedIn(session.data.tag, info)return info}on server gitoriaLogOut(session) {if (session == null) { return { error = 'no session' } }emit client gitoriaSignedOut(session.data.tag)session.user = nullreturn { ok = true }}
Branches
- mainmain branch
Latest commits
- 9b27cb26gitoria#21: installable app (manifest, service worker, offline start page), own iconmre
- 68dcb603deploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
- e2deed6dgitoria#20: "Add code" only on the Code page of an empty repository, no collapsiblemre
- 8bb97ffddeploy.sh: never send .git or .gitignore to Byrodinmre
- fd981932State of 2026-09-27; bin/ no longer tracked (Hybriel commit is in README)mre
- 4a2d7125initial commitmre