gitoriaLog in with ident

gitoria

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commitfdfb4b1bfdfb4b1bgitoria: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gates 200/0, 46/0, 44/0mrefdfb4b1b/components/tickets.hl

5.1 KB

  1. // components/tickets.hl — THE TICKETS of a repo (gitoria#10, gitoria#16): `/tickets` on <slug>.<domain>, its own
  2. // route, rendered on the SERVER with its list: the repo comes from the request's host, the tickets from
  3. // tickets.worldapi.org (tickets.hl — hl:fetch answers at once, so the list is in the first HTML and in every
  4. // hl:web navigation). The repo must be connected to a tickets project first (gitoria#18, /settings). A named user
  5. // opens a ticket AS THEMSELVES (the repo's key + their ident id, tickets.hl); a ticket opened here reaches every open
  6. // tickets page of the repo (push `ticketOpened`).
  7. parent './main.hl'
  8. import { siteName } from '../project.hl'
  9. import RepoHead from './repohead.hl'
  10. import { repoAt, pageTitle } from '../repos.hl'
  11. import { loadTickets, openTicket } from '../tickets.hl'
  12. import { slugOfHost, infoOfSession, userOfSession } from '../users.hl'
  13. host = null
  14. session = null
  15. slug = slugOfHost(host)
  16. repo = repoAt(slug)
  17. hasRepo = repo != null
  18. me = infoOfSession(session)
  19. canCreate = repo != null && repo.ticketsOn && me != null && me.named
  20. needsLogin = repo != null && repo.ticketsOn && me == null
  21. notConnected = repo != null && repo.ticketsOff
  22. loaded = repo != null && repo.ticketsOn ? loadTickets(slug) : null
  23. ticketsErrorShown = loaded != null && loaded.error != null
  24. ticketsError = loaded != null && loaded.error != null ? loaded.error : ''
  25. tickets = loaded != null && loaded.tickets != null ? loaded.tickets : []
  26. ticketsTruncated = loaded != null && loaded.truncated == true
  27. ticketsHref = loaded != null && loaded.url != null ? loaded.url : ''
  28. ticketsShown = tickets.length > 0
  29. ticketsNone = loaded != null && loaded.error == null && tickets.length == 0
  30. ticketError = ''
  31. ticketNotice = ''
  32. hasTicketNotice = false
  33. __title = pageTitle(siteName, slug, 'Tickets')
  34. View {
  35. repoPage {
  36. RepoHead { }
  37. if (hasRepo) {
  38. repoTickets { id = "tickets"
  39. if (notConnected) { p { id = "ticketsnotconnected" class = "empty" "This repository is not connected to a tickets project. The owner connects it under " a { id = "tosettings" href = "/settings" "Settings" } "." } }
  40. if (ticketsErrorShown) { p { id = "ticketserror" class = "message" ticketsError } }
  41. if (canCreate) {
  42. details { id = "newticket"
  43. summary { "New ticket" }
  44. form { id = "newticketform"
  45. on submit(e) {
  46. e.preventDefault()
  47. emit openIt(e)
  48. }
  49. label { "Subject"
  50. input { id = "ticketsubject" name = "subject" required = "required" maxlength = "200" autocomplete = "off" }
  51. }
  52. label { "Text (Markdown, optional)"
  53. textarea { id = "ticketsummary" name = "summary" rows = "5" }
  54. }
  55. button { id = "ticketsave" type = "submit" "Open ticket" }
  56. p { id = "newticketerror" class = "message" ticketError }
  57. }
  58. }
  59. }
  60. if (needsLogin) { p { id = "ticketsloginhint" class = "muted" "Log in with ident (top right) to open a ticket." } }
  61. if (hasTicketNotice) { p { id = "ticketnotice" class = "notice" ticketNotice } }
  62. if (ticketsNone) { p { id = "noticketsyet" class = "empty" "No ticket yet in this project." } }
  63. if (ticketsShown) {
  64. ol { id = "ticketlist"
  65. for (t of tickets) {
  66. li {
  67. a { class = "number" href = t.url "#" t.number }
  68. a { class = "subject" href = t.url t.subject }
  69. span { class = "state" t.state }
  70. time { t.updated }
  71. }
  72. }
  73. }
  74. p { class = "muted" "Open a ticket to read and answer it in " a { id = "ticketsall" href = ticketsHref "tickets.worldapi.org" } "." }
  75. if (ticketsTruncated) { p { id = "ticketstruncated" class = "muted" "Only the latest tickets are listed here." } }
  76. }
  77. }
  78. }
  79. }
  80. }
  81. on openIt(e) {
  82. let f = e.target.elements
  83. let r = emit server gitoriaOpenTicket(slug, f.subject.value, f.summary.value)
  84. if (r == null || r.error != null) {
  85. ticketError = r != null ? r.error : 'could not open the ticket'
  86. return null
  87. }
  88. ticketError = ''
  89. f.subject.value = ''
  90. f.summary.value = ''
  91. ticketNotice = 'Opened ticket #' + r.ticket.number + '.'
  92. hasTicketNotice = true
  93. addTicket(r.ticket)
  94. }
  95. addTicket = (row) => {
  96. let next = [row]
  97. for (x of tickets) { if (x.number != row.number) { next.push(x) } }
  98. tickets = next
  99. ticketsShown = true
  100. ticketsNone = false
  101. }
  102. // a ticket opened here by anyone: every open tickets page of that repo follows
  103. on client ticketOpened(slugOf, row) {
  104. if (!hasRepo || slugOf != slug) { return null }
  105. addTicket(row)
  106. }
  107. // this session's tabs follow a login / name / logout (the shell's faces push them)
  108. on client gitoriaSignedIn(tag, info) {
  109. canCreate = repo != null && repo.ticketsOn && info.named
  110. needsLogin = false
  111. }
  112. on client gitoriaSignedOut(tag) {
  113. canCreate = false
  114. needsLogin = repo != null && repo.ticketsOn
  115. }
  116. on server gitoriaOpenTicket(slugOf, subject, summary, session) {
  117. if (repoAt(slugOf) == null) { return { error = 'no such repository' } }
  118. let u = userOfSession(session)
  119. if (u == null) { return { error = 'log in with ident (top right) to open a ticket' } }
  120. if (u.name == '') { return { error = 'choose a display name first (top of the page)' } }
  121. let r = openTicket(slugOf, u.identity, subject, summary)
  122. if (r.error != null) { return { error = r.error } }
  123. emit client ticketOpened(slugOf, r.ticket)
  124. return r
  125. }

Branches

Latest commits

  • fdfb4b1bgitoria: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gates 200/0, 46/0, 44/0mre
  • 5b46ac84antcolony#40: LOG.md — missions 069/072 are antcolony missions (report paths on Byrodin)mre
  • 5602ff41gitoria: Hybriel master 190aa11d (fc838894 GC correctness, #127 mountKids by reference, #126, #48) — tracker README flat; gates 200/0, 46/0, 44/0mre
  • e85eaf01gitoria: 069 round 2 — hybriel 1a096ad3 not adopted (Markdown SSR still grows); browser gate waits for the server-side logout before restartmre
  • 09ce4f3fgitoria: mission 069 re-vendor hybriel 8efba065 stopped (big SSR pages grow + slow down); lambda audit clean; old vendor keptmre
  • 3dc43108antcolony#40: mission references point to the moved missionsmre
  • 8d9450fdantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
  • 205d5fe4gitoria: Hybriel master ff51cf46; ssh keys/tokens no double rows (session sync); gates follow #20mre
  • 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