Monorepo for Tangled
0

Configure Feed

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

appview/issues, appview/pulls: add subscribe/unsubscribe endpoints

author
Anirudh Oppiliappan
committer
Tangled
date (Jul 22, 2026, 1:01 PM +0300) commit fdc4b465 parent 3614eaeb change-id tnoorqvn
+121
+59
appview/issues/issues.go
··· 161 161 defs[l.AtUri().String()] = &l 162 162 } 163 163 164 + var isSubscribed *bool 165 + if user != nil { 166 + sub, found, err2 := db.GetIssueSubscription(rp.db, user.Did, issue.Id) 167 + if err2 == nil { 168 + if found { 169 + isSubscribed = &sub 170 + } else { 171 + // Implicitly subscribed when you're the author or a participant. 172 + isAuthorOrParticipant := issue.Did == user.Did 173 + if !isAuthorOrParticipant { 174 + for _, p := range issue.Participants() { 175 + if p.String() == user.Did { 176 + isAuthorOrParticipant = true 177 + break 178 + } 179 + } 180 + } 181 + if isAuthorOrParticipant { 182 + t := true 183 + isSubscribed = &t 184 + } 185 + } 186 + } 187 + } 188 + 164 189 err = rp.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ 165 190 BaseParams: pages.BaseParamsFromContext(r.Context()), 166 191 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), ··· 171 196 UserReacted: userReactions, 172 197 LabelDefs: defs, 173 198 VouchRelationships: vouchRelationships, 199 + IsSubscribed: isSubscribed, 174 200 }) 175 201 if err != nil { 176 202 l.Error("failed to render issue", "err", err) 177 203 } 204 + } 205 + 206 + // SubscribeIssue handles subscribe/unsubscribe for a specific issue. 207 + func (rp *Issues) SubscribeIssue(w http.ResponseWriter, r *http.Request) { 208 + l := rp.logger.With("handler", "SubscribeIssue") 209 + user := rp.oauth.GetMultiAccountUser(r) 210 + if user == nil { 211 + w.WriteHeader(http.StatusUnauthorized) 212 + return 213 + } 214 + 215 + issue, ok := r.Context().Value("issue").(*models.Issue) 216 + if !ok { 217 + l.Error("failed to get issue from context") 218 + w.WriteHeader(http.StatusNotFound) 219 + return 220 + } 221 + 222 + subscribe := r.FormValue("subscribe") != "false" 223 + 224 + if err := db.UpsertIssueSubscription(rp.db, user.Did, issue.Id, subscribe); err != nil { 225 + l.Error("failed to update issue subscription", "err", err) 226 + w.WriteHeader(http.StatusInternalServerError) 227 + return 228 + } 229 + 230 + // Return the updated subscription button fragment. 231 + repoInfo := rp.repoResolver.GetRepoInfo(r, user) 232 + rp.pages.IssueSubscribeFragment(w, pages.IssueSubscribeParams{ 233 + RepoInfo: repoInfo, 234 + IssueId: issue.IssueId, 235 + IsSubscribed: &subscribe, 236 + }) 178 237 } 179 238 180 239 func (rp *Issues) EditIssue(w http.ResponseWriter, r *http.Request) {
+1
appview/issues/router.go
··· 26 26 r.Delete("/", i.DeleteIssue) 27 27 r.Post("/close", i.CloseIssue) 28 28 r.Post("/reopen", i.ReopenIssue) 29 + r.Post("/subscribe", i.SubscribeIssue) 29 30 }) 30 31 }) 31 32
+1
appview/pulls/router.go
··· 44 44 // it is handled within the route 45 45 r.Post("/close", s.ClosePull) 46 46 r.Post("/reopen", s.ReopenPull) 47 + r.Post("/subscribe", s.SubscribePull) 47 48 // collaborators only 48 49 r.Group(func(r chi.Router) { 49 50 r.Use(mw.RepoPermissionMiddleware("repo:push"))
+60
appview/pulls/single.go
··· 264 264 diff = s.combinedDiff(pull, roundIdInt) 265 265 } 266 266 267 + var isSubscribed *bool 268 + if user != nil { 269 + pullDbId := int64(pull.ID) 270 + sub, found, err2 := db.GetPullSubscription(s.db, user.Did, pullDbId) 271 + if err2 == nil { 272 + if found { 273 + isSubscribed = &sub 274 + } else { 275 + // Implicitly subscribed if author or participant. 276 + isAuthorOrParticipant := pull.OwnerDid == user.Did 277 + if !isAuthorOrParticipant { 278 + for _, p := range pull.Participants() { 279 + if p.String() == user.Did { 280 + isAuthorOrParticipant = true 281 + break 282 + } 283 + } 284 + } 285 + if isAuthorOrParticipant { 286 + t := true 287 + isSubscribed = &t 288 + } 289 + } 290 + } 291 + } 292 + 267 293 err = s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{ 268 294 BaseParams: pages.BaseParamsFromContext(r.Context()), 269 295 RepoInfo: s.repoResolver.GetRepoInfo(r, user), ··· 285 311 LabelDefs: defs, 286 312 VouchRelationships: vouchRelationships, 287 313 VouchSkips: vouchSkips, 314 + IsSubscribed: isSubscribed, 288 315 }) 289 316 if err != nil { 290 317 l.Error("failed to render page", "err", err) 291 318 } 319 + } 320 + 321 + // SubscribePull handles subscribe/unsubscribe for a specific pull request. 322 + func (s *Pulls) SubscribePull(w http.ResponseWriter, r *http.Request) { 323 + l := s.logger.With("handler", "SubscribePull") 324 + user := s.oauth.GetMultiAccountUser(r) 325 + if user == nil { 326 + w.WriteHeader(http.StatusUnauthorized) 327 + return 328 + } 329 + 330 + pull, ok := r.Context().Value("pull").(*models.Pull) 331 + if !ok { 332 + l.Error("failed to get pull from context") 333 + w.WriteHeader(http.StatusNotFound) 334 + return 335 + } 336 + 337 + subscribe := r.FormValue("subscribe") != "false" 338 + pullDbId := int64(pull.ID) 339 + 340 + if err := db.UpsertPullSubscription(s.db, user.Did, pullDbId, subscribe); err != nil { 341 + l.Error("failed to update pull subscription", "err", err) 342 + w.WriteHeader(http.StatusInternalServerError) 343 + return 344 + } 345 + 346 + repoInfo := s.repoResolver.GetRepoInfo(r, user) 347 + s.pages.PullSubscribeFragment(w, pages.PullSubscribeParams{ 348 + RepoInfo: repoInfo, 349 + PullId: pull.PullId, 350 + IsSubscribed: &subscribe, 351 + }) 292 352 } 293 353 294 354 func (s *Pulls) combinedDiff(pull *models.Pull, round int) types.DiffRenderer {