Multicolumn Bluesky client powered by Angular

feat: post-replies grouping in author-feed

kbenlloch debef20c 7d16a8c2

Changed files
+30 -48
src
+18 -10
src/app/components/feeds/author-feed/author-feed.component.html
··· 6 6 (scrollTop)="manageRefresh();" 7 7 > 8 8 @if (posts) { 9 - @for (post of posts; track post.uuid) { 10 - <post-card 11 - [post]="post.post()" 12 - [reply]="post.reply" 13 - [reason]="post.reason" 14 - (postChange)="post.post.set($event)" 15 - (click)="dialogService.openThread(post.post().uri)" 16 - (onEmbedRecord)="dialogService.openRecord($event)" 17 - class="cursor-pointer hover:bg-primary/2 w-full py-3 px-2" 18 - /> 9 + @for (group of posts; track group.uuid) { 10 + @for (post of group.thread; track post.uuid; let i = $index) { 11 + <post-card 12 + [post]="post.post()" 13 + [reply]="group.thread.length == 1 ? post.reply : undefined" 14 + [reason]="post.reason" 15 + (postChange)="post.post.set($event)" 16 + (click)="dialogService.openThread(post.post().uri)" 17 + (onEmbedRecord)="dialogService.openRecord($event)" 18 + [parent]="group | postCardParentPipe: i" 19 + [grandParent]="group | postCardGrandParentPipe: i" 20 + class="cursor-pointer hover:bg-primary/2 w-full px-3 pb-1" 21 + [ngClass]="{ 22 + 'pt-3': i == 0, 23 + 'pt-2': i > 0 24 + }" 25 + /> 26 + } 19 27 20 28 <divider/> 21 29 }
+11 -7
src/app/components/feeds/author-feed/author-feed.component.ts
··· 14 14 import {$Typed} from '@atproto/api'; 15 15 import {ReasonRepost} from '@atproto/api/dist/client/types/app/bsky/feed/defs'; 16 16 import {PostService} from '@services/post.service'; 17 - import {SignalizedFeedViewPost} from '@models/signalized-feed-view-post'; 18 17 import {from} from 'rxjs'; 19 18 import {PostCardComponent} from '@components/cards/post-card/post-card.component'; 20 19 import {MessageService} from '@services/message.service'; 21 20 import {DialogService} from '@services/dialog.service'; 22 21 import {DividerComponent} from '@components/shared/divider/divider.component'; 23 22 import {FeedService} from '@services/feed.service'; 23 + import {GroupedPost} from '@models/grouped-post'; 24 + import {PostCardGrandParentPipe} from '@shared/pipes/post-card-grandparent.pipe'; 25 + import {PostCardParentPipe} from '@shared/pipes/post-card-parent.pipe'; 24 26 25 27 @Component({ 26 28 selector: 'author-feed', ··· 29 31 ScrollDirective, 30 32 PostCardComponent, 31 33 DividerComponent, 34 + PostCardGrandParentPipe, 35 + PostCardParentPipe, 32 36 ], 33 37 templateUrl: './author-feed.component.html', 34 38 changeDetection: ChangeDetectionStrategy.OnPush ··· 37 41 feed = viewChild<ElementRef>('feed'); 38 42 did = input.required<string>(); 39 43 40 - posts: SignalizedFeedViewPost[]; 44 + posts: GroupedPost[]; 41 45 cursor: string; 42 46 loading = true; 43 47 reloadReady = false; ··· 75 79 this.loading = true; 76 80 from(agent.getAuthorFeed({ 77 81 actor: this.did(), 78 - limit: 15 82 + limit: 50 79 83 })).subscribe({ 80 84 next: response => { 81 85 this.cursor = response.data.cursor; 82 - this.posts = response.data.feed.map(fvp => this.feedService.parseFeedViewPost(fvp)); 86 + this.posts = this.feedService.groupFeedViewPosts(response.data.feed); 83 87 this.cdRef.markForCheck(); 84 88 setTimeout(() => { 85 89 this.loading = false; ··· 96 100 from(agent.getAuthorFeed({ 97 101 actor: this.did(), 98 102 cursor: this.cursor, 99 - limit: 15 103 + limit: 50 100 104 })).subscribe({ 101 105 next: response => { 102 106 this.cursor = response.data.cursor; 103 - const newPosts = response.data.feed.map(fvp => this.feedService.parseFeedViewPost(fvp)); 107 + const newPosts = this.feedService.groupFeedViewPosts(response.data.feed); 104 108 this.posts = [...this.posts, ...newPosts]; 105 109 this.cdRef.markForCheck(); 106 110 setTimeout(() => { ··· 125 129 })).subscribe({ 126 130 next: response => { 127 131 const post = response.data.feed[0]; 128 - const lastPost = this.posts[0]; 132 + const lastPost = this.posts[0].thread[this.posts[0].thread.length-1]; 129 133 let isNewPost = false; 130 134 131 135 if (post) {
+1 -23
src/app/components/feeds/timeline-feed/timeline-feed.component.html
··· 25 25 /> 26 26 } 27 27 28 - <divider 29 - /> 28 + <divider/> 30 29 } 31 30 <!-- } @else {--> 32 31 <!-- <div--> ··· 40 39 <!-- </div>--> 41 40 } 42 41 </div> 43 - 44 - <ng-template 45 - #onePost 46 - let-thread="thread" 47 - > 48 - <post-card 49 - [post]="thread[0].post()" 50 - [reply]="thread[0].reply" 51 - [reason]="thread[0].reason" 52 - (postChange)="thread[0].post.set($event)" 53 - (click)="dialogService.openThread(thread[0].post().uri)" 54 - (onEmbedRecord)="dialogService.openRecord($event)" 55 - class="cursor-pointer hover:bg-primary/2 w-full px-3 pt-3 pb-1" 56 - /> 57 - </ng-template> 58 - 59 - <ng-template 60 - #twoPosts 61 - let-thread="thread" 62 - > 63 - </ng-template>
-4
src/app/components/feeds/timeline-feed/timeline-feed.component.ts
··· 157 157 this.initData(); 158 158 } 159 159 } 160 - 161 - log(obj: any) { 162 - console.log(obj) 163 - } 164 160 }
-4
src/app/shared/pipes/post-card-parent.pipe.ts
··· 8 8 transform(group: GroupedPost, index: number): boolean { 9 9 if (group.thread.length < 2) return false; 10 10 if (index == group.thread.length-1) return false; 11 - if (group.thread.length > 2 && 12 - index == 0 && 13 - group.thread[0].post().uri == (group.thread[1].post().record as any)?.reply?.parent?.uri 14 - ) return false; 15 11 16 12 return true; 17 13 }