Monorepo for Tangled tangled.org

appview: allow timeline db queries to be filterable by users follows #626

closed opened by willdot.net targeting master from [deleted fork]: feat/filter-user-timeline

Signed-off-by: Will Andrews did:plc:dadhhalkfcq3gucaq25hjqon

Labels
enhancement
assignee

None yet.

Participants 3
AT URI
at://did:plc:dadhhalkfcq3gucaq25hjqon/sh.tangled.repo.pull/3m24d33byfj22
+468 -16
Diff #1
+38 -10
appview/db/timeline.go
··· 9 9 10 10 // TODO: this gathers heterogenous events from different sources and aggregates 11 11 // them in code; if we did this entirely in sql, we could order and limit and paginate easily 12 - func MakeTimeline(e Execer, limit int, loggedInUserDid string) ([]models.TimelineEvent, error) { 12 + func MakeTimeline(e Execer, limit int, loggedInUserDid string, limitToUsersIsFollowing bool) ([]models.TimelineEvent, error) { 13 13 var events []models.TimelineEvent 14 14 15 - repos, err := getTimelineRepos(e, limit, loggedInUserDid) 15 + var userIsFollowing []string 16 + if limitToUsersIsFollowing { 17 + following, err := GetFollowing(e, loggedInUserDid) 18 + if err != nil { 19 + return nil, err 20 + } 21 + 22 + userIsFollowing = make([]string, 0, len(following)) 23 + for _, follow := range following { 24 + userIsFollowing = append(userIsFollowing, follow.SubjectDid) 25 + } 26 + } 27 + 28 + repos, err := getTimelineRepos(e, limit, loggedInUserDid, userIsFollowing) 16 29 if err != nil { 17 30 return nil, err 18 31 } 19 32 20 - stars, err := getTimelineStars(e, limit, loggedInUserDid) 33 + stars, err := getTimelineStars(e, limit, loggedInUserDid, userIsFollowing) 21 34 if err != nil { 22 35 return nil, err 23 36 } 24 37 25 - follows, err := getTimelineFollows(e, limit, loggedInUserDid) 38 + follows, err := getTimelineFollows(e, limit, loggedInUserDid, userIsFollowing) 26 39 if err != nil { 27 40 return nil, err 28 41 } ··· 70 83 return isStarred, starCount 71 84 } 72 85 73 - func getTimelineRepos(e Execer, limit int, loggedInUserDid string) ([]models.TimelineEvent, error) { 74 - repos, err := GetRepos(e, limit) 86 + func getTimelineRepos(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 87 + filters := make([]filter, 0) 88 + if userIsFollowing != nil { 89 + filters = append(filters, FilterIn("did", userIsFollowing)) 90 + } 91 + 92 + repos, err := GetRepos(e, limit, filters...) 75 93 if err != nil { 76 94 return nil, err 77 95 } ··· 125 143 return events, nil 126 144 } 127 145 128 - func getTimelineStars(e Execer, limit int, loggedInUserDid string) ([]models.TimelineEvent, error) { 129 - stars, err := GetStars(e, limit) 146 + func getTimelineStars(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 147 + filters := make([]filter, 0) 148 + if userIsFollowing != nil { 149 + filters = append(filters, FilterIn("starred_by_did", userIsFollowing)) 150 + } 151 + 152 + stars, err := GetStars(e, limit, filters...) 130 153 if err != nil { 131 154 return nil, err 132 155 } ··· 166 189 return events, nil 167 190 } 168 191 169 - func getTimelineFollows(e Execer, limit int, loggedInUserDid string) ([]models.TimelineEvent, error) { 170 - follows, err := GetFollows(e, limit) 192 + func getTimelineFollows(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 193 + filters := make([]filter, 0) 194 + if userIsFollowing != nil { 195 + filters = append(filters, FilterIn("user_did", userIsFollowing)) 196 + } 197 + 198 + follows, err := GetFollows(e, limit, filters...) 171 199 if err != nil { 172 200 return nil, err 173 201 }
+411 -4
appview/state/state.go
··· 5 5 6 6 7 7 8 + "log" 9 + "log/slog" 10 + "net/http" 11 + "strconv" 12 + "strings" 13 + "time" 8 14 9 15 10 16 ··· 220 226 221 227 222 228 229 + } 223 230 231 + func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { 232 + filtered := getTimelineFilteredQuery(r) 233 + user := s.oauth.GetUser(r) 224 234 235 + var userDid string 236 + if user != nil { 237 + userDid = user.Did 238 + } 239 + timeline, err := db.MakeTimeline(s.db, 50, userDid, filtered) 240 + if err != nil { 241 + log.Println(err) 242 + s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.") 225 243 226 244 227 245 ··· 231 249 232 250 233 251 234 - if user != nil { 235 - userDid = user.Did 236 - } 237 - timeline, err := db.MakeTimeline(s.db, 50, userDid) 252 + 253 + LoggedInUser: user, 254 + Timeline: timeline, 255 + Repos: repos, 256 + Filtered: filtered, 257 + }) 258 + } 259 + 260 + 261 + 262 + 263 + 264 + 265 + 266 + 267 + 268 + 269 + 270 + 271 + 272 + 273 + 274 + 275 + 276 + 277 + 278 + 279 + 280 + 281 + 282 + 283 + 284 + 285 + 286 + 287 + 288 + 289 + 290 + 291 + 292 + 293 + 294 + 295 + 296 + } 297 + 298 + func (s *State) Home(w http.ResponseWriter, r *http.Request) { 299 + filtered := getTimelineFilteredQuery(r) 300 + timeline, err := db.MakeTimeline(s.db, 5, "", filtered) 238 301 if err != nil { 239 302 log.Println(err) 240 303 s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.") 304 + 305 + 306 + 307 + 308 + 309 + 310 + 311 + 312 + 313 + 314 + 315 + LoggedInUser: nil, 316 + Timeline: timeline, 317 + Repos: repos, 318 + Filtered: filtered, 319 + }) 320 + } 321 + 322 + 323 + 324 + 325 + 326 + 327 + 328 + 329 + 330 + 331 + 332 + 333 + 334 + 335 + 336 + 337 + 338 + 339 + 340 + 341 + 342 + 343 + 344 + 345 + 346 + 347 + 348 + 349 + 350 + 351 + 352 + 353 + 354 + 355 + 356 + 357 + 358 + 359 + 360 + 361 + 362 + 363 + 364 + 365 + 366 + 367 + 368 + 369 + 370 + 371 + 372 + 373 + 374 + 375 + 376 + 377 + 378 + 379 + 380 + 381 + 382 + 383 + 384 + 385 + 386 + 387 + 388 + 389 + 390 + 391 + 392 + 393 + 394 + 395 + 396 + 397 + 398 + 399 + 400 + 401 + 402 + 403 + 404 + 405 + 406 + 407 + 408 + 409 + 410 + 411 + 412 + 413 + 414 + 415 + 416 + 417 + 418 + 419 + 420 + 421 + 422 + 423 + 424 + 425 + 426 + 427 + 428 + 429 + 430 + 431 + 432 + 433 + 434 + 435 + 436 + 437 + 438 + 439 + 440 + 441 + 442 + 443 + 444 + 445 + 446 + 447 + 448 + 449 + 450 + 451 + 452 + 453 + 454 + 455 + 456 + 457 + 458 + 459 + 460 + 461 + 462 + 463 + 464 + 465 + 466 + 467 + 468 + 469 + 470 + 471 + 472 + 473 + 474 + 475 + 476 + 477 + 478 + 479 + 480 + 481 + 482 + 483 + 484 + 485 + 486 + 487 + 488 + 489 + 490 + 491 + 492 + 493 + 494 + 495 + 496 + 497 + 498 + 499 + 500 + 501 + 502 + 503 + 504 + 505 + 506 + 507 + 508 + 509 + 510 + 511 + 512 + 513 + 514 + 515 + 516 + 517 + 518 + 519 + 520 + 521 + 522 + 523 + 524 + 525 + 526 + 527 + 528 + 529 + 530 + 531 + 532 + 533 + 534 + 535 + 536 + 537 + 538 + 539 + 540 + 541 + 542 + 543 + 544 + 545 + 546 + 547 + 548 + 549 + 550 + 551 + 552 + 553 + 554 + 555 + 556 + 557 + 558 + 559 + 560 + 561 + 562 + 563 + 564 + 565 + 566 + 567 + 568 + 569 + 570 + 571 + 572 + 573 + 574 + 575 + 576 + 577 + 578 + 579 + 580 + 581 + 582 + 583 + 584 + 585 + 586 + 587 + 588 + 589 + 590 + 591 + 592 + 593 + 594 + 595 + 596 + 597 + 598 + 599 + 600 + 601 + 602 + 603 + 604 + 605 + 606 + 607 + 608 + 609 + 610 + 611 + 612 + 613 + 614 + 615 + 616 + 617 + 618 + 619 + 620 + 621 + 622 + 623 + 624 + 625 + 626 + 627 + 628 + 629 + 630 + 631 + 632 + 633 + 634 + 635 + 636 + return nil 637 + } 638 + 639 + func getTimelineFilteredQuery(r *http.Request) bool { 640 + filteredStr := r.URL.Query().Get("filtered") 641 + if filteredStr == "" { 642 + return false 643 + } 644 + 645 + res, _ := strconv.ParseBool(filteredStr) 646 + return res 647 + }
+1
appview/pages/pages.go
··· 306 306 LoggedInUser *oauth.User 307 307 Timeline []models.TimelineEvent 308 308 Repos []models.Repo 309 + Filtered bool 309 310 } 310 311 311 312 func (p *Pages) Timeline(w io.Writer, params TimelineParams) error {
+18 -2
appview/pages/templates/timeline/fragments/timeline.html
··· 1 1 {{ define "timeline/fragments/timeline" }} 2 2 <div class="py-4"> 3 - <div class="px-6 pb-4"> 4 - <p class="text-xl font-bold dark:text-white">Timeline</p> 3 + 4 + <div class="flex gap-2 px-6 pb-4"> 5 + <div> 6 + <p class="text-xl font-bold dark:text-white">Timeline</p> 7 + </div> 8 + {{ if .LoggedInUser }} 9 + <div> 10 + {{ if .Filtered }} 11 + <a href="/timeline" class="hover:underline text-sm"> 12 + Show All 13 + </a> 14 + {{ else }} 15 + <a href="/timeline?filtered=true" class="hover:underline text-sm"> 16 + Show following only 17 + </a> 18 + {{ end }} 19 + </div> 20 + {{ end }} 5 21 </div> 6 22 7 23 <div class="flex flex-col gap-4">

History

5 rounds 4 comments
sign up or login to add to the discussion
6 commits
expand
d76a775d
appview: switch to indigo oauth library
4a1653c5
appview: improve logged-out CTAs
56721612
appview/{db,pages,models}: show tooltips for user handles when hovering on reactions
98084555
appview: allow timeline db queries to be filterable by users follows
e067fac7
appview: allows the user to toggle between a filtered or non filtered timeline
2812b7c6
remove the front end changes
expand 0 comments
closed without merging
5 commits
expand
d76a775d
appview: switch to indigo oauth library
4a1653c5
appview: improve logged-out CTAs
56721612
appview/{db,pages,models}: show tooltips for user handles when hovering on reactions
98084555
appview: allow timeline db queries to be filterable by users follows
e067fac7
appview: allows the user to toggle between a filtered or non filtered timeline
expand 0 comments
2 commits
expand
9366b5f4
appview: allow timeline db queries to be filterable by users follows
2bb3d5d6
appview: allows the user to toggle between a filtered or non filtered timeline
expand 1 comment

following up on discord for this patch, but it works like a charm!

2 commits
expand
af82d9d6
appview: allow timeline db queries to be filterable by users follows
bba81ead
appview: allows the user to toggle between a filtered or non filtered timeline
expand 0 comments
1 commit
expand
af82d9d6
appview: allow timeline db queries to be filterable by users follows
expand 3 comments

This paves the way for allowing users to filter their timeline by just content of users they follow. I'm not great at front end stuff so may need some help getting an actual toggle into the UI.

Note: See this issue as to why the PR has no description

https://tangled.org/@tangled.org/core/issues/241

I'd be happy to assist with the toggle. I'm out on vacation next week, but feel free to reach out on Discord after that :-)

Amazing, thank you! I was planning on giving it a go this weekend and see how I get on but will ping you if I fall into a pit of dispair.