gitoriaLog in with ident

tracker

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit44b7d9f944b7d9f9tracker#6: /schedule — upcoming episodes of followed shows, soonest firstmre44b7d9f9/components/main.hl

3.5 KB

  1. // components/main.hl — THE SHELL: the application header (brand; top right the login: ident's "Log in with
  2. // ident" button or Log out) and the slot every page renders into. All CSS is in styles.hl. Copied from
  3. // calendar.worldapi.org's components/main.hl unchanged in shape (tracker.worldapi.org#1, rejected once for
  4. // centering the sign-in on the page instead of using the header selector).
  5. // THE SELECTOR: `<ident-selector key=IDENT_API_KEY>` from <ident>/selector.js, as in calendar/gitoria. Choosing an
  6. // identity fires `ident-login` (one-time code); /login.js hands it to the hidden input #identcode, whose `change`
  7. // goes to the face trackerLogin (server-side exchange, no reload). The element's class is `in` / `out`; /login.js
  8. // mirrors it into the selector's `loggedIn`.
  9. // LOGIN: a plain link to <ident>/login?key=&return=<public url>/login/callback (project.hl loginCallback).
  10. // After a login or logout in this tab the page is fetched again (navigate('/')).
  11. import { navigate } from 'hl:web'
  12. import { appTitle } from '../project.hl'
  13. import { identKey, selectorScript, loginHref, userIdOfSession, exchangeCode, ensureUser } from '../users.hl'
  14. import { randomBytes } from 'hl:crypto'
  15. slot = null
  16. session = null
  17. loggedIn = userIdOfSession(session) != null
  18. loggedOut = userIdOfSession(session) == null
  19. buttonHref = loginHref
  20. selectorClass = loggedIn ? 'in' : 'out'
  21. selectorKey = identKey
  22. identScript = selectorScript
  23. loginError = ''
  24. hasLoginError = false
  25. View {
  26. body {
  27. applicationHeader {
  28. a { class = "brand" href = "/" appTitle }
  29. userBox { id = "userbox"
  30. identSelector { id = "selector" key = selectorKey class = selectorClass }
  31. if (loggedIn) {
  32. button { id = "logout" type = "button" class = "quiet" "Log out" on click(e) { emit doLogout(e) } }
  33. }
  34. if (loggedOut) {
  35. a { id = "loginbutton" class = "button" href = buttonHref "Log in with ident" }
  36. }
  37. input { id = "identcode" type = "hidden" on change(e) { emit gotCode(e) } }
  38. }
  39. }
  40. if (hasLoginError) { p { id = "loginerror" class = "message banner" loginError } }
  41. main { slot }
  42. script { src = identScript }
  43. script { src = "/login.js" }
  44. }
  45. }
  46. showIn = () => {
  47. loggedIn = true
  48. loggedOut = false
  49. selectorClass = 'in'
  50. loginError = ''
  51. hasLoginError = false
  52. }
  53. showOut = () => {
  54. loggedIn = false
  55. loggedOut = true
  56. selectorClass = 'out'
  57. }
  58. // the selector's code (via /login.js and the hidden input) → the server exchanges it
  59. on gotCode(e) {
  60. let code = e.target.value
  61. e.target.value = ''
  62. if (code == null || code == '') { return null }
  63. let r = emit server trackerLogin(code)
  64. if (r == null || r.error != null) {
  65. loginError = r != null ? r.error : 'the login failed'
  66. hasLoginError = true
  67. return null
  68. }
  69. showIn()
  70. navigate('/')
  71. }
  72. on client trackerSignedIn(tag) {
  73. showIn()
  74. navigate('/')
  75. }
  76. on client trackerSignedOut(tag) {
  77. showOut()
  78. navigate('/')
  79. }
  80. on doLogout(e) {
  81. emit server trackerLogOut()
  82. showOut()
  83. navigate('/')
  84. }
  85. // ---- the faces (the trailing `session` is always the server's: hybriel #16) ----
  86. on server trackerLogin(code, session) {
  87. if (session == null) { return { error = 'no session — reload the page' } }
  88. let x = exchangeCode(code)
  89. if (x.error != null) { return { error = x.error } }
  90. let u = ensureUser(x.identity)
  91. if (u == null) { return { error = 'could not store the user' } }
  92. let tag = randomBytes(16)
  93. session.user = { id = u.id }
  94. session.data.tag = tag
  95. emit client trackerSignedIn(tag)
  96. return { ok = true }
  97. }
  98. on server trackerLogOut(session) {
  99. if (session == null) { return { error = 'no session' } }
  100. emit client trackerSignedOut(session.data.tag)
  101. session.user = null
  102. return { ok = true }
  103. }

Branches

Latest commits

  • 44b7d9f9tracker#6: /schedule — upcoming episodes of followed shows, soonest firstmre
  • 91c9fc8ctracker#5: /unwatched — unwatched released episodes of followed shows, newest firstmre
  • a97c0295tracker#4: show page /shows/:slug (header, seasons, episodes, watch checks) + tools/relink-episode-seasons.hlmre
  • 05f407c5tracker: no border on any button except inverted ones (Log out, ident status and identities too); header brand weight 100mre
  • 17375427tracker#2: tools/migrate.hl + tools/verify.hl — old MongoDB data into mpackdb with new idsmre
  • 31aac936tracker#3: no border on the header and on filled buttons; inverted buttons keep theirsmre
  • 2ad9d29ctracker#1: login exactly like calendar (identity selector in the header, empty homepage)mre
  • 3691e176tracker#1: empty tracker with the ident login (state of 2026-09-27)mre