Add widgets

This commit is contained in:
Milad Raeisi
2024-11-26 15:56:45 +04:00
parent fef26eff2c
commit 57f7a9a52b
42 changed files with 547 additions and 510 deletions

View File

@@ -16,23 +16,12 @@ import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTooltipModule } from '@angular/material/tooltip';
import { ActivatedRoute, Router } from '@angular/router';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
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 { 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';
import { PostComponent } from 'app/layout/common/post/post.component';
@Component({

View File

@@ -72,96 +72,29 @@
</div>
<!-- Separator -->
<div class="mx-8 hidden h-8 flex-shrink-0 border-l-2 lg:flex"></div>
<div class="flex flex-shrink-0 items-center space-x-6 lg:mt-0">
<div
*ngIf="isCurrentUserProfile; else otherUserStats"
class="mt-6 flex flex-shrink-0 items-center space-x-6 lg:mt-0"
>
<ng-container *ngIf="stats$ | async as stats">
<div class="flex flex-col items-center">
<span
[matTooltip]="
'Total contact: ' + stats.totalContacts
"
class="font-bold"
>
{{ stats.followersCount }}
</span>
<span class="text-secondary text-sm font-medium"
>FOLLOWERS</span
>
</div>
<div class="flex flex-shrink-0 items-center space-x-6 lg:mt-0">
<div class="flex flex-col items-center">
<span class="font-bold">{{
stats.followingCount
}}</span>
<span
[matTooltip]="
'Total contact: ' + stats.totalContacts
"
class="text-secondary text-sm font-medium"
>
FOLLOWING
</span>
</div>
</ng-container>
</div>
<ng-template #otherUserStats>
<div
class="mt-6 flex flex-shrink-0 items-center space-x-6 lg:mt-0"
>
<div class="flex flex-col items-center">
<span
[matTooltip]="'Total contact: ' + totalContacts"
class="font-bold"
>
{{ followersCount }}
</span>
<span class="text-secondary text-sm font-medium"
>FOLLOWERS</span
>
</div>
<div class="flex flex-col items-center">
<span class="font-bold">{{ followingCount }}</span>
<span
[matTooltip]="'Total contact: ' + totalContacts"
class="text-secondary text-sm font-medium"
>
FOLLOWING
</span>
</div>
</div>
</ng-template>
<!-- Separator -->
<div
class="mx-8 hidden h-8 flex-shrink-0 border-l-2 lg:flex"
></div>
<!-- Tools -->
<div
class="mb-4 mt-8 flex flex-shrink-0 items-center space-x-6 lg:m-0 lg:ml-auto"
>
<div *ngIf="!isCurrentUserProfile">
<button
mat-icon-button
(click)="toggleFollow()"
[matTooltip]="isFollowing ? 'Unfollow' : 'Follow'"
>
<mat-icon
[svgIcon]="
isFollowing
? 'heroicons_outline:user-minus'
: 'heroicons_outline:user-plus'
"
>
<div
class="flex h-10 w-10 items-center justify-center rounded-full border bg-white shadow-md">
<button mat-icon-button >
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon>
</button>
</div>
<div
class="flex h-10 w-10 items-center justify-center rounded-full border bg-white shadow-md">
<button mat-icon-button >
<mat-icon class="icon-size-5"
[svgIcon]="'heroicons_solid:bookmark'">
</mat-icon>
</button>
</div>
<div>
<button
mat-icon-button
@@ -508,7 +441,7 @@
eventInput.nativeElement.value
);
trackBy: trackByFn
"
class="inline-block whitespace-pre-wrap break-words"
>

View File

@@ -30,7 +30,6 @@ import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { PickerComponent } from '@ctrl/ngx-emoji-mart';
import { EventService } from 'app/services/event.service';
import { SignerService } from 'app/services/signer.service';
import { SocialService } from 'app/services/social.service';
import { ContactEvent, StorageService } from 'app/services/storage.service';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { Filter, NostrEvent } from 'nostr-tools';
@@ -141,7 +140,6 @@ export class ProfileComponent implements OnInit, OnDestroy {
private _sanitizer: DomSanitizer,
private _route: ActivatedRoute,
private _router: Router,
private _socialService: SocialService,
private _snackBar: MatSnackBar,
private _dialog: MatDialog,
private _angorConfigService: AngorConfigService,
@@ -170,6 +168,17 @@ export class ProfileComponent implements OnInit, OnDestroy {
});
}
private checkIfRoutePubKeyIsFollowing(): void {
if (!this.routePubKey || !this.followersList) {
this.isFollowing = false;
return;
}
this.isFollowing = this.followersList.some(follower => follower.pubkey === this.routePubKey);
}
private processRouteParams(): void {
this._route.paramMap.subscribe((params) => {
const routeKey = params.get('pubkey') || '';
@@ -198,8 +207,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
private loadUserProfileData(pubKey: string): void {
this.loadUserProfile(pubKey);
this.stats$ = this._storageService.getContactStats$(pubKey);
}
}
private isValidHexPubkey(pubkey: string): boolean {
const hexPattern = /^[a-fA-F0-9]{64}$/;
@@ -242,7 +250,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
this.loading = false;
}
this._changeDetectorRef.detectChanges();
this.refreshUI();
}
@@ -268,7 +276,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
if (newPost.pubkey === this.routePubKey) {
this.posts.unshift(newPost);
this.posts.sort((a, b) => b.created_at - a.created_at);
this._changeDetectorRef.detectChanges();
this.refreshUI();
}
}
});
@@ -312,12 +320,12 @@ export class ProfileComponent implements OnInit, OnDestroy {
this.errorMessage = null;
this.profileUser = null;
this._changeDetectorRef.detectChanges();
this.refreshUI();
if (!publicKey) {
this.errorMessage = 'No public key found. Please log in again.';
this.isLoading = false;
this._changeDetectorRef.detectChanges();
this.refreshUI();
return;
}
try {
@@ -325,7 +333,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
const cachedMetadata = await this._storageService.getProfile(publicKey);
if (cachedMetadata) {
this.profileUser = cachedMetadata;
this._changeDetectorRef.detectChanges();
this.refreshUI();
}
this.subscribeToUserProfileAndContacts(publicKey);
@@ -386,52 +394,27 @@ export class ProfileComponent implements OnInit, OnDestroy {
};
if (isFollower) {
// Add to followers list
this.followersList.push(contactEvent);
this.followersCount++;
this.totalContacts++;
this._changeDetectorRef.detectChanges();
} else {
// Add to following list
this.followingList.push(contactEvent);
this.followingCount++;
this.totalContacts++;
this._changeDetectorRef.detectChanges();
}
}
this.checkIfRoutePubKeyIsFollowing();
this.refreshUI();
}
getSafeUrl(url: string): SafeUrl {
return this._sanitizer.bypassSecurityTrustUrl(url);
}
async toggleFollow(): Promise<void> {
try {
const userPubKey = this._signerService.getPublicKey();
const routePubKey = this.routePubKey || this.currentUserPubKey;
if (!routePubKey || !userPubKey) {
console.error('Public key missing. Unable to toggle follow.');
return;
}
if (this.isFollowing) {
await this._socialService.unfollow(routePubKey);
} else {
await this._socialService.follow(routePubKey);
}
this.isFollowing = !this.isFollowing;
this._changeDetectorRef.detectChanges();
} catch (error) {
console.error('Failed to toggle follow:', error);
}
private refreshUI(): void {
this._changeDetectorRef.detectChanges();
}
openSnackBar(message: string, action: string = 'dismiss'): void {

View File

@@ -0,0 +1 @@
<p>call-to-action works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CallToActionComponent } from './call-to-action.component';
describe('CallToActionComponent', () => {
let component: CallToActionComponent;
let fixture: ComponentFixture<CallToActionComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CallToActionComponent]
})
.compileComponents();
fixture = TestBed.createComponent(CallToActionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-call-to-action',
imports: [],
templateUrl: './call-to-action.component.html',
styleUrl: './call-to-action.component.scss'
})
export class CallToActionComponent {
}

View File

@@ -0,0 +1 @@
<p>downloadbox works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DownloadboxComponent } from './downloadbox.component';
describe('DownloadboxComponent', () => {
let component: DownloadboxComponent;
let fixture: ComponentFixture<DownloadboxComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DownloadboxComponent]
})
.compileComponents();
fixture = TestBed.createComponent(DownloadboxComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-downloadbox',
imports: [],
templateUrl: './downloadbox.component.html',
styleUrl: './downloadbox.component.scss'
})
export class DownloadboxComponent {
}

View File

@@ -0,0 +1 @@
<p>image-gallery works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ImageGalleryComponent } from './image-gallery.component';
describe('ImageGalleryComponent', () => {
let component: ImageGalleryComponent;
let fixture: ComponentFixture<ImageGalleryComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ImageGalleryComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ImageGalleryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-image-gallery',
imports: [],
templateUrl: './image-gallery.component.html',
styleUrl: './image-gallery.component.scss'
})
export class ImageGalleryComponent {
}

View File

@@ -0,0 +1 @@
<p>investors-box works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InvestorsBoxComponent } from './investors-box.component';
describe('InvestorsBoxComponent', () => {
let component: InvestorsBoxComponent;
let fixture: ComponentFixture<InvestorsBoxComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [InvestorsBoxComponent]
})
.compileComponents();
fixture = TestBed.createComponent(InvestorsBoxComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-investors-box',
imports: [],
templateUrl: './investors-box.component.html',
styleUrl: './investors-box.component.scss'
})
export class InvestorsBoxComponent {
}

View File

@@ -0,0 +1 @@
<p>slide-show works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SlideShowComponent } from './slide-show.component';
describe('SlideShowComponent', () => {
let component: SlideShowComponent;
let fixture: ComponentFixture<SlideShowComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SlideShowComponent]
})
.compileComponents();
fixture = TestBed.createComponent(SlideShowComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-slide-show',
imports: [],
templateUrl: './slide-show.component.html',
styleUrl: './slide-show.component.scss'
})
export class SlideShowComponent {
}

View File

@@ -0,0 +1 @@
<p>social-media-links works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SocialMediaLinksComponent } from './social-media-links.component';
describe('SocialMediaLinksComponent', () => {
let component: SocialMediaLinksComponent;
let fixture: ComponentFixture<SocialMediaLinksComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SocialMediaLinksComponent]
})
.compileComponents();
fixture = TestBed.createComponent(SocialMediaLinksComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-social-media-links',
imports: [],
templateUrl: './social-media-links.component.html',
styleUrl: './social-media-links.component.scss'
})
export class SocialMediaLinksComponent {
}

View File

@@ -0,0 +1 @@
<p>team-members works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TeamMembersComponent } from './team-members.component';
describe('TeamMembersComponent', () => {
let component: TeamMembersComponent;
let fixture: ComponentFixture<TeamMembersComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TeamMembersComponent]
})
.compileComponents();
fixture = TestBed.createComponent(TeamMembersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-team-members',
imports: [],
templateUrl: './team-members.component.html',
styleUrl: './team-members.component.scss'
})
export class TeamMembersComponent {
}

View File

@@ -0,0 +1 @@
<p>useful-links works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UsefulLinksComponent } from './useful-links.component';
describe('UsefulLinksComponent', () => {
let component: UsefulLinksComponent;
let fixture: ComponentFixture<UsefulLinksComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UsefulLinksComponent]
})
.compileComponents();
fixture = TestBed.createComponent(UsefulLinksComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-useful-links',
imports: [],
templateUrl: './useful-links.component.html',
styleUrl: './useful-links.component.scss'
})
export class UsefulLinksComponent {
}

View File

@@ -0,0 +1 @@
<p>zap works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ZapComponent } from './zap.component';
describe('ZapComponent', () => {
let component: ZapComponent;
let fixture: ComponentFixture<ZapComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ZapComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ZapComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-zap',
imports: [],
templateUrl: './zap.component.html',
styleUrl: './zap.component.scss'
})
export class ZapComponent {
}

View File

@@ -1,183 +0,0 @@
import { Injectable } from '@angular/core';
import { Event, Filter, NostrEvent, UnsignedEvent } from 'nostr-tools';
import { Observable, Subject } from 'rxjs';
import { RelayService } from './relay.service';
import { SignerService } from './signer.service';
import { QueueService } from './queue-service.service';
@Injectable({
providedIn: 'root',
})
export class SocialService {
private followersSubject = new Subject<NostrEvent>();
private followingSubject = new Subject<NostrEvent>();
constructor(
private relayService: RelayService,
private signerService: SignerService,
private queueService: QueueService
) {}
getFollowersObservable(): Observable<NostrEvent> {
return this.followersSubject.asObservable();
}
getFollowingObservable(): Observable<NostrEvent> {
return this.followingSubject.asObservable();
}
// Fetch followers
async getFollowers(pubkey: string): Promise<any[]> {
const filters: Filter[] = [{ kinds: [3], '#p': [pubkey] }];
const followers: any[] = [];
return new Promise((resolve, reject) => {
const eventObservable = this.queueService.addRequestToQueue(filters);
eventObservable.subscribe({
next: (event: NostrEvent) => {
followers.push(event);
this.followersSubject.next(event);
},
error: (err) => {
console.error('Error fetching followers:', err);
reject(err);
},
complete: () => {
resolve(followers);
}
});
});
}
// Fetch who the user is following
async getFollowing(pubkey: string): Promise<any[]> {
const filters: Filter[] = [{ kinds: [3], authors: [pubkey] }];
const following: any[] = [];
return new Promise((resolve, reject) => {
const eventObservable = this.queueService.addRequestToQueue(filters);
eventObservable.subscribe({
next: (event: NostrEvent) => {
const tags = event.tags.filter((tag) => tag[0] === 'p');
tags.forEach((tag) => {
following.push(tag[1]);
this.followingSubject.next(event);
});
},
error: (err) => {
console.error('Error fetching following:', err);
reject(err);
},
complete: () => {
resolve(following);
}
});
});
}
// Follow a user
async follow(pubkeyToFollow: string): Promise<void> {
const currentFollowing = this.getFollowingList();
if (currentFollowing.includes(pubkeyToFollow)) {
console.log(`Already following ${pubkeyToFollow}`);
return;
}
// Add the user to the following list
const newFollowingList = [...currentFollowing, pubkeyToFollow];
this.setFollowingList(newFollowingList);
const unsignedEvent: UnsignedEvent = this.signerService.getUnsignedEvent(
3,
newFollowingList.map((f) => ['p', f]),
''
);
// Sign and publish the follow event
await this.publishSignedEvent(unsignedEvent);
console.log(`Now following ${pubkeyToFollow}`);
}
// Unfollow a user
async unfollow(pubkeyToUnfollow: string): Promise<void> {
const currentFollowing = this.getFollowingList();
if (!currentFollowing.includes(pubkeyToUnfollow)) {
console.log(`Not following ${pubkeyToUnfollow}`);
return;
}
// Remove the user from the following list
const updatedFollowingList = currentFollowing.filter(
(pubkey) => pubkey !== pubkeyToUnfollow
);
this.setFollowingList(updatedFollowingList);
const unsignedEvent: UnsignedEvent = this.signerService.getUnsignedEvent(
3,
updatedFollowingList.map((f) => ['p', f]),
''
);
// Sign and publish the unfollow event
await this.publishSignedEvent(unsignedEvent);
console.log(`Unfollowed ${pubkeyToUnfollow}`);
}
private async publishSignedEvent(unsignedEvent: UnsignedEvent): Promise<void> {
const isUsingExtension = await this.signerService.isUsingExtension();
let signedEvent: Event;
if (isUsingExtension) {
signedEvent = await this.signerService.signEventWithExtension(unsignedEvent);
} else {
const secretKey = await this.signerService.getDecryptedSecretKey();
if (!secretKey) {
throw new Error('Secret key is missing. Unable to sign event.');
}
signedEvent = this.signerService.getSignedEvent(unsignedEvent, secretKey);
}
this.relayService.publishEventToWriteRelays(signedEvent);
}
// Retrieve following list as tags for publishing follow/unfollow events
getFollowingListAsTags(): string[][] {
const following = this.getFollowingList();
const tags: string[][] = [];
const relays = this.relayService.getConnectedRelays();
following.forEach((f) => {
relays.forEach((relay) => {
tags.push(['p', f, relay, localStorage.getItem(`${f}`) || '']);
});
});
return tags;
}
setFollowingListFromTags(tags: string[][]): void {
const following: string[] = [];
tags.forEach((t) => {
following.push(t[1]);
});
this.setFollowingList(following);
}
setFollowingList(following: string[]): void {
const followingSet = Array.from(new Set(following));
const newFollowingList = followingSet.filter((s) => s).join(',');
localStorage.setItem('following', newFollowingList);
}
getFollowingList(): string[] {
const followingRaw = localStorage.getItem('following');
if (followingRaw === null || followingRaw === '') {
return [];
}
const following = followingRaw.split(',');
return following.filter((value) => /[a-f0-9]{64}/.test(value));
}
}

View File

@@ -22,7 +22,7 @@ export class StateService {
}
await this.subscribeToUserProfile(pubkey);
await this.subscribeToUserContacts(pubkey);
// await this.subscribeToUserContacts(pubkey);
await this.subscribeToUserChats(pubkey);
await this.subscribeToUserPosts(pubkey);
await this.subscribeToMyLikes(pubkey);
@@ -47,40 +47,40 @@ export class StateService {
});
}
private async subscribeToUserContacts(pubkey: string): Promise<void> {
// private async subscribeToUserContacts(pubkey: string): Promise<void> {
const contactsLastUpdate = await this.storageService.getLastUpdateDate('contacts');
// const contactsLastUpdate = await this.storageService.getLastUpdateDate('contacts');
const contactsFilter: Filter[] = [
{
kinds: [Contacts],
authors: [pubkey],
},
{
kinds: [Contacts],
'#p': [pubkey],
},
];
// const contactsFilter: Filter[] = [
// {
// kinds: [Contacts],
// authors: [pubkey],
// },
// {
// kinds: [Contacts],
// '#p': [pubkey],
// },
// ];
if (contactsLastUpdate) {
const lastUpdateTimestamp = parseInt(contactsLastUpdate, 10);
contactsFilter.forEach(filter => filter.since = lastUpdateTimestamp);
}
// if (contactsLastUpdate) {
// const lastUpdateTimestamp = parseInt(contactsLastUpdate, 10);
// contactsFilter.forEach(filter => filter.since = lastUpdateTimestamp);
// }
this.subscriptionService.addSubscriptions(contactsFilter, (event: NostrEvent) => {
const isFollower = event.pubkey === pubkey;
// this.subscriptionService.addSubscriptions(contactsFilter, (event: NostrEvent) => {
// const isFollower = event.pubkey === pubkey;
const contactEvent: ContactEvent = {
id: event.id,
pubkey: event.pubkey,
created_at: event.created_at,
tags: event.tags,
isFollower,
};
// const contactEvent: ContactEvent = {
// id: event.id,
// pubkey: event.pubkey,
// created_at: event.created_at,
// tags: event.tags,
// isFollower,
// };
this.storageService.saveContacts(pubkey, [contactEvent]);
});
}
// this.storageService.saveContacts(pubkey, [contactEvent]);
// });
// }
private async subscribeToUserChats(pubkey: string): Promise<void> {

View File

@@ -61,12 +61,12 @@ export class StorageService {
this.loadAllProjectsFromDB();
this.loadAllProjectStatsFromDB();
this.loadAllContactsFromDB();
//this.loadAllContactsFromDB();
this.loadAllChatEventsFromDB();
this.loadAllMyLikesFromDB();
this.loadAllNotificationsFromDB();
this.loadContactStatsFromDB();
this.calculateAndStoreAllContactStats();
//this.calculateAndStoreAllContactStats();
}
private createStore(storeName: string): LocalForage {
@@ -121,181 +121,181 @@ export class StorageService {
// --------------------- Contacts Methods ---------------------
async saveContacts(pubKey: string, contacts: ContactEvent[]): Promise<void> {
try {
const savedContacts: ContactEvent[] = [];
for (const contact of contacts) {
const key = `${pubKey}:${contact.id}`;
await this.contactsStore.setItem(key, contact);
savedContacts.push(contact);
}
this.contactsSubject.next({ pubKey, contacts: savedContacts });
// async saveContacts(pubKey: string, contacts: ContactEvent[]): Promise<void> {
// try {
// const savedContacts: ContactEvent[] = [];
// for (const contact of contacts) {
// const key = `${pubKey}:${contact.id}`;
// await this.contactsStore.setItem(key, contact);
// savedContacts.push(contact);
// }
// this.contactsSubject.next({ pubKey, contacts: savedContacts });
await this.calculateAndStoreAllContactStats();
// await this.calculateAndStoreAllContactStats();
await this.setUpdateHistory('contacts');
} catch (error) {
console.error('Error saving contacts:', error);
}
}
// await this.setUpdateHistory('contacts');
// } catch (error) {
// console.error('Error saving contacts:', error);
// }
// }
private contactStatsMap: { [pubKey: string]: BehaviorSubject<{ pubKey: string, totalContacts: number, followersCount: number, followingCount: number }> } = {};
// private contactStatsMap: { [pubKey: string]: BehaviorSubject<{ pubKey: string, totalContacts: number, followersCount: number, followingCount: number }> } = {};
private async calculateAndStoreAllContactStats(): Promise<void> {
try {
const statsMap: { [pubKey: string]: { totalContacts: number, followersCount: number, followingCount: number } } = {};
// private async calculateAndStoreAllContactStats(): Promise<void> {
// try {
// const statsMap: { [pubKey: string]: { totalContacts: number, followersCount: number, followingCount: number } } = {};
await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
const [storedPubKey] = key.split(':');
if (!statsMap[storedPubKey]) {
statsMap[storedPubKey] = { totalContacts: 0, followersCount: 0, followingCount: 0 };
}
statsMap[storedPubKey].totalContacts++;
if (contact.isFollower) {
statsMap[storedPubKey].followersCount++;
} else {
statsMap[storedPubKey].followingCount++;
}
});
// await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
// const [storedPubKey] = key.split(':');
// if (!statsMap[storedPubKey]) {
// statsMap[storedPubKey] = { totalContacts: 0, followersCount: 0, followingCount: 0 };
// }
// statsMap[storedPubKey].totalContacts++;
// if (contact.isFollower) {
// statsMap[storedPubKey].followersCount++;
// } else {
// statsMap[storedPubKey].followingCount++;
// }
// });
for (const pubKey in statsMap) {
if (!this.contactStatsMap[pubKey]) {
this.contactStatsMap[pubKey] = new BehaviorSubject<{ pubKey: string, totalContacts: number, followersCount: number, followingCount: number }>({
pubKey,
totalContacts: 0,
followersCount: 0,
followingCount: 0,
});
}
this.contactStatsMap[pubKey].next({
pubKey,
totalContacts: statsMap[pubKey].totalContacts,
followersCount: statsMap[pubKey].followersCount,
followingCount: statsMap[pubKey].followingCount,
});
}
} catch (error) {
console.error('Error calculating and storing contact stats:', error);
}
}
// for (const pubKey in statsMap) {
// if (!this.contactStatsMap[pubKey]) {
// this.contactStatsMap[pubKey] = new BehaviorSubject<{ pubKey: string, totalContacts: number, followersCount: number, followingCount: number }>({
// pubKey,
// totalContacts: 0,
// followersCount: 0,
// followingCount: 0,
// });
// }
// this.contactStatsMap[pubKey].next({
// pubKey,
// totalContacts: statsMap[pubKey].totalContacts,
// followersCount: statsMap[pubKey].followersCount,
// followingCount: statsMap[pubKey].followingCount,
// });
// }
// } catch (error) {
// console.error('Error calculating and storing contact stats:', error);
// }
// }
getContactStats$(pubKey: string): Observable<{ pubKey: string, totalContacts: number, followersCount: number, followingCount: number }> {
if (!this.contactStatsMap[pubKey]) {
this.contactStatsMap[pubKey] = new BehaviorSubject<{ pubKey: string, totalContacts: number, followersCount: number, followingCount: number }>({
pubKey,
totalContacts: 0,
followersCount: 0,
followingCount: 0,
});
// getContactStats$(pubKey: string): Observable<{ pubKey: string, totalContacts: number, followersCount: number, followingCount: number }> {
// if (!this.contactStatsMap[pubKey]) {
// this.contactStatsMap[pubKey] = new BehaviorSubject<{ pubKey: string, totalContacts: number, followersCount: number, followingCount: number }>({
// pubKey,
// totalContacts: 0,
// followersCount: 0,
// followingCount: 0,
// });
this.calculateAndStoreAllContactStats();
}
return this.contactStatsMap[pubKey].asObservable();
}
// this.calculateAndStoreAllContactStats();
// }
// return this.contactStatsMap[pubKey].asObservable();
// }
async getAllContactsPaginated(pubKey: string, page: number, pageSize: number): Promise<{ contacts: ContactEvent[], totalCount: number }> {
try {
const allContacts: ContactEvent[] = [];
await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
const [storedPubKey] = key.split(':');
if (storedPubKey === pubKey) {
allContacts.push(contact);
}
});
// async getAllContactsPaginated(pubKey: string, page: number, pageSize: number): Promise<{ contacts: ContactEvent[], totalCount: number }> {
// try {
// const allContacts: ContactEvent[] = [];
// await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
// const [storedPubKey] = key.split(':');
// if (storedPubKey === pubKey) {
// allContacts.push(contact);
// }
// });
const totalCount = allContacts.length;
const start = (page - 1) * pageSize;
const end = start + pageSize;
// const totalCount = allContacts.length;
// const start = (page - 1) * pageSize;
// const end = start + pageSize;
return {
contacts: allContacts.slice(start, end),
totalCount,
};
} catch (error) {
console.error('Error retrieving paginated contacts for pubKey:', error);
return { contacts: [], totalCount: 0 };
}
}
// return {
// contacts: allContacts.slice(start, end),
// totalCount,
// };
// } catch (error) {
// console.error('Error retrieving paginated contacts for pubKey:', error);
// return { contacts: [], totalCount: 0 };
// }
// }
async getAllContacts(pubKey: string = ""): Promise<{ pubKey: string, contact: ContactEvent }[]> {
try {
const allContacts: { pubKey: string, contact: ContactEvent }[] = [];
await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
const [storedPubKey, contactId] = key.split(':');
// async getAllContacts(pubKey: string = ""): Promise<{ pubKey: string, contact: ContactEvent }[]> {
// try {
// const allContacts: { pubKey: string, contact: ContactEvent }[] = [];
// await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
// const [storedPubKey, contactId] = key.split(':');
if (pubKey === "" || storedPubKey === pubKey) {
allContacts.push({ pubKey: storedPubKey, contact });
}
});
// if (pubKey === "" || storedPubKey === pubKey) {
// allContacts.push({ pubKey: storedPubKey, contact });
// }
// });
return allContacts;
} catch (error) {
console.error('Error retrieving contacts:', error);
return [];
}
}
// return allContacts;
// } catch (error) {
// console.error('Error retrieving contacts:', error);
// return [];
// }
// }
async getContactStats(pubKey: string): Promise<{ totalContacts: number, followersCount: number, followingCount: number }> {
try {
let totalContacts = 0;
let followersCount = 0;
let followingCount = 0;
// async getContactStats(pubKey: string): Promise<{ totalContacts: number, followersCount: number, followingCount: number }> {
// try {
// let totalContacts = 0;
// let followersCount = 0;
// let followingCount = 0;
await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
const [storedPubKey, contactId] = key.split(':');
// await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
// const [storedPubKey, contactId] = key.split(':');
if (storedPubKey === pubKey) {
totalContacts++;
// if (storedPubKey === pubKey) {
// totalContacts++;
if (contact.isFollower) {
followersCount++;
} else {
followingCount++;
}
}
});
// if (contact.isFollower) {
// followersCount++;
// } else {
// followingCount++;
// }
// }
// });
return {
totalContacts,
followersCount,
followingCount,
};
} catch (error) {
console.error('Error retrieving contact stats for pubKey:', error);
return { totalContacts: 0, followersCount: 0, followingCount: 0 };
}
}
// return {
// totalContacts,
// followersCount,
// followingCount,
// };
// } catch (error) {
// console.error('Error retrieving contact stats for pubKey:', error);
// return { totalContacts: 0, followersCount: 0, followingCount: 0 };
// }
// }
async removeAllContacts(pubKey: string): Promise<void> {
try {
const keysToRemove: string[] = [];
// async removeAllContacts(pubKey: string): Promise<void> {
// try {
// const keysToRemove: string[] = [];
await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
const [storedPubKey] = key.split(':');
if (storedPubKey === pubKey) {
keysToRemove.push(key);
}
});
// await this.contactsStore.iterate<ContactEvent, void>((contact, key) => {
// const [storedPubKey] = key.split(':');
// if (storedPubKey === pubKey) {
// keysToRemove.push(key);
// }
// });
for (const key of keysToRemove) {
await this.contactsStore.removeItem(key);
}
// for (const key of keysToRemove) {
// await this.contactsStore.removeItem(key);
// }
await this.contactsStore.clear();
this.contactStatsSubject.next({ totalContacts: 0, followersCount: 0, followingCount: 0 });
// await this.contactsStore.clear();
// this.contactStatsSubject.next({ totalContacts: 0, followersCount: 0, followingCount: 0 });
this.contactsSubject.next({ pubKey, contacts: [] });
await this.setUpdateHistory('contacts');
} catch (error) {
console.error('Error removing all contacts for pubKey:', error);
}
}
// this.contactsSubject.next({ pubKey, contacts: [] });
// await this.setUpdateHistory('contacts');
// } catch (error) {
// console.error('Error removing all contacts for pubKey:', error);
// }
// }
// --------------------- profiles Metadata Methods ---------------------
@@ -722,26 +722,26 @@ export class StorageService {
}
}
private async loadAllContactsFromDB(pubKey: string = ""): Promise<void> {
try {
const contacts = await this.getAllContacts(pubKey);
if (contacts.length > 0) {
const groupedContacts: { [pubKey: string]: ContactEvent[] } = {};
// private async loadAllContactsFromDB(pubKey: string = ""): Promise<void> {
// try {
// const contacts = await this.getAllContacts(pubKey);
// if (contacts.length > 0) {
// const groupedContacts: { [pubKey: string]: ContactEvent[] } = {};
for (const contact of contacts) {
if (!groupedContacts[contact.pubKey]) {
groupedContacts[contact.pubKey] = [];
}
groupedContacts[contact.pubKey].push(contact.contact);
}
for (const pubKey in groupedContacts) {
this.contactsSubject.next({ pubKey, contacts: groupedContacts[pubKey] });
}
}
} catch (error) {
console.error('Error loading contacts from DB:', error);
}
}
// for (const contact of contacts) {
// if (!groupedContacts[contact.pubKey]) {
// groupedContacts[contact.pubKey] = [];
// }
// groupedContacts[contact.pubKey].push(contact.contact);
// }
// for (const pubKey in groupedContacts) {
// this.contactsSubject.next({ pubKey, contacts: groupedContacts[pubKey] });
// }
// }
// } catch (error) {
// console.error('Error loading contacts from DB:', error);
// }
// }
private async loadAllChatEventsFromDB(): Promise<void> {
try {