+42
-50
appview/notify/merged_notifier.go
+42
-50
appview/notify/merged_notifier.go
···
2
3
import (
4
"context"
5
6
"tangled.org/core/appview/models"
7
)
···
16
17
var _ Notifier = &mergedNotifier{}
18
19
-
func (m *mergedNotifier) NewRepo(ctx context.Context, repo *models.Repo) {
20
-
for _, notifier := range m.notifiers {
21
-
notifier.NewRepo(ctx, repo)
22
}
23
}
24
25
func (m *mergedNotifier) NewStar(ctx context.Context, star *models.Star) {
26
-
for _, notifier := range m.notifiers {
27
-
notifier.NewStar(ctx, star)
28
-
}
29
}
30
func (m *mergedNotifier) DeleteStar(ctx context.Context, star *models.Star) {
31
-
for _, notifier := range m.notifiers {
32
-
notifier.DeleteStar(ctx, star)
33
-
}
34
}
35
36
func (m *mergedNotifier) NewIssue(ctx context.Context, issue *models.Issue) {
37
-
for _, notifier := range m.notifiers {
38
-
notifier.NewIssue(ctx, issue)
39
-
}
40
}
41
func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment) {
42
-
for _, notifier := range m.notifiers {
43
-
notifier.NewIssueComment(ctx, comment)
44
-
}
45
}
46
47
func (m *mergedNotifier) NewIssueClosed(ctx context.Context, issue *models.Issue) {
48
-
for _, notifier := range m.notifiers {
49
-
notifier.NewIssueClosed(ctx, issue)
50
-
}
51
}
52
53
func (m *mergedNotifier) NewFollow(ctx context.Context, follow *models.Follow) {
54
-
for _, notifier := range m.notifiers {
55
-
notifier.NewFollow(ctx, follow)
56
-
}
57
}
58
func (m *mergedNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {
59
-
for _, notifier := range m.notifiers {
60
-
notifier.DeleteFollow(ctx, follow)
61
-
}
62
}
63
64
func (m *mergedNotifier) NewPull(ctx context.Context, pull *models.Pull) {
65
-
for _, notifier := range m.notifiers {
66
-
notifier.NewPull(ctx, pull)
67
-
}
68
}
69
func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *models.PullComment) {
70
-
for _, notifier := range m.notifiers {
71
-
notifier.NewPullComment(ctx, comment)
72
-
}
73
}
74
75
func (m *mergedNotifier) NewPullMerged(ctx context.Context, pull *models.Pull) {
76
-
for _, notifier := range m.notifiers {
77
-
notifier.NewPullMerged(ctx, pull)
78
-
}
79
}
80
81
func (m *mergedNotifier) NewPullClosed(ctx context.Context, pull *models.Pull) {
82
-
for _, notifier := range m.notifiers {
83
-
notifier.NewPullClosed(ctx, pull)
84
-
}
85
}
86
87
func (m *mergedNotifier) UpdateProfile(ctx context.Context, profile *models.Profile) {
88
-
for _, notifier := range m.notifiers {
89
-
notifier.UpdateProfile(ctx, profile)
90
-
}
91
}
92
93
-
func (m *mergedNotifier) NewString(ctx context.Context, string *models.String) {
94
-
for _, notifier := range m.notifiers {
95
-
notifier.NewString(ctx, string)
96
-
}
97
}
98
99
-
func (m *mergedNotifier) EditString(ctx context.Context, string *models.String) {
100
-
for _, notifier := range m.notifiers {
101
-
notifier.EditString(ctx, string)
102
-
}
103
}
104
105
func (m *mergedNotifier) DeleteString(ctx context.Context, did, rkey string) {
106
-
for _, notifier := range m.notifiers {
107
-
notifier.DeleteString(ctx, did, rkey)
108
-
}
109
}
···
2
3
import (
4
"context"
5
+
"reflect"
6
+
"sync"
7
8
"tangled.org/core/appview/models"
9
)
···
18
19
var _ Notifier = &mergedNotifier{}
20
21
+
// fanout calls the same method on all notifiers concurrently
22
+
func (m *mergedNotifier) fanout(method string, args ...any) {
23
+
var wg sync.WaitGroup
24
+
for _, n := range m.notifiers {
25
+
wg.Add(1)
26
+
go func(notifier Notifier) {
27
+
defer wg.Done()
28
+
v := reflect.ValueOf(notifier).MethodByName(method)
29
+
in := make([]reflect.Value, len(args))
30
+
for i, arg := range args {
31
+
in[i] = reflect.ValueOf(arg)
32
+
}
33
+
v.Call(in)
34
+
}(n)
35
}
36
+
wg.Wait()
37
+
}
38
+
39
+
func (m *mergedNotifier) NewRepo(ctx context.Context, repo *models.Repo) {
40
+
m.fanout("NewRepo", ctx, repo)
41
}
42
43
func (m *mergedNotifier) NewStar(ctx context.Context, star *models.Star) {
44
+
m.fanout("NewStar", ctx, star)
45
}
46
+
47
func (m *mergedNotifier) DeleteStar(ctx context.Context, star *models.Star) {
48
+
m.fanout("DeleteStar", ctx, star)
49
}
50
51
func (m *mergedNotifier) NewIssue(ctx context.Context, issue *models.Issue) {
52
+
m.fanout("NewIssue", ctx, issue)
53
}
54
+
55
func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment) {
56
+
m.fanout("NewIssueComment", ctx, comment)
57
}
58
59
func (m *mergedNotifier) NewIssueClosed(ctx context.Context, issue *models.Issue) {
60
+
m.fanout("NewIssueClosed", ctx, issue)
61
}
62
63
func (m *mergedNotifier) NewFollow(ctx context.Context, follow *models.Follow) {
64
+
m.fanout("NewFollow", ctx, follow)
65
}
66
+
67
func (m *mergedNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {
68
+
m.fanout("DeleteFollow", ctx, follow)
69
}
70
71
func (m *mergedNotifier) NewPull(ctx context.Context, pull *models.Pull) {
72
+
m.fanout("NewPull", ctx, pull)
73
}
74
+
75
func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *models.PullComment) {
76
+
m.fanout("NewPullComment", ctx, comment)
77
}
78
79
func (m *mergedNotifier) NewPullMerged(ctx context.Context, pull *models.Pull) {
80
+
m.fanout("NewPullMerged", ctx, pull)
81
}
82
83
func (m *mergedNotifier) NewPullClosed(ctx context.Context, pull *models.Pull) {
84
+
m.fanout("NewPullClosed", ctx, pull)
85
}
86
87
func (m *mergedNotifier) UpdateProfile(ctx context.Context, profile *models.Profile) {
88
+
m.fanout("UpdateProfile", ctx, profile)
89
}
90
91
+
func (m *mergedNotifier) NewString(ctx context.Context, s *models.String) {
92
+
m.fanout("NewString", ctx, s)
93
}
94
95
+
func (m *mergedNotifier) EditString(ctx context.Context, s *models.String) {
96
+
m.fanout("EditString", ctx, s)
97
}
98
99
func (m *mergedNotifier) DeleteString(ctx context.Context, did, rkey string) {
100
+
m.fanout("DeleteString", ctx, did, rkey)
101
}
+3
-2
appview/pages/funcmap.go
+3
-2
appview/pages/funcmap.go
···
297
},
298
299
"normalizeForHtmlId": func(s string) string {
300
+
normalized := strings.ReplaceAll(s, ":", "_")
301
+
normalized = strings.ReplaceAll(normalized, ".", "_")
302
+
return normalized
303
},
304
"sshFingerprint": func(pubKey string) string {
305
fp, err := crypto.SSHFingerprint(pubKey)
+2
-2
appview/pages/templates/repo/issues/fragments/issueCommentHeader.html
+2
-2
appview/pages/templates/repo/issues/fragments/issueCommentHeader.html
···
34
35
{{ define "editIssueComment" }}
36
<a
37
-
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group"
38
hx-get="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment/{{ .Comment.Id }}/edit"
39
hx-swap="outerHTML"
40
hx-target="#comment-body-{{.Comment.Id}}">
···
44
45
{{ define "deleteIssueComment" }}
46
<a
47
-
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group"
48
hx-delete="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment/{{ .Comment.Id }}/"
49
hx-confirm="Are you sure you want to delete your comment?"
50
hx-swap="outerHTML"
···
34
35
{{ define "editIssueComment" }}
36
<a
37
+
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group cursor-pointer"
38
hx-get="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment/{{ .Comment.Id }}/edit"
39
hx-swap="outerHTML"
40
hx-target="#comment-body-{{.Comment.Id}}">
···
44
45
{{ define "deleteIssueComment" }}
46
<a
47
+
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group cursor-pointer"
48
hx-delete="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment/{{ .Comment.Id }}/"
49
hx-confirm="Are you sure you want to delete your comment?"
50
hx-swap="outerHTML"
+2
-2
appview/pages/templates/repo/issues/issue.html
+2
-2
appview/pages/templates/repo/issues/issue.html
···
84
85
{{ define "editIssue" }}
86
<a
87
-
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group"
88
hx-get="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/edit"
89
hx-swap="innerHTML"
90
hx-target="#issue-{{.Issue.IssueId}}">
···
94
95
{{ define "deleteIssue" }}
96
<a
97
-
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group"
98
hx-delete="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/"
99
hx-confirm="Are you sure you want to delete your issue?"
100
hx-swap="none">
···
84
85
{{ define "editIssue" }}
86
<a
87
+
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group cursor-pointer"
88
hx-get="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/edit"
89
hx-swap="innerHTML"
90
hx-target="#issue-{{.Issue.IssueId}}">
···
94
95
{{ define "deleteIssue" }}
96
<a
97
+
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group cursor-pointer"
98
hx-delete="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/"
99
hx-confirm="Are you sure you want to delete your issue?"
100
hx-swap="none">
+2
appview/pages/templates/repo/settings/access.html
+2
appview/pages/templates/repo/settings/access.html
+2
appview/pages/templates/spindles/fragments/addMemberModal.html
+2
appview/pages/templates/spindles/fragments/addMemberModal.html
+1
-1
appview/pages/templates/user/fragments/followCard.html
+1
-1
appview/pages/templates/user/fragments/followCard.html
···
3
<div class="flex flex-col divide-y divide-gray-200 dark:divide-gray-700 rounded-sm">
4
<div class="py-4 px-6 drop-shadow-sm rounded bg-white dark:bg-gray-800 flex items-center gap-4">
5
<div class="flex-shrink-0 max-h-full w-24 h-24">
6
-
<img class="object-cover rounded-full p-2" src="{{ fullAvatar $userIdent }}" />
7
</div>
8
9
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-2 w-full">
···
3
<div class="flex flex-col divide-y divide-gray-200 dark:divide-gray-700 rounded-sm">
4
<div class="py-4 px-6 drop-shadow-sm rounded bg-white dark:bg-gray-800 flex items-center gap-4">
5
<div class="flex-shrink-0 max-h-full w-24 h-24">
6
+
<img class="object-cover rounded-full p-2" src="{{ fullAvatar $userIdent }}" alt="{{ $userIdent }}" />
7
</div>
8
9
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-2 w-full">
+3
appview/pages/templates/user/login.html
+3
appview/pages/templates/user/login.html
+1
appview/state/follow.go
+1
appview/state/follow.go