+29
-1
appview/models/notifications.go
+29
-1
appview/models/notifications.go
···
1
package models
2
3
+
import (
4
+
"time"
5
+
)
6
7
type NotificationType string
8
···
32
RepoId *int64
33
IssueId *int64
34
PullId *int64
35
+
}
36
+
37
+
// lucide icon that represents this notification
38
+
func (n *Notification) Icon() string {
39
+
switch n.Type {
40
+
case NotificationTypeRepoStarred:
41
+
return "star"
42
+
case NotificationTypeIssueCreated:
43
+
return "circle-dot"
44
+
case NotificationTypeIssueCommented:
45
+
return "message-square"
46
+
case NotificationTypeIssueClosed:
47
+
return "ban"
48
+
case NotificationTypePullCreated:
49
+
return "git-pull-request-create"
50
+
case NotificationTypePullCommented:
51
+
return "message-square"
52
+
case NotificationTypePullMerged:
53
+
return "git-merge"
54
+
case NotificationTypePullClosed:
55
+
return "git-pull-request-closed"
56
+
case NotificationTypeFollowed:
57
+
return "user-plus"
58
+
default:
59
+
return ""
60
+
}
61
}
62
63
type NotificationWithEntity struct {
+64
-14
appview/pages/templates/notifications/fragments/item.html
+64
-14
appview/pages/templates/notifications/fragments/item.html
···
1
{{define "notifications/fragments/item"}}
2
-
<div class="border border-gray-200 dark:border-gray-700 rounded-sm p-3 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors {{if not .Read}}bg-blue-50 dark:bg-blue-900/20{{end}}">
3
-
{{if .Issue}}
4
-
{{template "issueNotification" .}}
5
-
{{else if .Pull}}
6
-
{{template "pullNotification" .}}
7
-
{{else if .Repo}}
8
-
{{template "repoNotification" .}}
9
-
{{else if eq .Type "followed"}}
10
-
{{template "followNotification" .}}
11
-
{{else}}
12
-
{{template "genericNotification" .}}
13
-
{{end}}
14
-
</div>
15
{{end}}
16
17
{{define "issueNotification"}}
18
{{$url := printf "/%s/%s/issues/%d" (resolve .Repo.Did) .Repo.Name .Issue.IssueId}}
19
<a
···
37
{{ i "ban" "w-4 h-4" }}
38
</span>
39
{{end}}
40
-
{{template "user/fragments/picHandle" (resolve .ActorDid)}}
41
{{if eq .Type "issue_created"}}
42
<span class="text-gray-500 dark:text-gray-400">opened issue</span>
43
{{else if eq .Type "issue_commented"}}
···
1
{{define "notifications/fragments/item"}}
2
+
<div
3
+
class="
4
+
w-full mx-auto rounded drop-shadow-sm dark:text-white bg-white dark:bg-gray-800 px-2 md:px-6 py-4 transition-colors
5
+
{{if not .Read}}bg-blue-50 dark:bg-blue-900/20 border border-blue-500 dark:border-sky-800{{end}}
6
+
flex gap-2 items-center
7
+
"
8
+
>
9
+
10
+
{{ template "notificationIcon" . }}
11
+
<div class="flex-1 w-full flex flex-col gap-1">
12
+
<span>{{ template "notificationHeader" . }}</span>
13
+
<span class="text-sm text-gray-500 dark:text-gray-400">{{ template "notificationSummary" . }}</span>
14
+
</div>
15
+
16
+
</div>
17
{{end}}
18
19
+
{{ define "notificationIcon" }}
20
+
<div class="flex-shrink-0 max-h-full w-16 h-16 relative">
21
+
<img class="object-cover rounded-full p-2" src="{{ fullAvatar .ActorDid }}" />
22
+
<div class="absolute border-2 border-white dark:border-gray-800 bg-gray-200 dark:bg-gray-700 bottom-1 right-1 rounded-full p-2 flex items-center justify-center z-10">
23
+
{{ i .Icon "size-3 text-black dark:text-white" }}
24
+
</div>
25
+
</div>
26
+
{{ end }}
27
+
28
+
{{ define "notificationHeader" }}
29
+
{{ $actor := resolve .ActorDid }}
30
+
31
+
<span class="text-black dark:text-white w-fit">{{ $actor }}</span>
32
+
{{ if eq .Type "repo_starred" }}
33
+
starred <span class="text-black dark:text-white">{{ resolve .Repo.Did }}/{{ .Repo.Name }}</span>
34
+
{{ else if eq .Type "issue_created" }}
35
+
opened an issue
36
+
{{ else if eq .Type "issue_commented" }}
37
+
commented on an issue
38
+
{{ else if eq .Type "issue_closed" }}
39
+
closed an issue
40
+
{{ else if eq .Type "pull_created" }}
41
+
created a pull request
42
+
{{ else if eq .Type "pull_commented" }}
43
+
commented on a pull request
44
+
{{ else if eq .Type "pull_merged" }}
45
+
merged a pull request
46
+
{{ else if eq .Type "pull_closed" }}
47
+
closed a pull request
48
+
{{ else if eq .Type "followed" }}
49
+
followed you
50
+
{{ else }}
51
+
{{ end }}
52
+
{{ end }}
53
+
54
+
{{ define "notificationSummary" }}
55
+
{{ if eq .Type "repo_starred" }}
56
+
<!-- no summary -->
57
+
{{ else if .Issue }}
58
+
#{{.Issue.IssueId}} {{.Issue.Title}} on {{resolve .Repo.Did}}/{{.Repo.Name}}
59
+
{{ else if .Pull }}
60
+
#{{.Pull.PullId}} {{.Pull.Title}} on {{resolve .Repo.Did}}/{{.Repo.Name}}
61
+
{{ else if eq .Type "followed" }}
62
+
<!-- no summary -->
63
+
{{ else }}
64
+
{{ end }}
65
+
{{ end }}
66
+
67
{{define "issueNotification"}}
68
{{$url := printf "/%s/%s/issues/%d" (resolve .Repo.Did) .Repo.Name .Issue.IssueId}}
69
<a
···
87
{{ i "ban" "w-4 h-4" }}
88
</span>
89
{{end}}
90
+
{{template "user/fragments/picHandle" .ActorDid}}
91
{{if eq .Type "issue_created"}}
92
<span class="text-gray-500 dark:text-gray-400">opened issue</span>
93
{{else if eq .Type "issue_commented"}}
+10
-24
appview/pages/templates/notifications/list.html
+10
-24
appview/pages/templates/notifications/list.html
···
1
{{ define "title" }}notifications{{ end }}
2
3
{{ define "content" }}
4
-
<div class="p-6">
5
-
<div class="flex items-center justify-between mb-4">
6
<p class="text-xl font-bold dark:text-white">Notifications</p>
7
<a href="/settings/notifications" class="flex items-center gap-2">
8
{{ i "settings" "w-4 h-4" }}
···
11
</div>
12
</div>
13
14
-
<div class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">
15
{{if .Notifications}}
16
-
<div class="flex flex-col gap-4" id="notifications-list">
17
{{range .Notifications}}
18
{{template "notifications/fragments/item" .}}
19
{{end}}
20
</div>
21
22
-
{{if .HasMore}}
23
-
<div class="mt-6 text-center">
24
-
<button
25
-
class="btn gap-2 group"
26
-
hx-get="/notifications?offset={{.NextOffset}}&limit={{.Limit}}"
27
-
hx-target="#notifications-list"
28
-
hx-swap="beforeend"
29
-
>
30
-
{{ i "chevron-down" "w-4 h-4 group-[.htmx-request]:hidden" }}
31
-
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
32
-
Load more
33
-
</button>
34
-
</div>
35
-
{{end}}
36
{{else}}
37
-
<div class="text-center py-12">
38
-
<div class="w-16 h-16 mx-auto mb-4 text-gray-300 dark:text-gray-600">
39
-
{{ i "bell-off" "w-16 h-16" }}
40
</div>
41
-
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">No notifications</h3>
42
-
<p class="text-gray-600 dark:text-gray-400">When you receive notifications, they'll appear here.</p>
43
</div>
44
{{end}}
45
-
</div>
46
{{ end }}
···
1
{{ define "title" }}notifications{{ end }}
2
3
{{ define "content" }}
4
+
<div class="px-6 py-4">
5
+
<div class="flex items-center justify-between">
6
<p class="text-xl font-bold dark:text-white">Notifications</p>
7
<a href="/settings/notifications" class="flex items-center gap-2">
8
{{ i "settings" "w-4 h-4" }}
···
11
</div>
12
</div>
13
14
{{if .Notifications}}
15
+
<div class="flex flex-col gap-2" id="notifications-list">
16
{{range .Notifications}}
17
{{template "notifications/fragments/item" .}}
18
{{end}}
19
</div>
20
21
{{else}}
22
+
<div class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">
23
+
<div class="text-center py-12">
24
+
<div class="w-16 h-16 mx-auto mb-4 text-gray-300 dark:text-gray-600">
25
+
{{ i "bell-off" "w-16 h-16" }}
26
+
</div>
27
+
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">No notifications</h3>
28
+
<p class="text-gray-600 dark:text-gray-400">When you receive notifications, they'll appear here.</p>
29
</div>
30
</div>
31
{{end}}
32
{{ end }}