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:
SondreB
2024-11-27 19:32:37 +01:00
parent 1f05b52fd7
commit 938a60b994
16 changed files with 119 additions and 95 deletions

View File

@@ -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;

View File

@@ -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: [],
}; };

View File

@@ -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;

View File

@@ -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"
/> />

View File

@@ -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]
}) })

View File

@@ -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) }}

View File

@@ -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 ||

View File

@@ -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[] = [];

View File

@@ -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;

View File

@@ -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 {
@@ -227,7 +232,8 @@ export class ProfileComponent implements OnInit, OnDestroy {
try { try {
while (attemptCount < maxAttempts) { while (attemptCount < maxAttempts) {
const additionalPosts = await this._storageService.getPostsByPubKeysWithPagination( const additionalPosts =
await this._storageService.getPostsByPubKeysWithPagination(
[this.routePubKey], [this.routePubKey],
this.currentPage, this.currentPage,
10 10
@@ -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 =
this._subscriptionService.addSubscriptions(
filters,
async (event: NostrEvent) => {
if (!this.isReply(event)) { if (!this.isReply(event)) {
this._storageService.savePost(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(
combinedFilters,
async (event: NostrEvent) => {
// Handle profile metadata // Handle profile metadata
await this.processProfileMetadata(event, pubKey); 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 {

View File

@@ -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;

View File

@@ -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

View File

@@ -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();

View File

@@ -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;

View File

@@ -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)));

View File

@@ -26,6 +26,6 @@
"enableI18nLegacyMessageIdFormat": false, "enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": false, "strictInjectionParameters": false,
"strictInputAccessModifiers": false, "strictInputAccessModifiers": false,
"strictTemplates": false "strictTemplates": true
} }
} }