mirror of
https://github.com/block-core/angor-hub-old.git
synced 2025-12-17 09:54:19 +01:00
Implement strict template mode
- Fixes all issues with strict mode. - Might introduce some bugs as there was a few properties and types that was not correct.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { AngorAlertComponent } from '@angor/components/alert';
|
import { AngorAlertComponent, AngorAlertType } from '@angor/components/alert';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
@@ -22,6 +22,7 @@ import { Subscription } from 'rxjs';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'auth-sign-in',
|
selector: 'auth-sign-in',
|
||||||
templateUrl: './login.component.html',
|
templateUrl: './login.component.html',
|
||||||
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
RouterLink,
|
RouterLink,
|
||||||
AngorAlertComponent,
|
AngorAlertComponent,
|
||||||
@@ -39,10 +40,10 @@ import { Subscription } from 'rxjs';
|
|||||||
export class LoginComponent implements OnInit {
|
export class LoginComponent implements OnInit {
|
||||||
SecretKeyLoginForm: FormGroup;
|
SecretKeyLoginForm: FormGroup;
|
||||||
MenemonicLoginForm: FormGroup;
|
MenemonicLoginForm: FormGroup;
|
||||||
secAlert = { type: 'error', message: '' };
|
secAlert = { type: 'error' as AngorAlertType, message: '' };
|
||||||
showSecAlert = false;
|
showSecAlert = false;
|
||||||
|
|
||||||
menemonicAlert = { type: 'error', message: '' };
|
menemonicAlert = { type: 'error' as AngorAlertType, message: '' };
|
||||||
showMenemonicAlert = false;
|
showMenemonicAlert = false;
|
||||||
|
|
||||||
loading = false;
|
loading = false;
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ export class ChatService implements OnDestroy {
|
|||||||
|
|
||||||
if (Number(existingChat.lastMessageAt) < createdAt) {
|
if (Number(existingChat.lastMessageAt) < createdAt) {
|
||||||
existingChat.lastMessage = message;
|
existingChat.lastMessage = message;
|
||||||
existingChat.lastMessageAt = createdAt.toString();
|
existingChat.lastMessageAt = createdAt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -329,7 +329,7 @@ export class ChatService implements OnDestroy {
|
|||||||
displayName: contactInfo.displayName || contactInfo.name || 'Unknown',
|
displayName: contactInfo.displayName || contactInfo.name || 'Unknown',
|
||||||
},
|
},
|
||||||
lastMessage: message,
|
lastMessage: message,
|
||||||
lastMessageAt: createdAt.toString(),
|
lastMessageAt: createdAt,
|
||||||
messages: [newMessage],
|
messages: [newMessage],
|
||||||
};
|
};
|
||||||
this.chatList.push(newChat);
|
this.chatList.push(newChat);
|
||||||
@@ -544,7 +544,7 @@ export class ChatService implements OnDestroy {
|
|||||||
picture: '/images/avatars/avatar-placeholder.png',
|
picture: '/images/avatars/avatar-placeholder.png',
|
||||||
},
|
},
|
||||||
lastMessage: 'new chat...',
|
lastMessage: 'new chat...',
|
||||||
lastMessageAt: Math.floor(Date.now() / 1000).toString() || '0',
|
lastMessageAt: Math.floor(Date.now() / 1000) || 0,
|
||||||
messages: [],
|
messages: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export interface Contact {
|
|||||||
pubKey?: string;
|
pubKey?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
|
avatar?: string; // TODO: I introduced this to fix strict mode, must be verified.
|
||||||
picture?: string;
|
picture?: string;
|
||||||
about?: string;
|
about?: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
@@ -33,7 +34,7 @@ export interface Chat {
|
|||||||
unreadCount?: number;
|
unreadCount?: number;
|
||||||
muted?: boolean;
|
muted?: boolean;
|
||||||
lastMessage?: string;
|
lastMessage?: string;
|
||||||
lastMessageAt?: string;
|
lastMessageAt?: number;
|
||||||
messages?: {
|
messages?: {
|
||||||
id?: string;
|
id?: string;
|
||||||
chatId?: string;
|
chatId?: string;
|
||||||
|
|||||||
@@ -187,10 +187,6 @@
|
|||||||
*ngIf="chat.contact?.picture"
|
*ngIf="chat.contact?.picture"
|
||||||
class="h-full w-full rounded-full object-cover"
|
class="h-full w-full rounded-full object-cover"
|
||||||
[src]="chat.contact?.picture"
|
[src]="chat.contact?.picture"
|
||||||
(error)="
|
|
||||||
this.src =
|
|
||||||
'/images/avatars/avatar-placeholder.png'
|
|
||||||
"
|
|
||||||
alt="Contact picture"
|
alt="Contact picture"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { Chat } from '../chat.types';
|
|||||||
selector: 'chat-contact-info',
|
selector: 'chat-contact-info',
|
||||||
templateUrl: './contact-info.component.html',
|
templateUrl: './contact-info.component.html',
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
standalone: true,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [MatButtonModule, MatIconModule, RouterModule]
|
imports: [MatButtonModule, MatIconModule, RouterModule]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,12 +20,12 @@
|
|||||||
<!-- Contact info -->
|
<!-- Contact info -->
|
||||||
<div class="ml-2 mr-2 flex cursor-pointer items-center lg:ml-0" (click)="openContactInfo()">
|
<div class="ml-2 mr-2 flex cursor-pointer items-center lg:ml-0" (click)="openContactInfo()">
|
||||||
<div class="relative flex h-10 w-10 flex-0 items-center justify-center">
|
<div class="relative flex h-10 w-10 flex-0 items-center justify-center">
|
||||||
@if (chat.contact?.picture) {
|
@if (chat.contact?.avatar) {
|
||||||
<img class="h-full w-full rounded-full object-cover" [src]="chat.contact?.picture"
|
<img class="h-full w-full rounded-full object-cover" [src]="chat.contact?.avatar"
|
||||||
onerror="this.onerror=null; this.src='/images/avatars/avatar-placeholder.png';"
|
onerror="this.onerror=null; this.src='/images/avatars/avatar-placeholder.png';"
|
||||||
alt="Contact picture" />
|
alt="Contact picture" />
|
||||||
}
|
}
|
||||||
@if (!chat.contact?.picture) {
|
@if (!chat.contact?.avatar) {
|
||||||
<div
|
<div
|
||||||
class="flex h-full w-full items-center justify-center rounded-full bg-gray-200 text-lg uppercase text-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
class="flex h-full w-full items-center justify-center rounded-full bg-gray-200 text-lg uppercase text-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
||||||
{{ chat.contact?.name.charAt(0) }}
|
{{ chat.contact?.name.charAt(0) }}
|
||||||
|
|||||||
@@ -30,18 +30,19 @@ import { AngorConfigService } from '@angor/services/config';
|
|||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
import { PickerComponent } from '@ctrl/ngx-emoji-mart';
|
import { PickerComponent } from '@ctrl/ngx-emoji-mart';
|
||||||
import { Chat } from 'app/layout/common/quick-chat/quick-chat.types';
|
|
||||||
import { GifDialogComponent } from 'app/shared/gif-dialog/gif-dialog.component';
|
import { GifDialogComponent } from 'app/shared/gif-dialog/gif-dialog.component';
|
||||||
import { Subject, takeUntil } from 'rxjs';
|
import { Subject, takeUntil } from 'rxjs';
|
||||||
import { ChatService } from '../chat.service';
|
import { ChatService } from '../chat.service';
|
||||||
import { ContactInfoComponent } from '../contact-info/contact-info.component';
|
import { ContactInfoComponent } from '../contact-info/contact-info.component';
|
||||||
import { ParseContentService } from 'app/services/parse-content.service';
|
import { ParseContentService } from 'app/services/parse-content.service';
|
||||||
|
import { Chat } from '../chat.types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'chat-conversation',
|
selector: 'chat-conversation',
|
||||||
templateUrl: './conversation.component.html',
|
templateUrl: './conversation.component.html',
|
||||||
styleUrls: ['./conversation.component.css'],
|
styleUrls: ['./conversation.component.css'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
standalone: true,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
MatSidenavModule,
|
MatSidenavModule,
|
||||||
@@ -81,7 +82,7 @@ export class ConversationComponent implements OnInit, OnDestroy {
|
|||||||
private _angorConfigService: AngorConfigService,
|
private _angorConfigService: AngorConfigService,
|
||||||
public dialog: MatDialog,
|
public dialog: MatDialog,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
private parseContent: ParseContentService
|
public parseContent: ParseContentService
|
||||||
) {
|
) {
|
||||||
const SpeechRecognition =
|
const SpeechRecognition =
|
||||||
(window as any).SpeechRecognition ||
|
(window as any).SpeechRecognition ||
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ export class PostEventComponent implements OnInit, OnDestroy {
|
|||||||
private _unsubscribeAll: Subject<void> = new Subject<void>();
|
private _unsubscribeAll: Subject<void> = new Subject<void>();
|
||||||
private subscription: Subscription = new Subscription();
|
private subscription: Subscription = new Subscription();
|
||||||
|
|
||||||
|
// TODO: This should obviously not be on the component but come from some service. Added to fix strict mode.
|
||||||
|
darkMode: boolean = false;
|
||||||
|
|
||||||
likes: PostReaction[] = [];
|
likes: PostReaction[] = [];
|
||||||
reposts: PostReaction[] = [];
|
reposts: PostReaction[] = [];
|
||||||
zaps: PostReaction[] = [];
|
zaps: PostReaction[] = [];
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { MetadataService } from 'app/services/metadata.service';
|
|||||||
export class PostProfileComponent implements OnInit, OnDestroy {
|
export class PostProfileComponent implements OnInit, OnDestroy {
|
||||||
@Input() pubkey!: string;
|
@Input() pubkey!: string;
|
||||||
@Input() avatarUrl?: string;
|
@Input() avatarUrl?: string;
|
||||||
@Input() created_at?: string;
|
@Input() created_at?: number;
|
||||||
@Output() userChange = new EventEmitter<any>();
|
@Output() userChange = new EventEmitter<any>();
|
||||||
|
|
||||||
user: any;
|
user: any;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { AngorCardComponent } from '@angor/components/card';
|
import { AngorCardComponent } from '@angor/components/card';
|
||||||
import { AngorConfigService } from '@angor/services/config';
|
import { AngorConfigService } from '@angor/services/config';
|
||||||
import { AngorConfirmationService } from '@angor/services/confirmation';
|
import { AngorConfirmationService } from '@angor/services/confirmation';
|
||||||
|
import { Clipboard } from '@angular/cdk/clipboard';
|
||||||
import { TextFieldModule } from '@angular/cdk/text-field';
|
import { TextFieldModule } from '@angular/cdk/text-field';
|
||||||
import { CommonModule, DatePipe, NgClass, NgTemplateOutlet } from '@angular/common';
|
import { CommonModule, NgClass } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
@@ -17,34 +18,31 @@ import { FormsModule } from '@angular/forms';
|
|||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { 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 { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||||
import { PickerComponent } from '@ctrl/ngx-emoji-mart';
|
import { PickerComponent } from '@ctrl/ngx-emoji-mart';
|
||||||
import { EventService } from 'app/services/event.service';
|
|
||||||
import { SignerService } from 'app/services/signer.service';
|
|
||||||
import { ContactEvent, StorageService } from 'app/services/storage.service';
|
|
||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
|
||||||
import { Filter, NostrEvent } from 'nostr-tools';
|
|
||||||
import { Observable, Subject, takeUntil } from 'rxjs';
|
|
||||||
import { SubscriptionService } from 'app/services/subscription.service';
|
|
||||||
import { Clipboard } from '@angular/cdk/clipboard';
|
|
||||||
import { MatExpansionModule } from '@angular/material/expansion';
|
|
||||||
import { ParseContentService } from 'app/services/parse-content.service';
|
|
||||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
|
||||||
import { AgoPipe } from 'app/shared/pipes/ago.pipe';
|
|
||||||
import { ZapDialogComponent } from 'app/shared/zap-dialog/zap-dialog.component';
|
|
||||||
import { ZapDialogData } from 'app/services/interfaces';
|
|
||||||
import { Contacts } from 'nostr-tools/kinds';
|
|
||||||
import { PostComponent } from 'app/layout/common/post/post.component';
|
import { PostComponent } from 'app/layout/common/post/post.component';
|
||||||
import { BookmarkService } from 'app/services/bookmark.service';
|
import { BookmarkService } from 'app/services/bookmark.service';
|
||||||
|
import { EventService } from 'app/services/event.service';
|
||||||
|
import { ZapDialogData } from 'app/services/interfaces';
|
||||||
|
import { ParseContentService } from 'app/services/parse-content.service';
|
||||||
|
import { SignerService } from 'app/services/signer.service';
|
||||||
|
import { ContactEvent, StorageService } from 'app/services/storage.service';
|
||||||
|
import { SubscriptionService } from 'app/services/subscription.service';
|
||||||
|
import { ZapDialogComponent } from 'app/shared/zap-dialog/zap-dialog.component';
|
||||||
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
|
import { Filter, NostrEvent } from 'nostr-tools';
|
||||||
|
import { Observable, Subject } from 'rxjs';
|
||||||
interface Chip {
|
interface Chip {
|
||||||
color?: string;
|
color?: string;
|
||||||
selected?: string;
|
selected?: string;
|
||||||
@@ -79,11 +77,10 @@ interface Chip {
|
|||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatExpansionModule,
|
MatExpansionModule,
|
||||||
MatSidenavModule,
|
MatSidenavModule,
|
||||||
PostComponent
|
PostComponent,
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export class ProfileComponent implements OnInit, OnDestroy {
|
export class ProfileComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@ViewChild('eventInput', { static: false }) eventInput: ElementRef;
|
@ViewChild('eventInput', { static: false }) eventInput: ElementRef;
|
||||||
@ViewChild('commentInput') commentInput: ElementRef;
|
@ViewChild('commentInput') commentInput: ElementRef;
|
||||||
|
|
||||||
@@ -130,8 +127,12 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
followingList: ContactEvent[] = [];
|
followingList: ContactEvent[] = [];
|
||||||
aboutExpanded: boolean = true;
|
aboutExpanded: boolean = true;
|
||||||
|
|
||||||
stats$!: Observable<{ pubKey: string, totalContacts: number, followersCount: number, followingCount: number }>;
|
stats$!: Observable<{
|
||||||
|
pubKey: string;
|
||||||
|
totalContacts: number;
|
||||||
|
followersCount: number;
|
||||||
|
followingCount: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
bookmarks$: Observable<string[]>;
|
bookmarks$: Observable<string[]>;
|
||||||
bookmarkedProjectNpubs: string[] = [];
|
bookmarkedProjectNpubs: string[] = [];
|
||||||
@@ -149,9 +150,8 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
private _eventService: EventService,
|
private _eventService: EventService,
|
||||||
private _subscriptionService: SubscriptionService,
|
private _subscriptionService: SubscriptionService,
|
||||||
private _clipboard: Clipboard,
|
private _clipboard: Clipboard,
|
||||||
private parseContent: ParseContentService,
|
public parseContent: ParseContentService,
|
||||||
private _bookmarkService: BookmarkService,
|
private _bookmarkService: BookmarkService
|
||||||
|
|
||||||
) {
|
) {
|
||||||
this.bookmarks$ = this._bookmarkService.bookmarks$;
|
this.bookmarks$ = this._bookmarkService.bookmarks$;
|
||||||
}
|
}
|
||||||
@@ -173,6 +173,9 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackByFn(index: number, item: any): number {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
private checkIfRoutePubKeyIsFollowing(): void {
|
private checkIfRoutePubKeyIsFollowing(): void {
|
||||||
if (!this.routePubKey || !this.followersList) {
|
if (!this.routePubKey || !this.followersList) {
|
||||||
@@ -180,10 +183,11 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isFollowing = this.followersList.some(follower => follower.pubkey === this.routePubKey);
|
this.isFollowing = this.followersList.some(
|
||||||
|
(follower) => follower.pubkey === this.routePubKey
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private processRouteParams(): void {
|
private processRouteParams(): void {
|
||||||
this._route.paramMap.subscribe((params) => {
|
this._route.paramMap.subscribe((params) => {
|
||||||
const routeKey = params.get('pubkey') || '';
|
const routeKey = params.get('pubkey') || '';
|
||||||
@@ -194,7 +198,8 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
this.routePubKey = hexPubKey;
|
this.routePubKey = hexPubKey;
|
||||||
this.isCurrentUserProfile = false;
|
this.isCurrentUserProfile = false;
|
||||||
} else {
|
} else {
|
||||||
this.errorMessage = 'Public key is invalid. Please check your input.';
|
this.errorMessage =
|
||||||
|
'Public key is invalid. Please check your input.';
|
||||||
this.setCurrentUserProfile();
|
this.setCurrentUserProfile();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -212,7 +217,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private loadUserProfileData(pubKey: string): void {
|
private loadUserProfileData(pubKey: string): void {
|
||||||
this.loadUserProfile(pubKey);
|
this.loadUserProfile(pubKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private isValidHexPubkey(pubkey: string): boolean {
|
private isValidHexPubkey(pubkey: string): boolean {
|
||||||
const hexPattern = /^[a-fA-F0-9]{64}$/;
|
const hexPattern = /^[a-fA-F0-9]{64}$/;
|
||||||
@@ -227,11 +232,12 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
while (attemptCount < maxAttempts) {
|
while (attemptCount < maxAttempts) {
|
||||||
const additionalPosts = await this._storageService.getPostsByPubKeysWithPagination(
|
const additionalPosts =
|
||||||
[this.routePubKey],
|
await this._storageService.getPostsByPubKeysWithPagination(
|
||||||
this.currentPage,
|
[this.routePubKey],
|
||||||
10
|
this.currentPage,
|
||||||
);
|
10
|
||||||
|
);
|
||||||
|
|
||||||
if (additionalPosts.length > 0) {
|
if (additionalPosts.length > 0) {
|
||||||
this.posts = [...this.posts, ...additionalPosts];
|
this.posts = [...this.posts, ...additionalPosts];
|
||||||
@@ -258,24 +264,25 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
this.refreshUI();
|
this.refreshUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private delay(ms: number): Promise<void> {
|
private delay(ms: number): Promise<void> {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private subscribeToNewPosts(): void {
|
private subscribeToNewPosts(): void {
|
||||||
if (!this.isCurrentUserProfile) {
|
if (!this.isCurrentUserProfile) {
|
||||||
const filters: Filter[] = [
|
const filters: Filter[] = [
|
||||||
{ authors: [this.routePubKey], kinds: [1] },
|
{ authors: [this.routePubKey], kinds: [1] },
|
||||||
];
|
];
|
||||||
this.postsSubscriptionId = this._subscriptionService.addSubscriptions(filters, async (event: NostrEvent) => {
|
this.postsSubscriptionId =
|
||||||
if (!this.isReply(event)) {
|
this._subscriptionService.addSubscriptions(
|
||||||
this._storageService.savePost(event);
|
filters,
|
||||||
}
|
async (event: NostrEvent) => {
|
||||||
});
|
if (!this.isReply(event)) {
|
||||||
}
|
this._storageService.savePost(event);
|
||||||
else {
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
this._storageService.posts$.subscribe((newPost) => {
|
this._storageService.posts$.subscribe((newPost) => {
|
||||||
if (newPost) {
|
if (newPost) {
|
||||||
if (newPost.pubkey === this.routePubKey) {
|
if (newPost.pubkey === this.routePubKey) {
|
||||||
@@ -285,7 +292,6 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,24 +302,26 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
return replyTags.length > 0;
|
return replyTags.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
loadNextPage(): void {
|
loadNextPage(): void {
|
||||||
if (this.loading) return;
|
if (this.loading) return;
|
||||||
this.currentPage++;
|
this.currentPage++;
|
||||||
this.loadInitialPosts();
|
this.loadInitialPosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
toggleAbout(): void {
|
toggleAbout(): void {
|
||||||
this.aboutExpanded = !this.aboutExpanded;
|
this.aboutExpanded = !this.aboutExpanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.subscriptionId) {
|
if (this.subscriptionId) {
|
||||||
this._subscriptionService.removeSubscriptionById(this.subscriptionId);
|
this._subscriptionService.removeSubscriptionById(
|
||||||
|
this.subscriptionId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (this.postsSubscriptionId) {
|
if (this.postsSubscriptionId) {
|
||||||
this._subscriptionService.removeSubscriptionById(this.postsSubscriptionId);
|
this._subscriptionService.removeSubscriptionById(
|
||||||
|
this.postsSubscriptionId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._unsubscribeAll.next(null);
|
this._unsubscribeAll.next(null);
|
||||||
@@ -334,8 +342,8 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
const cachedMetadata =
|
||||||
const cachedMetadata = await this._storageService.getProfile(publicKey);
|
await this._storageService.getProfile(publicKey);
|
||||||
if (cachedMetadata) {
|
if (cachedMetadata) {
|
||||||
this.profileUser = cachedMetadata;
|
this.profileUser = cachedMetadata;
|
||||||
this.refreshUI();
|
this.refreshUI();
|
||||||
@@ -345,26 +353,29 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading user profile:', error);
|
console.error('Error loading user profile:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async subscribeToUserProfileAndContacts(
|
||||||
|
pubKey: string
|
||||||
private async subscribeToUserProfileAndContacts(pubKey: string): Promise<void> {
|
): Promise<void> {
|
||||||
const combinedFilters: Filter[] = [
|
const combinedFilters: Filter[] = [
|
||||||
// Profile filter (kind 0)
|
// Profile filter (kind 0)
|
||||||
{ authors: [pubKey], kinds: [0], limit: 1 },
|
{ authors: [pubKey], kinds: [0], limit: 1 },
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
this.subscriptionId = this._subscriptionService.addSubscriptions(combinedFilters, async (event: NostrEvent) => {
|
this.subscriptionId = this._subscriptionService.addSubscriptions(
|
||||||
// Handle profile metadata
|
combinedFilters,
|
||||||
await this.processProfileMetadata(event, pubKey);
|
async (event: NostrEvent) => {
|
||||||
});
|
// Handle profile metadata
|
||||||
|
await this.processProfileMetadata(event, pubKey);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async processProfileMetadata(event: NostrEvent, pubKey: string): Promise<void> {
|
private async processProfileMetadata(
|
||||||
|
event: NostrEvent,
|
||||||
|
pubKey: string
|
||||||
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const newMetadata = JSON.parse(event.content);
|
const newMetadata = JSON.parse(event.content);
|
||||||
this.profileUser = newMetadata;
|
this.profileUser = newMetadata;
|
||||||
@@ -378,13 +389,10 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getSafeUrl(url: string): SafeUrl {
|
getSafeUrl(url: string): SafeUrl {
|
||||||
return this._sanitizer.bypassSecurityTrustUrl(url);
|
return this._sanitizer.bypassSecurityTrustUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private refreshUI(): void {
|
private refreshUI(): void {
|
||||||
this._changeDetectorRef.detectChanges();
|
this._changeDetectorRef.detectChanges();
|
||||||
}
|
}
|
||||||
@@ -394,23 +402,27 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async canUseZap(): Promise<boolean> {
|
async canUseZap(): Promise<boolean> {
|
||||||
const canReceiveZap = this.profileUser && (this.profileUser.lud06 || this.profileUser.lud16);
|
const canReceiveZap =
|
||||||
|
this.profileUser &&
|
||||||
|
(this.profileUser.lud06 || this.profileUser.lud16);
|
||||||
if (canReceiveZap) {
|
if (canReceiveZap) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
this.openSnackBar("Using Zap is not possible. Please complete your profile to include lud06 or lud16.");
|
this.openSnackBar(
|
||||||
|
'Using Zap is not possible. Please complete your profile to include lud06 or lud16.'
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async openZapDialog(eventId: string = ""): Promise<void> {
|
async openZapDialog(eventId: string = ''): Promise<void> {
|
||||||
const canZap = await this.canUseZap();
|
const canZap = await this.canUseZap();
|
||||||
if (canZap) {
|
if (canZap) {
|
||||||
const zapData: ZapDialogData = {
|
const zapData: ZapDialogData = {
|
||||||
lud16: this.profileUser.lud16,
|
lud16: this.profileUser.lud16,
|
||||||
lud06: this.profileUser.lud06,
|
lud06: this.profileUser.lud06,
|
||||||
pubkey: this.profileUser.pubkey,
|
pubkey: this.profileUser.pubkey,
|
||||||
eventId: eventId
|
eventId: eventId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Open dialog with mapped data
|
// Open dialog with mapped data
|
||||||
@@ -468,7 +480,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
this._eventService
|
this._eventService
|
||||||
.sendTextEvent(this.eventInput.nativeElement.value)
|
.sendTextEvent(this.eventInput.nativeElement.value)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.eventInput.nativeElement.value = "";
|
this.eventInput.nativeElement.value = '';
|
||||||
this._changeDetectorRef.markForCheck();
|
this._changeDetectorRef.markForCheck();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -483,7 +495,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
copyNpub() {
|
copyNpub() {
|
||||||
var npub = this._signerService.getNpubFromPubkey(this.routePubKey)
|
var npub = this._signerService.getNpubFromPubkey(this.routePubKey);
|
||||||
this._clipboard.copy(npub);
|
this._clipboard.copy(npub);
|
||||||
this.openSnackBar('npub public key copied', 'dismiss');
|
this.openSnackBar('npub public key copied', 'dismiss');
|
||||||
}
|
}
|
||||||
@@ -493,7 +505,9 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
this._clipboard.copy(this.routePubKey);
|
this._clipboard.copy(this.routePubKey);
|
||||||
this.openSnackBar('hex public key copied', 'dismiss');
|
this.openSnackBar('hex public key copied', 'dismiss');
|
||||||
} else if (keyType === 'npub') {
|
} else if (keyType === 'npub') {
|
||||||
const npub = this._signerService.getNpubFromPubkey(this.routePubKey);
|
const npub = this._signerService.getNpubFromPubkey(
|
||||||
|
this.routePubKey
|
||||||
|
);
|
||||||
this._clipboard.copy(npub);
|
this._clipboard.copy(npub);
|
||||||
this.openSnackBar('npub public key copied', 'dismiss');
|
this.openSnackBar('npub public key copied', 'dismiss');
|
||||||
}
|
}
|
||||||
@@ -513,7 +527,8 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async toggleBookmark(projectNpub: string): Promise<void> {
|
async toggleBookmark(projectNpub: string): Promise<void> {
|
||||||
const isBookmarked = await this._bookmarkService.isBookmarked(projectNpub);
|
const isBookmarked =
|
||||||
|
await this._bookmarkService.isBookmarked(projectNpub);
|
||||||
if (isBookmarked) {
|
if (isBookmarked) {
|
||||||
await this._bookmarkService.removeBookmark(projectNpub);
|
await this._bookmarkService.removeBookmark(projectNpub);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ export interface Project {
|
|||||||
projectIdentifier: string;
|
projectIdentifier: string;
|
||||||
nostrPubKey: string;
|
nostrPubKey: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
|
name?: string // TODO: Verify if this is actually a possible value?
|
||||||
|
totalInvestmentsCount?: number; // TODO: Verify if this is wrong type or not? There is a different project interface.
|
||||||
about?: string;
|
about?: string;
|
||||||
picture?: string;
|
picture?: string;
|
||||||
banner?: string;
|
banner?: string;
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
<button
|
<button
|
||||||
class="h-6 min-h-6 w-6 sm:opacity-0 sm:group-hover:opacity-100"
|
class="h-6 min-h-6 w-6 sm:opacity-0 sm:group-hover:opacity-100"
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
(click)="delete(notification)"
|
(click)="deleteNotification(notification)"
|
||||||
[matTooltip]="'Remove'"
|
[matTooltip]="'Remove'"
|
||||||
>
|
>
|
||||||
<mat-icon
|
<mat-icon
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import { Subject, takeUntil } from 'rxjs';
|
|||||||
NgTemplateOutlet,
|
NgTemplateOutlet,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
DatePipe,
|
DatePipe,
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export class NotificationsComponent implements OnInit, OnDestroy {
|
export class NotificationsComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild('notificationsOrigin') private _notificationsOrigin: MatButton;
|
@ViewChild('notificationsOrigin') private _notificationsOrigin: MatButton;
|
||||||
@@ -80,6 +80,10 @@ export class NotificationsComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteNotification(notification: NostrNotification) {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this._unsubscribeAll.next(null);
|
this._unsubscribeAll.next(null);
|
||||||
this._unsubscribeAll.complete();
|
this._unsubscribeAll.complete();
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ export class PostComponent implements OnDestroy {
|
|||||||
private subscription: Subscription = new Subscription();
|
private subscription: Subscription = new Subscription();
|
||||||
|
|
||||||
profileUser: any;
|
profileUser: any;
|
||||||
tokens = signal<(string | ParsedToken)[]>([]);
|
tokens = signal<(string | ParsedToken | any)[]>([]);
|
||||||
|
|
||||||
private isLiked = false;
|
isLiked = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export class ParseContentService {
|
|||||||
|
|
||||||
constructor(private utilities: Utilities) {}
|
constructor(private utilities: Utilities) {}
|
||||||
|
|
||||||
parseContent(text: string): (string | ParsedToken)[] {
|
parseContent(text: string): (string | ParsedToken | any)[] {
|
||||||
const sanitizedText = this.sanitizeText(text);
|
const sanitizedText = this.sanitizeText(text);
|
||||||
const tokens = this.tokenizeText(sanitizedText);
|
const tokens = this.tokenizeText(sanitizedText);
|
||||||
return this.combinePlainText(tokens.map(token => this.processToken(token)));
|
return this.combinePlainText(tokens.map(token => this.processToken(token)));
|
||||||
|
|||||||
@@ -26,6 +26,6 @@
|
|||||||
"enableI18nLegacyMessageIdFormat": false,
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
"strictInjectionParameters": false,
|
"strictInjectionParameters": false,
|
||||||
"strictInputAccessModifiers": false,
|
"strictInputAccessModifiers": false,
|
||||||
"strictTemplates": false
|
"strictTemplates": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user