tangled
alpha
login
or
join now
back
round
0
view raw
appview/labels: add "subscribe all" button for default labels
#597
merged
opened by
oppi.li
3 months ago
targeting
master
from
push-lxxtrqtnnoxy
quickly subscribe to all default labels.
Signed-off-by: oppiliappan
me@oppi.li
options
unified
split
Changed files
+187
-37
appview
ingester.go
labels
labels.go
pages
pages.go
templates
repo
settings
general.html
repo
repo.go
state
state.go
+80
appview/ingester.go
···
5
"encoding/json"
6
"fmt"
7
"log/slog"
0
0
8
9
"time"
10
···
80
err = i.ingestIssueComment(e)
81
case tangled.LabelDefinitionNSID:
82
err = i.ingestLabelDefinition(e)
0
0
83
}
84
l = i.Logger.With("nsid", e.Commit.Collection)
85
}
···
953
954
return nil
955
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
5
"encoding/json"
6
"fmt"
7
"log/slog"
8
+
"maps"
9
+
"slices"
10
11
"time"
12
···
82
err = i.ingestIssueComment(e)
83
case tangled.LabelDefinitionNSID:
84
err = i.ingestLabelDefinition(e)
85
+
case tangled.LabelOpNSID:
86
+
err = i.ingestLabelOp(e)
87
}
88
l = i.Logger.With("nsid", e.Commit.Collection)
89
}
···
957
958
return nil
959
}
960
+
961
+
func (i *Ingester) ingestLabelOp(e *jmodels.Event) error {
962
+
did := e.Did
963
+
rkey := e.Commit.RKey
964
+
965
+
var err error
966
+
967
+
l := i.Logger.With("handler", "ingestLabelOp", "nsid", e.Commit.Collection, "did", did, "rkey", rkey)
968
+
l.Info("ingesting record")
969
+
970
+
ddb, ok := i.Db.Execer.(*db.DB)
971
+
if !ok {
972
+
return fmt.Errorf("failed to index label op, invalid db cast")
973
+
}
974
+
975
+
switch e.Commit.Operation {
976
+
case jmodels.CommitOperationCreate:
977
+
raw := json.RawMessage(e.Commit.Record)
978
+
record := tangled.LabelOp{}
979
+
err = json.Unmarshal(raw, &record)
980
+
if err != nil {
981
+
return fmt.Errorf("invalid record: %w", err)
982
+
}
983
+
984
+
subject := syntax.ATURI(record.Subject)
985
+
collection := subject.Collection()
986
+
987
+
var repo *models.Repo
988
+
switch collection {
989
+
case tangled.RepoIssueNSID:
990
+
i, err := db.GetIssues(ddb, db.FilterEq("at_uri", subject))
991
+
if err != nil || len(i) != 1 {
992
+
return fmt.Errorf("failed to find subject: %w || subject count %d", err, len(i))
993
+
}
994
+
repo = i[0].Repo
995
+
default:
996
+
return fmt.Errorf("unsupport label subject: %s", collection)
997
+
}
998
+
999
+
actx, err := db.NewLabelApplicationCtx(ddb, db.FilterIn("at_uri", repo.Labels))
1000
+
if err != nil {
1001
+
return fmt.Errorf("failed to build label application ctx: %w", err)
1002
+
}
1003
+
1004
+
ops := models.LabelOpsFromRecord(did, rkey, record)
1005
+
1006
+
for _, o := range ops {
1007
+
def, ok := actx.Defs[o.OperandKey]
1008
+
if !ok {
1009
+
return fmt.Errorf("failed to find label def for key: %s, expected: %q", o.OperandKey, slices.Collect(maps.Keys(actx.Defs)))
1010
+
}
1011
+
if err := i.Validator.ValidateLabelOp(def, &o); err != nil {
1012
+
return fmt.Errorf("failed to validate labelop: %w", err)
1013
+
}
1014
+
}
1015
+
1016
+
tx, err := ddb.Begin()
1017
+
if err != nil {
1018
+
return err
1019
+
}
1020
+
defer tx.Rollback()
1021
+
1022
+
for _, o := range ops {
1023
+
_, err = db.AddLabelOp(tx, &o)
1024
+
if err != nil {
1025
+
return fmt.Errorf("failed to add labelop: %w", err)
1026
+
}
1027
+
}
1028
+
1029
+
if err = tx.Commit(); err != nil {
1030
+
return err
1031
+
}
1032
+
}
1033
+
1034
+
return nil
1035
+
}
-3
appview/labels/labels.go
···
104
return
105
}
106
107
-
l.logger.Info("actx", "labels", labelAts)
108
-
l.logger.Info("actx", "defs", actx.Defs)
109
-
110
// calculate the start state by applying already known labels
111
existingOps, err := db.GetLabelOps(l.db, db.FilterEq("subject", subjectUri))
112
if err != nil {
···
104
return
105
}
106
0
0
0
107
// calculate the start state by applying already known labels
108
existingOps, err := db.GetLabelOps(l.db, db.FilterEq("subject", subjectUri))
109
if err != nil {
+10
-9
appview/pages/pages.go
···
834
}
835
836
type RepoGeneralSettingsParams struct {
837
-
LoggedInUser *oauth.User
838
-
RepoInfo repoinfo.RepoInfo
839
-
Labels []models.LabelDefinition
840
-
DefaultLabels []models.LabelDefinition
841
-
SubscribedLabels map[string]struct{}
842
-
Active string
843
-
Tabs []map[string]any
844
-
Tab string
845
-
Branches []types.Branch
0
846
}
847
848
func (p *Pages) RepoGeneralSettings(w io.Writer, params RepoGeneralSettingsParams) error {
···
834
}
835
836
type RepoGeneralSettingsParams struct {
837
+
LoggedInUser *oauth.User
838
+
RepoInfo repoinfo.RepoInfo
839
+
Labels []models.LabelDefinition
840
+
DefaultLabels []models.LabelDefinition
841
+
SubscribedLabels map[string]struct{}
842
+
ShouldSubscribeAll bool
843
+
Active string
844
+
Tabs []map[string]any
845
+
Tab string
846
+
Branches []types.Branch
847
}
848
849
func (p *Pages) RepoGeneralSettings(w io.Writer, params RepoGeneralSettingsParams) error {
+36
-6
appview/pages/templates/repo/settings/general.html
···
46
47
{{ define "defaultLabelSettings" }}
48
<div class="flex flex-col gap-2">
49
-
<h2 class="text-sm pb-2 uppercase font-bold">Default Labels</h2>
50
-
<p class="text-gray-500 dark:text-gray-400">
51
-
Manage your issues and pulls by creating labels to categorize them. Only
52
-
repository owners may configure labels. You may choose to subscribe to
53
-
default labels, or create entirely custom labels.
54
-
</p>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
55
<div class="flex flex-col rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 w-full">
56
{{ range .DefaultLabels }}
57
<div id="label-{{.Id}}" class="flex items-center justify-between p-2 pl-4">
···
46
47
{{ define "defaultLabelSettings" }}
48
<div class="flex flex-col gap-2">
49
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 items-center">
50
+
<div class="col-span-1 md:col-span-2">
51
+
<h2 class="text-sm pb-2 uppercase font-bold">Default Labels</h2>
52
+
<p class="text-gray-500 dark:text-gray-400">
53
+
Manage your issues and pulls by creating labels to categorize them. Only
54
+
repository owners may configure labels. You may choose to subscribe to
55
+
default labels, or create entirely custom labels.
56
+
<p>
57
+
</div>
58
+
<form class="col-span-1 md:col-span-1 md:justify-self-end">
59
+
{{ $title := "Unubscribe from all labels" }}
60
+
{{ $icon := "x" }}
61
+
{{ $text := "unsubscribe all" }}
62
+
{{ $action := "unsubscribe" }}
63
+
{{ if $.ShouldSubscribeAll }}
64
+
{{ $title = "Subscribe to all labels" }}
65
+
{{ $icon = "check-check" }}
66
+
{{ $text = "subscribe all" }}
67
+
{{ $action = "subscribe" }}
68
+
{{ end }}
69
+
{{ range .DefaultLabels }}
70
+
<input type="hidden" name="label" value="{{ .AtUri.String }}">
71
+
{{ end }}
72
+
<button
73
+
type="submit"
74
+
title="{{$title}}"
75
+
class="btn flex items-center gap-2 group"
76
+
hx-swap="none"
77
+
hx-post="/{{ $.RepoInfo.FullName }}/settings/label/{{$action}}"
78
+
{{ if not .RepoInfo.Roles.IsOwner }}disabled{{ end }}>
79
+
{{ i $icon "size-4" }}
80
+
{{ $text }}
81
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
82
+
</button>
83
+
</form>
84
+
</div>
85
<div class="flex flex-col rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 w-full">
86
{{ range .DefaultLabels }}
87
<div id="label-{{.Id}}" class="flex items-center justify-between p-2 pl-4">
+60
-19
appview/repo/repo.go
···
1248
return
1249
}
1250
0
0
0
0
0
1251
errorId := "default-label-operation"
1252
fail := func(msg string, err error) {
1253
l.Error(msg, "err", err)
1254
rp.pages.Notice(w, errorId, msg)
1255
}
1256
1257
-
labelAt := r.FormValue("label")
1258
-
_, err = db.GetLabelDefinition(rp.db, db.FilterEq("at_uri", labelAt))
1259
if err != nil {
1260
fail("Failed to subscribe to label.", err)
1261
return
1262
}
1263
1264
newRepo := f.Repo
1265
-
newRepo.Labels = append(newRepo.Labels, labelAt)
0
0
0
0
0
1266
repoRecord := newRepo.AsRecord()
1267
1268
client, err := rp.oauth.AuthorizedClient(r)
···
1286
},
1287
})
1288
1289
-
err = db.SubscribeLabel(rp.db, &models.RepoLabel{
1290
-
RepoAt: f.RepoAt(),
1291
-
LabelAt: syntax.ATURI(labelAt),
1292
-
})
1293
if err != nil {
1294
fail("Failed to subscribe to label.", err)
1295
return
1296
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1297
1298
// everything succeeded
1299
rp.pages.HxRefresh(w)
···
1311
return
1312
}
1313
0
0
0
0
0
1314
errorId := "default-label-operation"
1315
fail := func(msg string, err error) {
1316
l.Error(msg, "err", err)
1317
rp.pages.Notice(w, errorId, msg)
1318
}
1319
1320
-
labelAt := r.FormValue("label")
1321
-
_, err = db.GetLabelDefinition(rp.db, db.FilterEq("at_uri", labelAt))
1322
if err != nil {
1323
fail("Failed to unsubscribe to label.", err)
1324
return
···
1328
newRepo := f.Repo
1329
var updated []string
1330
for _, l := range newRepo.Labels {
1331
-
if l != labelAt {
1332
updated = append(updated, l)
1333
}
1334
}
···
1359
err = db.UnsubscribeLabel(
1360
rp.db,
1361
db.FilterEq("repo_at", f.RepoAt()),
1362
-
db.FilterEq("label_at", labelAt),
1363
)
1364
if err != nil {
1365
fail("Failed to unsubscribe label.", err)
···
1927
subscribedLabels[l] = struct{}{}
1928
}
1929
0
0
0
0
0
0
0
0
0
0
0
1930
rp.pages.RepoGeneralSettings(w, pages.RepoGeneralSettingsParams{
1931
-
LoggedInUser: user,
1932
-
RepoInfo: f.RepoInfo(user),
1933
-
Branches: result.Branches,
1934
-
Labels: labels,
1935
-
DefaultLabels: defaultLabels,
1936
-
SubscribedLabels: subscribedLabels,
1937
-
Tabs: settingsTabs,
1938
-
Tab: "general",
0
1939
})
1940
}
1941
···
1248
return
1249
}
1250
1251
+
if err := r.ParseForm(); err != nil {
1252
+
l.Error("invalid form", "err", err)
1253
+
return
1254
+
}
1255
+
1256
errorId := "default-label-operation"
1257
fail := func(msg string, err error) {
1258
l.Error(msg, "err", err)
1259
rp.pages.Notice(w, errorId, msg)
1260
}
1261
1262
+
labelAts := r.Form["label"]
1263
+
_, err = db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", labelAts))
1264
if err != nil {
1265
fail("Failed to subscribe to label.", err)
1266
return
1267
}
1268
1269
newRepo := f.Repo
1270
+
newRepo.Labels = append(newRepo.Labels, labelAts...)
1271
+
1272
+
// dedup
1273
+
slices.Sort(newRepo.Labels)
1274
+
newRepo.Labels = slices.Compact(newRepo.Labels)
1275
+
1276
repoRecord := newRepo.AsRecord()
1277
1278
client, err := rp.oauth.AuthorizedClient(r)
···
1296
},
1297
})
1298
1299
+
tx, err := rp.db.Begin()
0
0
0
1300
if err != nil {
1301
fail("Failed to subscribe to label.", err)
1302
return
1303
}
1304
+
defer tx.Rollback()
1305
+
1306
+
for _, l := range labelAts {
1307
+
err = db.SubscribeLabel(tx, &models.RepoLabel{
1308
+
RepoAt: f.RepoAt(),
1309
+
LabelAt: syntax.ATURI(l),
1310
+
})
1311
+
if err != nil {
1312
+
fail("Failed to subscribe to label.", err)
1313
+
return
1314
+
}
1315
+
}
1316
+
1317
+
if err := tx.Commit(); err != nil {
1318
+
fail("Failed to subscribe to label.", err)
1319
+
return
1320
+
}
1321
1322
// everything succeeded
1323
rp.pages.HxRefresh(w)
···
1335
return
1336
}
1337
1338
+
if err := r.ParseForm(); err != nil {
1339
+
l.Error("invalid form", "err", err)
1340
+
return
1341
+
}
1342
+
1343
errorId := "default-label-operation"
1344
fail := func(msg string, err error) {
1345
l.Error(msg, "err", err)
1346
rp.pages.Notice(w, errorId, msg)
1347
}
1348
1349
+
labelAts := r.Form["label"]
1350
+
_, err = db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", labelAts))
1351
if err != nil {
1352
fail("Failed to unsubscribe to label.", err)
1353
return
···
1357
newRepo := f.Repo
1358
var updated []string
1359
for _, l := range newRepo.Labels {
1360
+
if !slices.Contains(labelAts, l) {
1361
updated = append(updated, l)
1362
}
1363
}
···
1388
err = db.UnsubscribeLabel(
1389
rp.db,
1390
db.FilterEq("repo_at", f.RepoAt()),
1391
+
db.FilterIn("label_at", labelAts),
1392
)
1393
if err != nil {
1394
fail("Failed to unsubscribe label.", err)
···
1956
subscribedLabels[l] = struct{}{}
1957
}
1958
1959
+
// if there is atleast 1 unsubbed default label, show the "subscribe all" button,
1960
+
// if all default labels are subbed, show the "unsubscribe all" button
1961
+
shouldSubscribeAll := false
1962
+
for _, dl := range defaultLabels {
1963
+
if _, ok := subscribedLabels[dl.AtUri().String()]; !ok {
1964
+
// one of the default labels is not subscribed to
1965
+
shouldSubscribeAll = true
1966
+
break
1967
+
}
1968
+
}
1969
+
1970
rp.pages.RepoGeneralSettings(w, pages.RepoGeneralSettingsParams{
1971
+
LoggedInUser: user,
1972
+
RepoInfo: f.RepoInfo(user),
1973
+
Branches: result.Branches,
1974
+
Labels: labels,
1975
+
DefaultLabels: defaultLabels,
1976
+
SubscribedLabels: subscribedLabels,
1977
+
ShouldSubscribeAll: shouldSubscribeAll,
1978
+
Tabs: settingsTabs,
1979
+
Tab: "general",
1980
})
1981
}
1982
+1
appview/state/state.go
···
103
tangled.RepoIssueNSID,
104
tangled.RepoIssueCommentNSID,
105
tangled.LabelDefinitionNSID,
0
106
},
107
nil,
108
slog.Default(),
···
103
tangled.RepoIssueNSID,
104
tangled.RepoIssueCommentNSID,
105
tangled.LabelDefinitionNSID,
106
+
tangled.LabelOpNSID,
107
},
108
nil,
109
slog.Default(),