Monorepo for Tangled tangled.org
4

Configure Feed

Select the types of activity you want to include in your feed.

at icy/ytnwlw 3.5 kB View raw
1package focus 2 3import ( 4 "log/slog" 5 "net/http" 6 "strconv" 7 8 "github.com/go-chi/chi/v5" 9 "tangled.org/core/appview/db" 10 "tangled.org/core/appview/middleware" 11 "tangled.org/core/appview/oauth" 12 "tangled.org/core/appview/pages" 13 "tangled.org/core/idresolver" 14) 15 16type Focus struct { 17 db *db.DB 18 oauth *oauth.OAuth 19 pages *pages.Pages 20 logger *slog.Logger 21 resolver *idresolver.Resolver 22} 23 24func New(database *db.DB, o *oauth.OAuth, res *idresolver.Resolver, p *pages.Pages, logger *slog.Logger) *Focus { 25 return &Focus{ 26 db: database, 27 oauth: o, 28 pages: p, 29 resolver: res, 30 logger: logger, 31 } 32} 33 34func (f *Focus) Router(mw *middleware.Middleware) http.Handler { 35 r := chi.NewRouter() 36 r.Use(middleware.AuthMiddleware(f.oauth)) 37 r.Post("/begin", f.BeginFocus) 38 r.Post("/end", f.EndFocus) 39 r.Post("/next", f.FocusNext) 40 return r 41} 42 43// BeginFocus activates focus mode and redirects the user to the oldest unread 44// focus-eligible notification. 45func (f *Focus) BeginFocus(w http.ResponseWriter, r *http.Request) { 46 l := f.logger.With("handler", "BeginFocus") 47 did := f.oauth.GetDid(r) 48 49 if err := db.BeginFocus(f.db, did); err != nil { 50 l.Error("failed to begin focus", "err", err) 51 http.Error(w, "internal error", http.StatusInternalServerError) 52 return 53 } 54 55 item, err := db.GetNextFocusItem(f.db, did) 56 if err != nil { 57 l.Error("failed to get first focus item", "err", err) 58 _ = db.EndFocus(f.db, did) 59 http.Redirect(w, r, "/notifications", http.StatusSeeOther) 60 return 61 } 62 if item == nil { 63 _ = db.EndFocus(f.db, did) 64 http.Redirect(w, r, "/notifications", http.StatusSeeOther) 65 return 66 } 67 68 target := item.URL(f.resolver) 69 if target == "" { 70 _ = db.EndFocus(f.db, did) 71 http.Redirect(w, r, "/notifications", http.StatusSeeOther) 72 return 73 } 74 75 http.Redirect(w, r, target, http.StatusSeeOther) 76} 77 78// EndFocus deactivates focus mode and redirects to /notifications. 79func (f *Focus) EndFocus(w http.ResponseWriter, r *http.Request) { 80 l := f.logger.With("handler", "EndFocus") 81 did := f.oauth.GetDid(r) 82 83 if err := db.EndFocus(f.db, did); err != nil { 84 l.Error("failed to end focus", "err", err) 85 http.Error(w, "internal error", http.StatusInternalServerError) 86 return 87 } 88 89 http.Redirect(w, r, "/notifications", http.StatusSeeOther) 90} 91 92// FocusNext marks the current focus notification as read and redirects to the 93// next focus item. If the queue is empty, focus mode is ended. 94func (f *Focus) FocusNext(w http.ResponseWriter, r *http.Request) { 95 l := f.logger.With("handler", "FocusNext") 96 did := f.oauth.GetDid(r) 97 98 // Mark the current item read so it leaves the queue. 99 if currentIDStr := r.FormValue("current_id"); currentIDStr != "" { 100 if currentID, err := strconv.ParseInt(currentIDStr, 10, 64); err == nil && currentID > 0 { 101 if err := db.MarkNotificationRead(f.db, currentID, did); err != nil { 102 l.Warn("failed to mark notification read", "id", currentID, "err", err) 103 } 104 } 105 } 106 107 item, err := db.GetNextFocusItem(f.db, did) 108 if err != nil { 109 l.Error("failed to get next focus item", "err", err) 110 http.Error(w, "internal error", http.StatusInternalServerError) 111 return 112 } 113 if item == nil { 114 _ = db.EndFocus(f.db, did) 115 http.Redirect(w, r, "/notifications", http.StatusSeeOther) 116 return 117 } 118 119 target := item.URL(f.resolver) 120 if target == "" { 121 _ = db.EndFocus(f.db, did) 122 http.Redirect(w, r, "/notifications", http.StatusSeeOther) 123 return 124 } 125 126 http.Redirect(w, r, target, http.StatusSeeOther) 127}