Skip to content

Commit

Permalink
Show posts in the Home page
Browse files Browse the repository at this point in the history
  • Loading branch information
miladsoft committed Nov 13, 2024
1 parent 0954da3 commit 2ff5622
Show file tree
Hide file tree
Showing 13 changed files with 436 additions and 534 deletions.
2 changes: 1 addition & 1 deletion src/app/components/event-list/event-list.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<angor-card class="mb-8 flex w-full flex-col" #expandableComments="angorCard"
*ngFor="let event of events$ | async let i = index trackBy: trackById">
<div class="mx-6 mb-4 mt-6 flex items-center sm:mx-8">
<img class="mr-4 h-10 w-10 rounded-full"
<img class="mr-4 h-10 w-10 rounded-full object-cover"
[src]="event.picture || 'images/avatars/avatar-placeholder.png'"
onerror="this.onerror=null; this.src='/images/avatars/avatar-placeholder.png';"
alt="{{ event.username }}" />
Expand Down
627 changes: 159 additions & 468 deletions src/app/components/home/home.component.html

Large diffs are not rendered by default.

166 changes: 132 additions & 34 deletions src/app/components/home/home.component.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,167 @@
import { AngorCardComponent } from '@angor/components/card';
import { TextFieldModule } from '@angular/cdk/text-field';
import {
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
ViewEncapsulation,
} from '@angular/core';
import { CommonModule, NgClass } from '@angular/common';
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDivider, MatDividerModule } from '@angular/material/divider';
import { MatDividerModule } from '@angular/material/divider';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIcon, MatIconModule } from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenu, MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
import { MatMenuModule } from '@angular/material/menu';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSlideToggle } from '@angular/material/slide-toggle';
import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip';
import { RouterLink } from '@angular/router';
import { PickerComponent } from '@ctrl/ngx-emoji-mart';
import { MatTooltipModule } from '@angular/material/tooltip';
import { ActivatedRoute, Router } from '@angular/router';
import { QRCodeModule } from 'angularx-qrcode';
import { SafeUrlPipe } from 'app/shared/pipes/safe-url.pipe';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { Subject, takeUntil } from 'rxjs';
import { EventListComponent } from '../event-list/event-list.component';
import { Subscription } from 'rxjs';
import { NostrEvent } from 'nostr-tools';
import { AngorCardComponent } from '@angor/components/card';
import { AngorConfigService } from '@angor/services/config';
import { AngorConfirmationService } from '@angor/services/confirmation';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { PaginatedEventService } from 'app/services/event.service';
import { ParseContentService } from 'app/services/parse-content.service';
import { SignerService } from 'app/services/signer.service';
import { SocialService } from 'app/services/social.service';
import { StorageService } from 'app/services/storage.service';
import { SubscriptionService } from 'app/services/subscription.service';
import { AgoPipe } from 'app/shared/pipes/ago.pipe';
import { PostProfileComponent } from '../post-event/post-profile/post-profile.component';

@Component({
selector: 'help-center',
templateUrl: './home.component.html',
encapsulation: ViewEncapsulation.None,
standalone: true,
imports: [
CommonModule,
FormsModule,
MatButtonModule,
MatDividerModule,
MatExpansionModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
RouterLink,
MatExpansionModule,
MatDivider,
MatMenu,
MatMenuTrigger,
AngorCardComponent,
MatTooltipModule,
MatIcon,
CommonModule,
MatButtonModule,
MatMenuModule,
TextFieldModule,
MatDividerModule,
NgClass,
FormsModule,
QRCodeModule,
PickerComponent,
MatSlideToggle,
SafeUrlPipe,
MatProgressSpinnerModule,
MatTooltipModule,
QRCodeModule,
InfiniteScrollModule,
EventListComponent
NgClass,
AngorCardComponent,
AgoPipe,
PostProfileComponent
],

})
export class LandingHomeComponent implements OnInit, OnDestroy {
posts: NostrEvent[] = [];
likes: any[] = [];
hasMorePosts: boolean = true;
currentPage: number = 1;
loading: boolean = false;
myLikes: NostrEvent[] = [];
private subscriptions: Subscription[] = [];

constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _signerService: SignerService,
private _storageService: StorageService,
private _sanitizer: DomSanitizer,
private _route: ActivatedRoute,
private _router: Router,
private _socialService: SocialService,
private _snackBar: MatSnackBar,
private _dialog: MatDialog,
private _angorConfigService: AngorConfigService,
private _angorConfirmationService: AngorConfirmationService,
private _eventService: PaginatedEventService,
private _subscriptionService: SubscriptionService,
private parseContent: ParseContentService
) {}

ngOnInit(): void {
this.loadInitialPosts();
this.subscribeToNewPosts();
}

private async loadInitialPosts(): Promise<void> {
this.loading = true;
const maxAttempts = 5;
const delay = 3000;

try {
for (let attemptCount = 0; attemptCount < maxAttempts; attemptCount++) {
const additionalPosts = await this._storageService.getAllPostsWithPagination(
this.currentPage,
10
);

if (additionalPosts.length > 0) {
this.posts = [...this.posts, ...additionalPosts];
this.posts.sort((a, b) => b.created_at - a.created_at);
break;
} else {
if (attemptCount < maxAttempts - 1) {
console.warn(`Attempt ${attemptCount + 1} failed, retrying in ${delay / 1000}s.`);
await this.delay(delay);
}
}
}

this.hasMorePosts = this.posts.length > 0;
if (!this.hasMorePosts) {
console.log('This user has no posts.');
}
} catch (error) {
console.error('Error loading posts:', error);
} finally {
this.loading = false;
this._changeDetectorRef.detectChanges();
}
}

private delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

getSafeUrl(url: string): SafeUrl {
return this._sanitizer.bypassSecurityTrustUrl(url);
}

ngOnDestroy(): void {
isSingleEmojiOrWord(token: string): boolean {
const trimmedToken = token.trim();
const isSingleWord = /^\w+$/.test(trimmedToken);
const isSingleEmoji = /^[\p{Emoji}]+$/u.test(trimmedToken);
return isSingleWord || isSingleEmoji;
}

private subscribeToNewPosts(): void {
const subscription = this._storageService.posts$.subscribe((newPost) => {
if (newPost) {
this.posts.unshift(newPost);
this.posts.sort((a, b) => b.created_at - a.created_at);
this._changeDetectorRef.detectChanges();
}
});
this.subscriptions.push(subscription);
}

loadNextPage(): void {
if (this.loading) return;
this.currentPage++;
this.loadInitialPosts();
}

ngOnDestroy(): void {
this.subscriptions.forEach((sub) => sub.unsubscribe());
}


Expand Down
8 changes: 5 additions & 3 deletions src/app/components/post-event/post-event.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
<div class="m-auto flex w-full max-w-140 flex-col items-start">
<angor-card class="mt-8 flex w-full flex-col">
<div class="mx-6 mb-4 mt-6 flex items-center sm:mx-8">
<img class="mr-4 h-10 w-10 rounded-full" src="/images/avatars/avatar-placeholder.png"
alt="Card cover image" />
<img class="mr-4 h-10 w-10 rounded-full object-cover" [src]="user?.picture"
onerror="this.onerror=null; this.src='/images/avatars/avatar-placeholder.png';" alt="{{
user?.display_name || user?.name || ''
}}" />
<div class="flex flex-col">
<span class="font-semibold leading-none">Milad</span>
<span class="font-semibold leading-none">{{user?.display_name || user?.name || ''}}</span>
<span class="text-secondary mt-1 text-sm leading-none">{{ post.created_at | ago }}</span>
</div>
<button class="-mr-4 ml-auto" mat-icon-button [matMenuTriggerFor]="postCardMenu02">
Expand Down
36 changes: 34 additions & 2 deletions src/app/components/post-event/post-event.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { PickerComponent } from '@ctrl/ngx-emoji-mart';
import { ContactInfoComponent } from '../chat/contact-info/contact-info.component';
import { Filter, NostrEvent } from 'nostr-tools';
import { ReplayProfileComponent } from './replay-profile/replay-profile.component';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';



Expand Down Expand Up @@ -61,14 +62,16 @@ export interface PostReaction {
MatSidenavModule,
AgoPipe,
MatProgressSpinnerModule,
ReplayProfileComponent
ReplayProfileComponent,

],
templateUrl: './post-event.component.html',
styleUrls: ['./post-event.component.scss']
})
export class PostEventComponent implements OnInit, OnDestroy {
postId: string | null = null;
post: any = null;
user:any = null;
loading = true;
loadingReactions = true;
private _unsubscribeAll: Subject<void> = new Subject<void>();
Expand All @@ -86,7 +89,9 @@ export class PostEventComponent implements OnInit, OnDestroy {
private subscriptionService: SubscriptionService,
private metadataQueueService: MetadataService,
private _changeDetectorRef: ChangeDetectorRef,
public parseContent: ParseContentService
public parseContent: ParseContentService,
private _sanitizer: DomSanitizer,

) { }

ngOnInit(): void {
Expand All @@ -97,19 +102,46 @@ export class PostEventComponent implements OnInit, OnDestroy {
this.subscribeToReactions(this.postId);
}
});

}


private async loadUserProfile(): Promise<void> {

this._storageService.getProfile(this.post.pubkey).then((metadata) => {
this.user = metadata;
console.log(this.user);

this._changeDetectorRef.detectChanges();
});

}

async loadPost(postId: string): Promise<void> {
try {
this.loading = true;
this.post = await this._storageService.getPostById(postId);

this.loadUserProfile();

this._storageService.profile$.subscribe((data) => {
if (data && data.pubKey === this.post.pubkey) {
this.user = data.metadata;
this._changeDetectorRef.detectChanges();
}
});

this.loading = false;
} catch (error) {
console.error('Error loading post:', error);
this._router.navigate(['/404']);
}
}

getSafeUrl(url: string): SafeUrl {
return this._sanitizer.bypassSecurityTrustUrl(url);
}

private subscribeToReactions(postId: string): void {
this.loadingReactions = true;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="flex items-center">
<a [href]="'/profile/' + pubkey" class="flex items-center group">
<img class="mr-4 h-10 w-10 rounded-full object-cover" [src]="displayAvatar"
[alt]="displayName"
onerror="this.onerror=null; this.src='/images/avatars/avatar-placeholder.png';" />
<div class="flex flex-col">
<span class="font-semibold leading-none">{{ displayName }}</span>
<span class="text-secondary mt-1 text-sm leading-none">{{ created_at | ago}}</span>
</div>
</a>
</div>

Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { StorageService } from 'app/services/storage.service';
import { CommonModule } from '@angular/common';
import { AgoPipe } from "../../../shared/pipes/ago.pipe";

@Component({
selector: 'app-post-profile',
standalone: true,
templateUrl: './post-profile.component.html',
styleUrls: ['./post-profile.component.scss'],
imports: [CommonModule, AgoPipe]
})
export class PostProfileComponent implements OnInit, OnDestroy {
@Input() pubkey!: string;
@Input() avatarUrl?: string;
@Input() created_at?: string;

user: any;
private subscription!: Subscription;
constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _storageService: StorageService
) {}

ngOnInit(): void {
this.loadUserProfile();

this.subscription = this._storageService.profile$.subscribe((data) => {
if (data && data.pubKey === this.pubkey) {
this.user = data.metadata;
this._changeDetectorRef.detectChanges();
}
});
}

private async loadUserProfile(): Promise<void> {
const metadata = await this._storageService.getProfile(this.pubkey);
this.user = metadata || {};
this._changeDetectorRef.detectChanges();
}

get displayName(): string {
return this.user?.display_name || this.user?.name || this.shortenPubkey(this.pubkey);
}

get displayAvatar(): string {
return this.user?.picture || this.avatarUrl || '/images/avatars/avatar-placeholder.png';
}

shortenPubkey(pubkey: string): string {
if (!pubkey) return '';
return `${pubkey.slice(0, 8)}...${pubkey.slice(-8)}`;
}

ngOnDestroy(): void {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="flex items-center">
<a [href]="'/profile/' + pubkey" class="flex items-center group">
<img
class="mr-4 h-10 w-10 rounded-full border border-gray-300 group-hover:shadow-md"
class="mr-4 h-10 w-10 rounded-full border border-gray-300 group-hover:shadow-md object-cover"
[src]="displayAvatar"
[alt]="displayName"
/>
Expand Down
Loading

0 comments on commit 2ff5622

Please sign in to comment.