tangled
alpha
login
or
join now
yoten.app
/
yoten
17
fork
atom
Yōten: A social tracker for your language learning journey built on the atproto.
17
fork
atom
overview
issues
pulls
pipelines
feat: add notification route handlers
brookjeynes.dev
6 months ago
63e15018
9c8f8954
verified
This commit was signed with the committer's
known signature
.
brookjeynes.dev
SSH Key Fingerprint:
SHA256:N3n3PCBSiXfS6EHlmGdx+LMEruJMj6FS2hqaXyfsw0s=
+127
3 changed files
expand all
collapse all
unified
split
internal
server
handlers
notification.go
profile.go
router.go
+84
internal/server/handlers/notification.go
···
1
1
+
package handlers
2
2
+
3
3
+
import (
4
4
+
"log"
5
5
+
"net/http"
6
6
+
"strconv"
7
7
+
8
8
+
"yoten.app/internal/clients/bsky"
9
9
+
"yoten.app/internal/db"
10
10
+
"yoten.app/internal/server/htmx"
11
11
+
"yoten.app/internal/server/views/partials"
12
12
+
)
13
13
+
14
14
+
func (h *Handler) HandleNotificationFeed(w http.ResponseWriter, r *http.Request) {
15
15
+
user, err := bsky.GetUserWithBskyProfile(h.Oauth, r)
16
16
+
if err != nil {
17
17
+
log.Println("failed to get logged-in user:", err)
18
18
+
htmx.HxRedirect(w, "/login")
19
19
+
return
20
20
+
}
21
21
+
22
22
+
pageStr := r.URL.Query().Get("page")
23
23
+
if pageStr == "" {
24
24
+
pageStr = "1"
25
25
+
}
26
26
+
page, err := strconv.ParseInt(pageStr, 10, 64)
27
27
+
if err != nil {
28
28
+
log.Println("failed to parse page value:", err)
29
29
+
page = 1
30
30
+
}
31
31
+
if page == 0 {
32
32
+
page = 1
33
33
+
}
34
34
+
35
35
+
const pageSize = 10
36
36
+
offset := (page - 1) * pageSize
37
37
+
38
38
+
switch r.Method {
39
39
+
case http.MethodGet:
40
40
+
notifications, err := db.GetNotificationsByDid(h.Db, user.Did, pageSize+1, int(offset))
41
41
+
if err != nil {
42
42
+
log.Println("failed to retrieve notifications:", err)
43
43
+
htmx.HxError(w, http.StatusInternalServerError, "Failed to get notifications, try again later.")
44
44
+
return
45
45
+
}
46
46
+
47
47
+
hydratedNotifications, err := h.getBskyProfileHydratedNotificationFeed(notifications)
48
48
+
if err != nil {
49
49
+
log.Println("failed to hydrate notifications with bsky profile:", err)
50
50
+
htmx.HxError(w, http.StatusInternalServerError, "Failed to get notifications, try again later.")
51
51
+
return
52
52
+
}
53
53
+
54
54
+
nextPage := 0
55
55
+
if len(hydratedNotifications) > pageSize {
56
56
+
nextPage = int(page + 1)
57
57
+
hydratedNotifications = hydratedNotifications[:pageSize]
58
58
+
}
59
59
+
60
60
+
partials.NotificationFeed(partials.NotificationFeedProps{
61
61
+
User: user,
62
62
+
Feed: hydratedNotifications,
63
63
+
NextPage: nextPage,
64
64
+
}).Render(r.Context(), w)
65
65
+
}
66
66
+
}
67
67
+
68
68
+
func (h *Handler) HandleNotificationMarkAllRead(w http.ResponseWriter, r *http.Request) {
69
69
+
user, err := bsky.GetUserWithBskyProfile(h.Oauth, r)
70
70
+
if err != nil {
71
71
+
log.Println("failed to get logged-in user:", err)
72
72
+
htmx.HxRedirect(w, "/login")
73
73
+
return
74
74
+
}
75
75
+
76
76
+
err = db.MarkAllNotificationsAsRead(h.Db, user.Did)
77
77
+
if err != nil {
78
78
+
log.Println("failed to mark all notifications:", err)
79
79
+
htmx.HxError(w, http.StatusInternalServerError, "Failed to mark all notifications as read, try again later.")
80
80
+
return
81
81
+
}
82
82
+
83
83
+
htmx.HxRedirect(w, "/profile/notifications")
84
84
+
}
+36
internal/server/handlers/profile.go
···
584
584
}).Render(r.Context(), w)
585
585
}
586
586
}
587
587
+
588
588
+
func (h *Handler) HandleNotificationsPage(w http.ResponseWriter, r *http.Request) {
589
589
+
user, err := bsky.GetUserWithBskyProfile(h.Oauth, r)
590
590
+
if err != nil {
591
591
+
log.Println("failed to get logged-in user:", err)
592
592
+
htmx.HxRedirect(w, "/login")
593
593
+
return
594
594
+
}
595
595
+
596
596
+
views.NotificationsPage(views.NotificationsPageParams{
597
597
+
User: user,
598
598
+
}).Render(r.Context(), w)
599
599
+
}
600
600
+
601
601
+
func (h *Handler) getBskyProfileHydratedNotificationFeed(feed []db.Notification) ([]db.NotificationWithBskyHandle, error) {
602
602
+
hydratedFeed := []db.NotificationWithBskyHandle{}
603
603
+
604
604
+
profileDids := utils.Map(feed, func(item db.Notification) string {
605
605
+
return item.ActorDid
606
606
+
})
607
607
+
bskyProfiles, err := bsky.GetBskyProfiles(profileDids)
608
608
+
if err != nil {
609
609
+
return hydratedFeed, err
610
610
+
}
611
611
+
612
612
+
for i := range feed {
613
613
+
if profile, ok := bskyProfiles[feed[i].ActorDid]; ok {
614
614
+
hydratedFeed = append(hydratedFeed, db.NotificationWithBskyHandle{
615
615
+
Notification: feed[i],
616
616
+
ActorBskyHandle: profile.Handle,
617
617
+
})
618
618
+
}
619
619
+
}
620
620
+
621
621
+
return hydratedFeed, nil
622
622
+
}
+7
internal/server/handlers/router.go
···
62
62
r.Post("/edit", h.HandleEditProfilePage)
63
63
r.Get("/activities", h.HandleActivitiesPage)
64
64
r.Get("/resources", h.HandleResourcesPage)
65
65
+
r.Get("/notifications", h.HandleNotificationsPage)
65
66
})
66
67
67
68
r.Route("/follow", func(r chi.Router) {
68
69
r.Use(middleware.AuthMiddleware(h.Oauth))
69
70
r.Post("/", h.HandleFollow)
70
71
r.Delete("/", h.HandleFollow)
72
72
+
})
73
73
+
74
74
+
r.Route("/notification", func(r chi.Router) {
75
75
+
r.Use(middleware.AuthMiddleware(h.Oauth))
76
76
+
r.Get("/feed", h.HandleNotificationFeed)
77
77
+
r.Post("/mark-all-read", h.HandleNotificationMarkAllRead)
71
78
})
72
79
73
80
r.Route("/resource", func(r chi.Router) {