+11
-2
appview/db/strings.go
+11
-2
appview/db/strings.go
···
131
131
return err
132
132
}
133
133
134
-
func GetStrings(e Execer, filters ...filter) ([]String, error) {
134
+
func GetStrings(e Execer, limit int, filters ...filter) ([]String, error) {
135
135
var all []String
136
136
137
137
var conditions []string
···
144
144
whereClause := ""
145
145
if conditions != nil {
146
146
whereClause = " where " + strings.Join(conditions, " and ")
147
+
}
148
+
149
+
limitClause := ""
150
+
if limit != 0 {
151
+
limitClause = fmt.Sprintf(" limit %d ", limit)
147
152
}
148
153
149
154
query := fmt.Sprintf(`select
···
154
159
content,
155
160
created,
156
161
edited
157
-
from strings %s`,
162
+
from strings
163
+
%s
164
+
order by created desc
165
+
%s`,
158
166
whereClause,
167
+
limitClause,
159
168
)
160
169
161
170
rows, err := e.Query(query, args...)
+9
appview/pages/pages.go
+9
appview/pages/pages.go
···
1160
1160
return p.execute("strings/dashboard", w, params)
1161
1161
}
1162
1162
1163
+
type StringTimelineParams struct {
1164
+
LoggedInUser *oauth.User
1165
+
Strings []db.String
1166
+
}
1167
+
1168
+
func (p *Pages) StringsTimeline(w io.Writer, params StringTimelineParams) error {
1169
+
return p.execute("strings/timeline", w, params)
1170
+
}
1171
+
1163
1172
type SingleStringParams struct {
1164
1173
LoggedInUser *oauth.User
1165
1174
ShowRendered bool
+2
-1
appview/pages/templates/strings/fragments/form.html
+2
-1
appview/pages/templates/strings/fragments/form.html
···
31
31
name="content"
32
32
id="content-textarea"
33
33
wrap="off"
34
-
class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"
34
+
class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400 font-mono"
35
35
rows="20"
36
+
spellcheck="false"
36
37
placeholder="Paste your string here!"
37
38
required>{{ .String.Contents }}</textarea>
38
39
<div class="flex justify-between items-center">
+1
-1
appview/pages/templates/strings/string.html
+1
-1
appview/pages/templates/strings/string.html
+65
appview/pages/templates/strings/timeline.html
+65
appview/pages/templates/strings/timeline.html
···
1
+
{{ define "title" }} all strings {{ end }}
2
+
3
+
{{ define "topbar" }}
4
+
{{ template "layouts/topbar" $ }}
5
+
{{ end }}
6
+
7
+
{{ define "content" }}
8
+
{{ block "timeline" $ }}{{ end }}
9
+
{{ end }}
10
+
11
+
{{ define "timeline" }}
12
+
<div>
13
+
<div class="p-6">
14
+
<p class="text-xl font-bold dark:text-white">All strings</p>
15
+
</div>
16
+
17
+
<div class="flex flex-col gap-4">
18
+
{{ range $i, $s := .Strings }}
19
+
<div class="relative">
20
+
{{ if ne $i 0 }}
21
+
<div class="absolute left-8 -top-4 w-px h-4 bg-gray-300 dark:bg-gray-600"></div>
22
+
{{ end }}
23
+
<div class="flex flex-col divide-y divide-gray-200 dark:divide-gray-700 border border-gray-200 dark:border-gray-700 rounded-sm">
24
+
{{ template "stringCard" $s }}
25
+
</div>
26
+
</div>
27
+
{{ end }}
28
+
</div>
29
+
</div>
30
+
{{ end }}
31
+
32
+
{{ define "stringCard" }}
33
+
<div class="py-4 px-6 drop-shadow-sm rounded bg-white dark:bg-gray-800">
34
+
<div class="font-medium dark:text-white flex gap-2 items-center">
35
+
<a href="/strings/{{ resolve .Did.String }}/{{ .Rkey }}">{{ .Filename }}</a>
36
+
</div>
37
+
{{ with .Description }}
38
+
<div class="text-gray-600 dark:text-gray-300 text-sm">
39
+
{{ . }}
40
+
</div>
41
+
{{ end }}
42
+
43
+
{{ template "stringCardInfo" . }}
44
+
</div>
45
+
{{ end }}
46
+
47
+
{{ define "stringCardInfo" }}
48
+
{{ $stat := .Stats }}
49
+
{{ $resolved := resolve .Did.String }}
50
+
<div class="text-gray-400 pt-4 text-sm font-mono inline-flex items-center gap-2 mt-auto">
51
+
<a href="/strings/{{ $resolved }}" class="flex items-center">
52
+
{{ template "user/fragments/picHandle" $resolved }}
53
+
</a>
54
+
<span class="select-none [&:before]:content-['·']"></span>
55
+
<span>{{ $stat.LineCount }} line{{if ne $stat.LineCount 1}}s{{end}}</span>
56
+
<span class="select-none [&:before]:content-['·']"></span>
57
+
{{ with .Edited }}
58
+
<span>edited {{ template "repo/fragments/shortTimeAgo" . }}</span>
59
+
{{ else }}
60
+
{{ template "repo/fragments/shortTimeAgo" .Created }}
61
+
{{ end }}
62
+
</div>
63
+
{{ end }}
64
+
65
+
+22
appview/strings/strings.go
+22
appview/strings/strings.go
···
44
44
r := chi.NewRouter()
45
45
46
46
r.
47
+
Get("/", s.timeline)
48
+
49
+
r.
47
50
With(mw.ResolveIdent()).
48
51
Route("/{user}", func(r chi.Router) {
49
52
r.Get("/", s.dashboard)
···
70
73
return r
71
74
}
72
75
76
+
func (s *Strings) timeline(w http.ResponseWriter, r *http.Request) {
77
+
l := s.Logger.With("handler", "timeline")
78
+
79
+
strings, err := db.GetStrings(s.Db, 50)
80
+
if err != nil {
81
+
l.Error("failed to fetch string", "err", err)
82
+
w.WriteHeader(http.StatusInternalServerError)
83
+
return
84
+
}
85
+
86
+
s.Pages.StringsTimeline(w, pages.StringTimelineParams{
87
+
LoggedInUser: s.OAuth.GetUser(r),
88
+
Strings: strings,
89
+
})
90
+
}
91
+
73
92
func (s *Strings) contents(w http.ResponseWriter, r *http.Request) {
74
93
l := s.Logger.With("handler", "contents")
75
94
···
91
110
92
111
strings, err := db.GetStrings(
93
112
s.Db,
113
+
0,
94
114
db.FilterEq("did", id.DID),
95
115
db.FilterEq("rkey", rkey),
96
116
)
···
154
174
155
175
all, err := db.GetStrings(
156
176
s.Db,
177
+
0,
157
178
db.FilterEq("did", id.DID),
158
179
)
159
180
if err != nil {
···
225
246
// get the string currently being edited
226
247
all, err := db.GetStrings(
227
248
s.Db,
249
+
0,
228
250
db.FilterEq("did", id.DID),
229
251
db.FilterEq("rkey", rkey),
230
252
)