mirror of
https://github.com/block-core/blockcore-notes.git
synced 2024-09-29 06:20:42 +00:00
Use multiple relays and improve loading of threads
This commit is contained in:
parent
fd277af778
commit
4af9f04e03
@ -53,6 +53,16 @@ const routes: Routes = [
|
||||
component: NoteComponent,
|
||||
canActivate: [AuthGuard],
|
||||
},
|
||||
{
|
||||
path: 'p/:id',
|
||||
component: UserComponent,
|
||||
canActivate: [AuthGuard],
|
||||
},
|
||||
{
|
||||
path: 'e/:id',
|
||||
component: NoteComponent,
|
||||
canActivate: [AuthGuard],
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
|
@ -1,25 +1,47 @@
|
||||
<!-- <mat-progress-bar *ngIf="!events || events.length == 0" mode="indeterminate"></mat-progress-bar> -->
|
||||
|
||||
<div class="feed-page">
|
||||
<div class="original-post events noclick" *ngIf="event">
|
||||
<div class="parent-events events" *ngIf="thread.root$ | async as event">
|
||||
<span *ngIf="event.kind != 7">
|
||||
<app-profile-actions [event]="event" [pubkey]="event.pubkey"></app-profile-actions>
|
||||
<app-profile-header [pubkey]="event.pubkey"
|
||||
><span class="event-date">{{ event.created_at | ago }}</span> <app-directory-icon [pubkey]="event.pubkey"></app-directory-icon
|
||||
></app-profile-header>
|
||||
|
||||
<div class="content">{{ event.content }}<span *ngIf="event.contentCut">... (message was truncated)</span></div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- <div class="parent-events events" *ngFor="let event of filteredThread(); trackBy: trackByFn">
|
||||
<span *ngIf="event.kind != 7">
|
||||
<app-profile-actions [event]="event" [pubkey]="event.pubkey"></app-profile-actions>
|
||||
<app-profile-header [pubkey]="event.pubkey"
|
||||
><span class="event-date">{{ event.created_at | ago }}</span> <app-directory-icon [pubkey]="event.pubkey"></app-directory-icon
|
||||
></app-profile-header>
|
||||
|
||||
<div class="content">{{ event.content }}<span *ngIf="event.contentCut">... (message was truncated)</span></div>
|
||||
</span>
|
||||
</div> -->
|
||||
|
||||
<div class="original-post events noclick" *ngIf="thread.event$ | async as event">
|
||||
<app-profile-actions [event]="event" [pubkey]="event.pubkey"></app-profile-actions>
|
||||
<app-profile-header [pubkey]="event.pubkey"
|
||||
><span class="event-date">{{ event.created_at | ago }}</span> <app-directory-icon [pubkey]="event.pubkey"></app-directory-icon
|
||||
></app-profile-header>
|
||||
<div class="content">
|
||||
{{ event.content }}<span *ngIf="event.contentCut">... (message was truncated)</span>
|
||||
<span class="dimmed">{{ rootLikes() }} likes {{ rootDislikes() }} dislikes {{ rootReplies() }} replies</span><br>
|
||||
<br><span class="dimmed">Replying to {{ repliesTo() }}</span>
|
||||
{{ event.content }}<span *ngIf="event.contentCut">... (message was truncated)</span><br />
|
||||
<span class="dimmed">{{ rootLikes() }} likes {{ rootDislikes() }} dislikes {{ rootReplies() }} replies</span><br />
|
||||
<br /><span class="dimmed">Replying to {{ repliesTo() }}</span> <br /><span class="dimmed">Root {{ rootEvent() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="page" *ngIf="feedService.thread.length === 0">
|
||||
<div class="page" *ngIf="!thread.events$">
|
||||
<h3 class="marginless">Loading thread... this can take some seconds.</h3>
|
||||
</div>
|
||||
|
||||
<div class="feed-page" *ngIf="feedService.thread.length > 0">
|
||||
<div class="feed-page" *ngIf="thread.events$ | async as events">
|
||||
<!-- <mat-accordion class="options">
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
@ -35,7 +57,7 @@
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion> -->
|
||||
|
||||
<div class="events" *ngFor="let event of filteredThread(); trackBy: trackByFn">
|
||||
<div class="events" *ngFor="let event of events; trackBy: trackByFn">
|
||||
<span *ngIf="event.kind != 7">
|
||||
<app-profile-actions [event]="event" [pubkey]="event.pubkey"></app-profile-actions>
|
||||
<app-profile-header [pubkey]="event.pubkey"
|
||||
|
@ -11,6 +11,7 @@ import { SettingsService } from '../services/settings.service';
|
||||
import { FeedService } from '../services/feed.service';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { OptionsService } from '../services/options.service';
|
||||
import { ThreadService } from '../services/thread.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-note',
|
||||
@ -59,6 +60,7 @@ export class NoteComponent {
|
||||
public options: OptionsService,
|
||||
public feedService: FeedService,
|
||||
public profiles: ProfileService,
|
||||
public thread: ThreadService,
|
||||
private validator: DataValidation,
|
||||
private utilities: Utilities,
|
||||
private router: Router
|
||||
@ -109,27 +111,64 @@ export class NoteComponent {
|
||||
return this.event.tags.filter((t) => t[0] === 'p').map((t) => t[1]);
|
||||
}
|
||||
|
||||
parentEvent?: NostrEventDocument;
|
||||
|
||||
/** Returns the root event, first looks for "root" attribute on the e tag element or picks first in array. */
|
||||
rootEvent() {
|
||||
if (!this.event) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO. All of this parsing of arrays is silly and could be greatly improved with some refactoring
|
||||
// whenever I have time for it.
|
||||
const eTags = this.event.tags.filter((t) => t[0] === 'e');
|
||||
|
||||
for (let i = 0; i < eTags.length; i++) {
|
||||
const tag = eTags[i];
|
||||
|
||||
// If more than 4, we likely have "root" or "reply"
|
||||
if (tag.length > 3) {
|
||||
if (tag[3] == 'root') {
|
||||
return tag[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return eTags[0][1];
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
console.log('NG INIT ON NOTE:');
|
||||
// this.appState.title = 'Blockcore Notes';
|
||||
this.appState.showBackButton = true;
|
||||
|
||||
// Subscribe to the event which will update whenever user requests to view a different event.
|
||||
this.feedService.event$.subscribe((event) => {
|
||||
console.log('EVENT CHANGED:', event);
|
||||
// this.feedService.event$(1).subscribe((event) => {
|
||||
// console.log('EVENT CHANGED:', event);
|
||||
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
// if (!event) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
this.event = event;
|
||||
// this.event = event;
|
||||
|
||||
// Clear the initial thread:
|
||||
this.feedService.thread = [];
|
||||
// // Query for root
|
||||
// // Query all child
|
||||
|
||||
// First download all posts, if any, that is mentioned in the e tags.
|
||||
this.feedService.downloadThread(this.event.id!);
|
||||
});
|
||||
// // Get the root event.
|
||||
// const rootEventId = this.rootEvent();
|
||||
|
||||
// if (rootEventId) {
|
||||
// // Start downloading the root event.
|
||||
// this.feedService.downloadEvent([rootEventId]);
|
||||
// }
|
||||
|
||||
// // Clear the initial thread:
|
||||
// this.feedService.thread = [];
|
||||
|
||||
// // First download all posts, if any, that is mentioned in the e tags.
|
||||
// this.feedService.downloadThread(this.event.id!);
|
||||
// });
|
||||
|
||||
this.activatedRoute.paramMap.subscribe(async (params) => {
|
||||
const id: any = params.get('id');
|
||||
@ -139,10 +178,11 @@ export class NoteComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('ROUTE ACTIVATE WITH ID:', id);
|
||||
this.feedService.setActiveEvent(id);
|
||||
|
||||
this.thread.changeSelectedEvent(id);
|
||||
this.id = id;
|
||||
|
||||
// console.log('ROUTE ACTIVATE WITH ID:', id);
|
||||
// this.feedService.setActiveEvent(id);
|
||||
// this.event = this.feedService.events.find((e) => e.id == this.id);
|
||||
|
||||
// if (!this.event) {
|
||||
|
@ -31,4 +31,32 @@ export class EventService {
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
/** Returns the root event, first looks for "root" attribute on the e tag element or picks first in array. */
|
||||
rootEventId(event: NostrEventDocument) {
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO. All of this parsing of arrays is silly and could be greatly improved with some refactoring
|
||||
// whenever I have time for it.
|
||||
const eTags = event.tags.filter((t) => t[0] === 'e');
|
||||
|
||||
for (let i = 0; i < eTags.length; i++) {
|
||||
const tag = eTags[i];
|
||||
|
||||
// If more than 4, we likely have "root" or "reply"
|
||||
if (tag.length > 3) {
|
||||
if (tag[3] == 'root') {
|
||||
return tag[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eTags.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return eTags[0][1];
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NostrEvent, NostrProfile, NostrEventDocument, NostrProfileDocument, Circle, Person, NostrSubscription } from './interfaces';
|
||||
import { NostrEvent, NostrProfile, NostrEventDocument, NostrProfileDocument, Circle, Person, NostrSubscription, NostrRelay } from './interfaces';
|
||||
import * as sanitizeHtml from 'sanitize-html';
|
||||
import { SettingsService } from './settings.service';
|
||||
import { Observable, of, BehaviorSubject, map, combineLatest, single } from 'rxjs';
|
||||
import { tap, delay, timer, takeUntil, timeout, Observable, of, BehaviorSubject, map, combineLatest, single, Subject, Observer, concat, concatMap, switchMap, catchError, race } from 'rxjs';
|
||||
import { Relay, relayInit, Sub } from 'nostr-tools';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { StorageService } from './storage.service';
|
||||
@ -243,38 +243,238 @@ export class FeedService {
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// async downloadEvent(ids: string[]) {
|
||||
// this.relayStorage.list();
|
||||
|
||||
// console.log('DOWNLOAD RECENT FOR:', ids);
|
||||
// const relay = this.relayService.relays[0];
|
||||
|
||||
// const backInTime = moment().subtract(12, 'hours').unix();
|
||||
|
||||
// // Start subscribing to our people feeds.
|
||||
// const sub = relay.sub([{ kinds: [1], since: backInTime, ids: ids }], {}) as NostrSubscription;
|
||||
|
||||
// sub.loading = true;
|
||||
|
||||
// // Keep all subscriptions around so we can close them when needed.
|
||||
// this.subs.push(sub);
|
||||
|
||||
// sub.on('event', (originalEvent: any) => {
|
||||
// const event = this.eventService.processEvent(originalEvent);
|
||||
|
||||
// if (!event) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// this.#persist(event);
|
||||
// });
|
||||
|
||||
// sub.on('eose', () => {
|
||||
// console.log('Initial load of people feed completed.');
|
||||
// sub.loading = false;
|
||||
// });
|
||||
// public subscribe<T>(key: string): Observable<EventBusMetaData> {
|
||||
// return this.eventBus.asObservable().pipe(
|
||||
// filter((event: IEventBusMessage) => this.keyMatch(event.key, key)),
|
||||
// map((event: IEventBusMessage) => event.metadata)
|
||||
// );
|
||||
// }
|
||||
|
||||
getEvent(id: string): Observable<NostrEventDocument> {
|
||||
const subject = new Subject<NostrEventDocument>();
|
||||
return subject.asObservable();
|
||||
}
|
||||
|
||||
// downloadFromRelayIndex(id: string, index: number, relayCount: number): Observable<NostrEventDocument> {
|
||||
downloadFromRelay(query: any, relay: NostrRelay): Observable<NostrEventDocument[]> {
|
||||
//const relay = this.relayService.relays[index];
|
||||
//console.log('downloadFromRelayIndex:', id, index, relayCount);
|
||||
|
||||
const observable = new Observable<NostrEventDocument[]>((observer: Observer<NostrEventDocument[]>) => {
|
||||
const totalEvents: NostrEventDocument[] = [];
|
||||
|
||||
console.log('111111111111111:');
|
||||
const sub = relay.sub([query], {}) as NostrSubscription;
|
||||
|
||||
sub.on('event', (originalEvent: any) => {
|
||||
// console.log('downloadFromRelayIndex: event', id);
|
||||
const event = this.eventService.processEvent(originalEvent);
|
||||
console.log('downloadFromRelayIndex: event', event);
|
||||
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
totalEvents.unshift(event);
|
||||
observer.next(totalEvents);
|
||||
// sub.unsub();
|
||||
});
|
||||
|
||||
sub.on('eose', () => {
|
||||
// console.log('downloadFromRelayIndex: eose', id);
|
||||
observer.complete();
|
||||
sub.unsub();
|
||||
});
|
||||
});
|
||||
// .pipe(race(observable, this.downloadFromRelayIndex(id, index, relayCount)))
|
||||
// // .pipe(takeUntil(timer(5)), tap(() => { console.log('TAKE UNTIL TAP!') }))
|
||||
// .pipe(
|
||||
// // tap(() => { console.log('CONTINUED HAPPENED, SWITCH MAP AND DOWNLOAD AGAIN!') }),
|
||||
// // timeout(5000),
|
||||
// // catchError(error => of("Error while request"))
|
||||
// // takeUntil(myTimer),
|
||||
// switchMap((data) => {
|
||||
// ++index;
|
||||
// console.log('DATA FROM SWITCH MAP:', data);
|
||||
// return data ? of(data) : this.downloadFromRelayIndex(id, index, relayCount);
|
||||
// })
|
||||
// );
|
||||
|
||||
// .pipe(concatMap(event => {
|
||||
// if (event == null) {
|
||||
// ++index;
|
||||
|
||||
// if (index < relayCount) {
|
||||
// return this.downloadFromRelayIndex(id, index, relayCount));
|
||||
// // return ;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
return observable;
|
||||
}
|
||||
|
||||
// downloadFromRelayIndex(id: string, index: number, relayCount: number): Observable<NostrEventDocument> {
|
||||
downloadSingleFromRelay(query: any, relay: NostrRelay): Observable<NostrEventDocument> {
|
||||
//const relay = this.relayService.relays[index];
|
||||
//console.log('downloadFromRelayIndex:', id, index, relayCount);
|
||||
|
||||
console.log('WITH DELAY:');
|
||||
|
||||
const observable = new Observable<NostrEventDocument>((observer: Observer<NostrEventDocument>) => {
|
||||
console.log('111111111111111:');
|
||||
const sub = relay.sub([query], {}) as NostrSubscription;
|
||||
|
||||
sub.on('event', (originalEvent: any) => {
|
||||
// console.log('downloadFromRelayIndex: event', id);
|
||||
const event = this.eventService.processEvent(originalEvent);
|
||||
console.log('downloadFromRelayIndex: event', event);
|
||||
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
observer.next(event);
|
||||
observer.complete();
|
||||
// sub.unsub();
|
||||
});
|
||||
|
||||
sub.on('eose', () => {
|
||||
// console.log('downloadFromRelayIndex: eose', id);
|
||||
// observer.complete();
|
||||
sub.unsub();
|
||||
});
|
||||
});
|
||||
// .pipe(race(observable, this.downloadFromRelayIndex(id, index, relayCount)))
|
||||
// // .pipe(takeUntil(timer(5)), tap(() => { console.log('TAKE UNTIL TAP!') }))
|
||||
// .pipe(
|
||||
// // tap(() => { console.log('CONTINUED HAPPENED, SWITCH MAP AND DOWNLOAD AGAIN!') }),
|
||||
// // timeout(5000),
|
||||
// // catchError(error => of("Error while request"))
|
||||
// // takeUntil(myTimer),
|
||||
// switchMap((data) => {
|
||||
// ++index;
|
||||
// console.log('DATA FROM SWITCH MAP:', data);
|
||||
// return data ? of(data) : this.downloadFromRelayIndex(id, index, relayCount);
|
||||
// })
|
||||
// );
|
||||
|
||||
// .pipe(concatMap(event => {
|
||||
// if (event == null) {
|
||||
// ++index;
|
||||
|
||||
// if (index < relayCount) {
|
||||
// return this.downloadFromRelayIndex(id, index, relayCount));
|
||||
// // return ;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
return observable;
|
||||
}
|
||||
|
||||
downloadThread(id: string) {
|
||||
// TODO: Change the logic to create a new observable for each X milisecond and make them
|
||||
// race against each other and don't create more observables if a result is found.
|
||||
const observables = this.relayService.relays.map((r, index) => this.downloadFromRelay({ ['#e']: [id] }, r));
|
||||
console.log('OBSERVABLES:', observables);
|
||||
return race(observables);
|
||||
}
|
||||
|
||||
downloadEvent(id: string) {
|
||||
// TODO: Change the logic to create a new observable for each X milisecond and make them
|
||||
// race against each other and don't create more observables if a result is found.
|
||||
const observables = this.relayService.relays.map((r, index) => this.downloadSingleFromRelay({ kinds: [1], ids: [id] }, r));
|
||||
console.log('OBSERVABLES:', observables);
|
||||
return race(observables);
|
||||
|
||||
// const observable = new Observable<NostrEventDocument>((observer: Observer<NostrEventDocument>) => {
|
||||
// const observables = this.relayService.relays.map((r => { this.downloadFromRelayIndex(id, r); }));
|
||||
// race(observables);
|
||||
// // for (let i = 0; i < this.relayService.relays.length; i++) {
|
||||
// // const relay = this.relayService.relays[i];
|
||||
|
||||
// // if (relay.status != 1) {
|
||||
// // continue;
|
||||
// // }
|
||||
|
||||
// // this.downloadFromRelay(id, relay);
|
||||
// // }
|
||||
|
||||
// // this.downloadFromRelayIndex(id, 0, this.relayService.relays.length).subscribe((event) => {
|
||||
// // console.log('FINIHED DOWNLOAD OBSERSVABLE:', event);
|
||||
// // observer.next(event);
|
||||
// // });
|
||||
|
||||
// // this.downloadFromRelayIndex(id, 0);
|
||||
|
||||
// // const relayIndex = 0;
|
||||
|
||||
// // const relay = this.relayService.relays[0];
|
||||
|
||||
// // this.downloadFromRelay(id, relay).subscribe((event) => {
|
||||
// // console.log('COMPLETED FIRST DOWNLAOD FROM RELAY:', event);
|
||||
// // });
|
||||
|
||||
// // for (let i = 0; i < this.relayService.relays.length; i++) {
|
||||
// // const relay = this.relayService.relays[i];
|
||||
|
||||
// // if (relay.status != 1) {
|
||||
// // continue;
|
||||
// // }
|
||||
|
||||
// // this.downloadFromRelay(id, relay);
|
||||
// // }
|
||||
|
||||
// // console.log('DOWNLOAD EVENT:', id);
|
||||
// // const relay = this.relayService.relays[0];
|
||||
|
||||
// // // Start subscribing to our people feeds.
|
||||
// // const sub = relay.sub([{ kinds: [1], ids: [id] }], {}) as NostrSubscription;
|
||||
|
||||
// // // Keep all subscriptions around so we can close them when needed.
|
||||
// // // this.subs.push(sub);
|
||||
|
||||
// // sub.on('event', (originalEvent: any) => {
|
||||
// // const event = this.eventService.processEvent(originalEvent);
|
||||
|
||||
// // if (!event) {
|
||||
// // return;
|
||||
// // }
|
||||
|
||||
// // downloadedEvent = event;
|
||||
// // observer.next(event);
|
||||
// // // this.#persist(event);
|
||||
// // });
|
||||
|
||||
// // sub.on('eose', () => {
|
||||
// // console.log('Initial load of people feed completed.');
|
||||
// // // const subIndex = this.subs.findIndex(sub);
|
||||
// // if (downloadedEvent) {
|
||||
// // observer.complete();
|
||||
// // sub.unsub();
|
||||
// // } else {
|
||||
// // // Go to next relay and try there.
|
||||
// // // First let us unsub the current subscription.
|
||||
// // sub.unsub();
|
||||
// // }
|
||||
// // });
|
||||
// });
|
||||
|
||||
// return observable;
|
||||
|
||||
// get rootEvents$(): Observable<NostrEventDocument[]> {
|
||||
// return this.events$.pipe(
|
||||
// map((data) => {
|
||||
// const filtered = data.filter((events) => !events.tags.find((t) => t[0] === 'e'));
|
||||
// return filtered;
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
|
||||
// this.relayStorage.list();
|
||||
}
|
||||
|
||||
async downloadRecent(pubkeys: string[]) {
|
||||
console.log('DOWNLOAD RECENT FOR:', pubkeys);
|
||||
const relay = this.relayService.relays[0];
|
||||
@ -311,8 +511,7 @@ export class FeedService {
|
||||
|
||||
// threadQueue: string[];
|
||||
|
||||
downloadThread(id: string) {
|
||||
|
||||
downloadThread2(id: string) {
|
||||
// First get existing data.
|
||||
// this.#filter((e) => { e.tags. });
|
||||
|
||||
|
@ -20,13 +20,14 @@ import { RelayStorageService } from './relay.storage.service';
|
||||
export class RelayService {
|
||||
/** Default relays that the app has for users without extension. This follows the document structure as extension data. */
|
||||
defaultRelays: any = {
|
||||
'wss://relay.nostr.info': { read: true, write: true },
|
||||
'wss://nostr-pub.wellorder.net': { read: true, write: true },
|
||||
'wss://nostr.nordlysln.net': { read: true, write: true },
|
||||
// 'wss://nostr-verified.wellorder.net': { read: false, write: true },
|
||||
// 'wss://nostr.bitcoiner.social': { read: true, write: true },
|
||||
// 'wss://nostr.drss.io': { read: true, write: true },
|
||||
'wss://relay.damus.io': { read: true, write: false },
|
||||
'wss://relay.nostr.info': { read: true, write: true },
|
||||
// 'wss://relay.nostr.info': { read: true, write: true },
|
||||
// 'wss://relay.minds.com/nostr/v1/ws': { read: false, write: true },
|
||||
'wss://relay.nostr.ch': { read: true, write: true },
|
||||
};
|
||||
|
161
src/app/services/thread.service.ts
Normal file
161
src/app/services/thread.service.ts
Normal file
@ -0,0 +1,161 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NostrEvent, NostrProfile, NostrEventDocument, NostrProfileDocument, Circle, Person, NostrSubscription } from './interfaces';
|
||||
import * as sanitizeHtml from 'sanitize-html';
|
||||
import { SettingsService } from './settings.service';
|
||||
import { Observable, of, BehaviorSubject, map, combineLatest, single, ReplaySubject, mergeMap } from 'rxjs';
|
||||
import { Relay, relayInit, Sub } from 'nostr-tools';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { StorageService } from './storage.service';
|
||||
import { ProfileService } from './profile.service';
|
||||
import { CirclesService } from './circles.service';
|
||||
import * as moment from 'moment';
|
||||
import { EventService } from './event.service';
|
||||
import { DataValidation } from './data-validation.service';
|
||||
import { OptionsService } from './options.service';
|
||||
import { RelayService } from './relay.service';
|
||||
import { RelayStorageService } from './relay.storage.service';
|
||||
import { FeedService } from './feed.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ThreadService {
|
||||
#event: NostrEventDocument | null = null;
|
||||
#eventChanged: BehaviorSubject<NostrEventDocument | null> = new BehaviorSubject<NostrEventDocument | null>(this.#event);
|
||||
event$ = this.#eventChanged.asObservable();
|
||||
|
||||
#root: NostrEventDocument | null = null;
|
||||
#rootChanged: BehaviorSubject<NostrEventDocument | null> = new BehaviorSubject<NostrEventDocument | null>(this.#root);
|
||||
root$ = this.#rootChanged.asObservable();
|
||||
|
||||
#events: NostrEventDocument[] = [];
|
||||
#eventsChanged: BehaviorSubject<NostrEventDocument[]> = new BehaviorSubject<NostrEventDocument[]>(this.#events);
|
||||
|
||||
// #beforeChanged: BehaviorSubject<NostrEventDocument[]> = new BehaviorSubject<NostrEventDocument[]>(this.#events);
|
||||
// before$ = this.#beforeChanged.asObservable();
|
||||
|
||||
// #afterChanged: BehaviorSubject<NostrEventDocument[]> = new BehaviorSubject<NostrEventDocument[]>(this.#events);
|
||||
// after$ = this.#afterChanged.asObservable();
|
||||
|
||||
get before$(): Observable<NostrEventDocument[]> {
|
||||
return this.#eventsChanged
|
||||
.asObservable()
|
||||
.pipe(
|
||||
map((data) => {
|
||||
data.sort((a, b) => {
|
||||
return a.created_at > b.created_at ? -1 : 1;
|
||||
});
|
||||
|
||||
return data;
|
||||
})
|
||||
)
|
||||
.pipe(map((data) => data.filter((events) => !this.profileService.blockedPublickKeys().includes(events.pubkey))));
|
||||
}
|
||||
|
||||
get after$(): Observable<NostrEventDocument[]> {
|
||||
return this.#eventsChanged
|
||||
.asObservable()
|
||||
.pipe(
|
||||
map((data) => {
|
||||
data.sort((a, b) => {
|
||||
return a.created_at > b.created_at ? -1 : 1;
|
||||
});
|
||||
|
||||
return data;
|
||||
})
|
||||
)
|
||||
.pipe(map((data) => data.filter((events) => !this.profileService.blockedPublickKeys().includes(events.pubkey))));
|
||||
}
|
||||
|
||||
get events$(): Observable<NostrEventDocument[]> {
|
||||
return this.#eventsChanged
|
||||
.asObservable()
|
||||
.pipe(
|
||||
map((data) => {
|
||||
data.sort((a, b) => {
|
||||
return a.created_at > b.created_at ? -1 : 1;
|
||||
});
|
||||
|
||||
return data;
|
||||
})
|
||||
)
|
||||
.pipe(map((data) => data.filter((events) => !this.profileService.blockedPublickKeys().includes(events.pubkey))));
|
||||
}
|
||||
|
||||
get rootEvents$(): Observable<NostrEventDocument[]> {
|
||||
return this.events$.pipe(
|
||||
map((data) => {
|
||||
const filtered = data.filter((events) => !events.tags.find((t) => t[0] === 'e'));
|
||||
return filtered;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#selectedEventSource = new ReplaySubject<string>();
|
||||
selectedEventChanges$ = this.#selectedEventSource.asObservable();
|
||||
|
||||
#eventSource = new ReplaySubject<NostrEventDocument>();
|
||||
eventChanges$ = this.#eventSource.asObservable();
|
||||
|
||||
// selectedEvent$ = combineLatest(this.selectedEventChanges$, this.eventChanges$).pipe(mergeMap());
|
||||
|
||||
constructor(private storage: StorageService, private eventService: EventService, private profileService: ProfileService, private feedService: FeedService) {
|
||||
// Whenever the event has changed, we can go grab the parent and the thread itself
|
||||
this.#eventChanged.subscribe((event) => {
|
||||
if (event == null) {
|
||||
console.log('EVENT WAS NULL!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('EVENT CHANGED!!!', event);
|
||||
|
||||
// Get the root event.
|
||||
const rootEventId = this.eventService.rootEventId(event);
|
||||
|
||||
console.log('ROOT EVENT ID:', rootEventId);
|
||||
|
||||
if (rootEventId) {
|
||||
this.feedService.downloadEvent(rootEventId).subscribe((event) => {
|
||||
console.log(event);
|
||||
this.#root = event;
|
||||
this.#rootChanged.next(this.#root);
|
||||
console.log('GOT ROOT EVENT', this.#root);
|
||||
});
|
||||
|
||||
// Get all events in the thread.
|
||||
this.feedService.downloadThread(rootEventId).subscribe((events) => {
|
||||
console.log(events);
|
||||
this.#events = events;
|
||||
this.#eventsChanged.next(this.#events);
|
||||
console.log('GOT ROOT EVENT', this.#root);
|
||||
});
|
||||
} else {
|
||||
this.#root = null;
|
||||
// Reset the root if the current event does not have one.
|
||||
this.#rootChanged.next(this.#root);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async changeSelectedEvent(eventId: string) {
|
||||
// Get the event itself.
|
||||
const event = await this.storage.get<NostrEventDocument>(eventId);
|
||||
|
||||
if (event) {
|
||||
this.#event = event;
|
||||
this.#eventChanged.next(this.#event);
|
||||
} else {
|
||||
// Go grab it from relays.
|
||||
this.feedService.downloadEvent(eventId).subscribe((event) => {
|
||||
console.log(event);
|
||||
this.#eventChanged.next(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Fetches the whole thread of data to be rendered in the UI. */
|
||||
async fetchThread(id: string) {
|
||||
// Second get the original post to render ontop of the page and all of the other events in e tags.
|
||||
// Third get the replies to the id supplied.
|
||||
}
|
||||
}
|
@ -205,6 +205,11 @@ mat-sidenav-content mat-toolbar {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.parent-events {
|
||||
margin-left: 3em;
|
||||
background-color: #a30770 !important;
|
||||
}
|
||||
|
||||
.events {
|
||||
margin-top: 1em;
|
||||
padding: 1em;
|
||||
|
Loading…
Reference in New Issue
Block a user