unoffical wafrn mirror wafrn.net
atproto social-network activitypub
fork

Configure Feed

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

feat: scroll by post hotkey

+70 -2
+1 -1
packages/frontend/src/app/components/post-list/post-list.component.html
··· 1 1 @for (post of posts(); track $index) { 2 - <app-post [post]="post"></app-post> 2 + <app-post [post]="post" [active]="$index === selectedPost"></app-post> 3 3 } 4 4 @if (loading()) { 5 5 <app-loader>posts</app-loader>
+68 -1
packages/frontend/src/app/components/post-list/post-list.component.ts
··· 2 2 import { ProcessedPost } from 'src/app/interfaces/processed-post' 3 3 import { PostModule } from '../post/post.module' 4 4 import { LoaderComponent } from '../loader/loader.component' 5 + import { HotkeyService, HotkeyType } from 'src/app/services/hotkey.service' 5 6 6 7 @Component({ 7 8 selector: 'app-post-list', ··· 19 20 bottomPageElement = computed(() => this.bottomPageElementRef()?.nativeElement) 20 21 bottomPageObserver: IntersectionObserver | undefined 21 22 22 - constructor() {} 23 + selectedPost: number | null = null // null means no hotkeys pressed yet 24 + 25 + constructor(hotkeyService: HotkeyService) { 26 + hotkeyService.hotkeySubscription.subscribe((type) => this.handleHotkeys(type)) 27 + } 23 28 24 29 ngOnInit() { 25 30 this.bottomPageObserver = new IntersectionObserver((entries) => { ··· 31 36 this.loadPosts.emit() 32 37 }) 33 38 this.bottomPageObserver.observe(this.bottomPageElement()!) 39 + } 40 + 41 + postElementAt(index: number): HTMLElement | null { 42 + return document.getElementById('post-element-' + this.posts().at(index)?.at(-1)?.id) 43 + } 44 + 45 + handleHotkeys(type: HotkeyType) { 46 + if (!this.visible()) return 47 + 48 + switch (type) { 49 + case HotkeyType.nextPost: 50 + this.nextPost() 51 + break 52 + case HotkeyType.previousPost: 53 + this.previousPost() 54 + break 55 + default: 56 + break 57 + } 58 + } 59 + 60 + previousPost() { 61 + if (this.selectedPost === null) { 62 + this.selectedPost = 0 63 + this.scrollToSelectedPost() 64 + return 65 + } 66 + if (this.selectedPost > 0) { 67 + this.selectedPost -= 1 68 + } 69 + this.scrollToSelectedPost() 70 + } 71 + 72 + nextPost() { 73 + if (this.selectedPost === null) { 74 + this.selectedPost = 0 75 + this.scrollToSelectedPost() 76 + return 77 + } 78 + if (this.selectedPost < this.posts().length - 1) { 79 + this.selectedPost += 1 80 + } 81 + if (this.selectedPost === this.posts().length - 1) { 82 + this.loadPosts.emit() 83 + this.scrollToLoader() 84 + return 85 + } 86 + this.scrollToSelectedPost() 87 + } 88 + 89 + scrollToLoader() { 90 + this.bottomPageElement()?.scrollIntoView() 91 + } 92 + 93 + scrollToSelectedPost() { 94 + const nextPost = this.postElementAt(this.selectedPost ?? 0) 95 + if (nextPost === null) return 96 + nextPost?.scrollIntoView({ behavior: 'instant' }) 97 + } 98 + 99 + test() { 100 + console.log('hi') 34 101 } 35 102 }
+1
packages/frontend/src/app/styles/post-card.scss
··· 10 10 padding: 1rem; 11 11 padding-bottom: 0.75rem; 12 12 content-visibility: auto; 13 + scroll-margin: 1rem; 13 14 & hr { 14 15 margin: 0.75rem -1rem; 15 16 }