summaryrefslogtreecommitdiff
path: root/web/templates/frag-note.tmpl
blob: 3b04e1124aad674f203bbe55df921e7643b61311 (plain) (blame)
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
{{define "note"}}{{$n := .N}}{{$p := .P}}{{$away := and $n.Change $p.ChangeID (ne $n.Change $p.ChangeID)}}
<article class="note am{{$n.Agent.Bit}}{{if $away}} away{{end}}" id="note-{{$n.ID}}"
	         data-agent="{{$n.Agent.Name}}" data-model="{{$n.Model}}" data-session="{{$n.Session}}"
	         data-kind="{{$n.Kind}}" data-file="{{$n.File}}" data-date="{{if not $n.At.IsZero}}{{$n.At.Format "2006-01-02"}}{{end}}"
	         data-start="{{$n.Start}}" data-end="{{$n.End}}" data-reply-to="{{$n.ReplyTo}}">
  <header>
    {{if $n.File}}<a href="{{$p.NoteHref $n}}" title="{{$n.File}}">{{base $n.File}}{{lines $n}}</a>{{end}}
    {{if $n.Kind}}<em>{{$n.Kind}}</em>{{end}}
    <b class="who">{{$n.Agent.Short}}</b>
  </header>
	  <div class="body">{{md $n.Text $p}}</div>
  <footer class="meta">
    {{with $p.PermaHref $n}}<a class="perma" href="{{.}}"
      title="permalink — this note on its own page, with the code it is about">#{{$n.ID}}</a>
    {{else}}<span class="perma self">#{{$n.ID}}</span>{{end}}
    {{if not $n.At.IsZero}}<time datetime="{{$n.At.Format "2006-01-02T15:04:05Z07:00"}}" title="{{$n.Ago}}">{{$n.When}}</time>{{end}}
    {{if $n.Model}}<span class="model">{{$n.Model}}{{if $n.Reason}}/{{$n.Reason}}{{end}}</span>{{end}}
	    {{if $n.Session}}<code class="sess" title="{{$n.Session}}">{{short $n.Session}}</code>{{end}}
	    {{if $n.ReplyTo}}{{with $p.ReplyHref $n}}<a class="parent" href="{{.}}"
	      title="the note this explicitly replies to">reply to #{{$n.ReplyTo}}</a>{{else}}<span class="parent">reply to #{{$n.ReplyTo}}</span>{{end}}{{end}}
	    {{with $p.ThreadHref $n}}<a class="relations" href="{{.}}" title="notes replying to or mentioning this note">
	      {{if $n.Replies}}↳ {{len $n.Replies}} repl{{if eq (len $n.Replies) 1}}y{{else}}ies{{end}}{{end}}{{if and $n.Replies $n.Mentions}} · {{end}}{{if $n.Mentions}}↶ {{len $n.Mentions}} mention{{if ne (len $n.Mentions) 1}}s{{end}}{{end}}</a>{{end}}
	    {{with $p.ConvHref $n}}<a class="conv" href="{{.}}" title="open the conversation this note was written in">conversation</a>{{end}}
    {{if $n.Change}}
      {{if $p.Live}}<a class="atchg{{if $away}} away{{end}}" href="{{$p.Href "change" $n.Change}}"
        title="{{if $away}}written at another change{{else}}written at this change{{end}}"><code>{{short $n.Change}}</code></a>
      {{else}}<code class="atchg{{if $away}} away{{end}}">{{short $n.Change}}</code>{{end}}
    {{end}}
  </footer>
</article>
{{end}}

{{define "notelist"}}{{range .Notes}}{{template "note" (dict "N" . "P" $)}}{{end}}{{if not .Notes}}<p class="meta hint empty">no notes match these filters</p>{{end}}{{end}}

{{/* htmx responses: the list plus an out-of-band count update */}}
{{define "notelist-oob"}}<span id="notecount" hx-swap-oob="true">{{len .Notes}}</span>{{template "notelist" .}}{{end}}

{{define "note-added"}}{{if .File}}<span id="notecount" hx-swap-oob="true">{{len .File.Notes}}</span>{{end}}{{range .Notes}}{{template "note" (dict "N" . "P" $)}}{{end}}{{end}}

{{/* A child-note form appears below every full card in a focused thread. It
     deliberately uses a normal POST: one append and one redirect rebuild the
     entire DAG from the log, including shared mention paths. */}}
{{define "replyform"}}{{$n := .N}}{{$p := .P}}
<details class="replybox">
  <summary>reply to #{{$n.ID}}</summary>
  <form class="notform replyform" method="post" action="{{$p.Href "notes"}}">
    <input type="hidden" name="reply_to" value="{{$n.ID}}">
    <input type="hidden" name="rev" value="{{$p.Rev}}">
    <input type="hidden" name="change" value="{{$p.ChangeID}}">
    <input type="hidden" name="scope" value="all">
    <input type="hidden" name="back" value="{{$p.Href "note" (printf "%d" $p.Note.ID)}}#discussion">
    <textarea name="text" rows="2" placeholder="reply… (⌘/ctrl+enter to post)" aria-label="reply to note {{$n.ID}}" required></textarea>
    <div class="row end">
      <span class="meta">as <b>{{$p.User}}</b> at <code title="{{$p.ChangeID}}">{{short $p.ChangeID}}</code> → the log</span>
      <button type="submit">reply</button>
    </div>
  </form>
</details>
{{end}}

{{/* Recursive at arbitrary depth. A DAG join/cycle is a reference rather than
     a second card, which keeps ids unique and recursion finite. */}}
{{define "threadnode"}}{{$x := .Node}}{{$p := .P}}
<div class="threadnode via-{{$x.Via}}">
  <div class="threadedge" aria-label="{{$x.Via}}"><span>{{if eq $x.Via "reply"}}↳{{else}}↶{{end}}</span></div>
  <div class="threadcontent">
    {{if $x.Reference}}
      <p class="threadref meta">{{$x.Via}} also reaches <a href="{{$p.Href "note" (printf "%d" $x.Note.ID)}}">note #{{$x.Note.ID}}</a></p>
    {{else}}
      {{template "note" (dict "N" $x.Note "P" $p)}}
      {{if $p.Live}}{{template "replyform" (dict "N" $x.Note "P" $p)}}{{end}}
      {{if $x.Children}}<div class="threadchildren">{{range $x.Children}}{{template "threadnode" (dict "Node" . "P" $p)}}{{end}}</div>{{end}}
    {{end}}
  </div>
</div>
{{end}}

{{/* the add-a-comment form; live pages only, since it writes to the log */}}
{{define "noteform"}}
<form class="notform" hx-post="{{.Href "notes"}}" hx-target="#notelist" hx-swap="afterbegin"
      hx-on::after-request="if(event.detail.successful){this.reset();{{if eq .Kind "notes"}}htmx.trigger('#notefilters','change');{{end}}}"
      method="post" action="{{.Href "notes"}}">
  <input type="hidden" name="rev" value="{{.Rev}}">
  <input type="hidden" name="change" value="{{.ChangeID}}">
  <input type="hidden" name="scope" value="{{.Scope}}">
  <input type="hidden" name="back" value="{{if .File}}{{.Href "code" .File.Path}}{{else}}{{.Href "notes"}}{{end}}">
  <div class="row">
    {{if .File}}
      <input type="hidden" name="file" value="{{.File.Path}}">
      <span class="meta on">on <code>{{base .File.Path}}</code></span>
    {{else}}
      <input type="search" name="file" placeholder="file (optional)" list="filelist" autocomplete="off">
    {{end}}
    <input type="number" id="nstart" name="start" min="1" placeholder="from" aria-label="first line">
    <input type="number" id="nend" name="end" min="1" placeholder="to" aria-label="last line">
    <input type="text" name="kind" placeholder="kind" aria-label="kind" list="kindlist" size="6">
  </div>
  <textarea name="text" rows="3" placeholder="comment… (⌘/ctrl+enter to post)" required></textarea>
  <div class="row end">
    <span class="meta">as <b>{{.User}}</b> at <code title="{{.ChangeID}}">{{short .ChangeID}}</code> → the log</span>
    <button type="submit">add note</button>
  </div>
</form>
<datalist id="kindlist">{{range .Facets.Kinds}}<option value="{{.}}">{{end}}</datalist>
{{end}}