mirror of
https://github.com/block-core/blockcore-notes.git
synced 2024-09-29 06:20:42 +00:00
Add support for editing articles
This commit is contained in:
parent
0afa9e973f
commit
e37ee0188f
@ -2,12 +2,20 @@
|
|||||||
<h1>Write your thoughts</h1>
|
<h1>Write your thoughts</h1>
|
||||||
|
|
||||||
<div class="note-type">
|
<div class="note-type">
|
||||||
<mat-button-toggle-group name="fontStyle" [(ngModel)]="eventType" aria-label="Font Style" #group="matButtonToggleGroup">
|
<mat-button-toggle-group name="fontStyle" (change)="noteTypeChanged()" [(ngModel)]="eventType" aria-label="Font Style" #group="matButtonToggleGroup">
|
||||||
<mat-button-toggle value="text">Note</mat-button-toggle>
|
<mat-button-toggle value="text">Note</mat-button-toggle>
|
||||||
<!-- <mat-button-toggle value="article">Article</mat-button-toggle> -->
|
<mat-button-toggle value="article">Article</mat-button-toggle>
|
||||||
</mat-button-toggle-group>
|
</mat-button-toggle-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field appearance="fill" *ngIf="group.value == 'article'">
|
||||||
|
<mat-label>Existing articles</mat-label>
|
||||||
|
<mat-select [(value)]="selectedArticle" (selectionChange)="changedArticle()">
|
||||||
|
<mat-option></mat-option>
|
||||||
|
<mat-option [value]="article.slug" *ngFor="let article of articleService.articles">{{ article.title }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
<form [formGroup]="articleForm" *ngIf="group.value == 'article'" (ngSubmit)="onSubmitArticle()">
|
<form [formGroup]="articleForm" *ngIf="group.value == 'article'" (ngSubmit)="onSubmitArticle()">
|
||||||
<div mat-dialog-content class="mat-dialog-content">
|
<div mat-dialog-content class="mat-dialog-content">
|
||||||
<mat-form-field appearance="outline" class="input-full-width">
|
<mat-form-field appearance="outline" class="input-full-width">
|
||||||
@ -40,6 +48,8 @@
|
|||||||
<input matInput #message placeholder="Tech, News, Social" formControlName="tags" />
|
<input matInput #message placeholder="Tech, News, Social" formControlName="tags" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<input type="hidden" formControlName="published_at" />
|
||||||
|
|
||||||
<emoji-mart class="picker" *ngIf="isEmojiPickerVisible" emoji="point_up" [isNative]="true" [showPreview]="false" (emojiSelect)="addEmojiArticle($event)" title="Choose your emoji"></emoji-mart>
|
<emoji-mart class="picker" *ngIf="isEmojiPickerVisible" emoji="point_up" [isNative]="true" [showPreview]="false" (emojiSelect)="addEmojiArticle($event)" title="Choose your emoji"></emoji-mart>
|
||||||
<mat-icon class="toolbar-icon margin-right" (click)="isEmojiPickerVisible = !isEmojiPickerVisible;" matTooltip="Insert emoji">sentiment_satisfied</mat-icon>
|
<mat-icon class="toolbar-icon margin-right" (click)="isEmojiPickerVisible = !isEmojiPickerVisible;" matTooltip="Insert emoji">sentiment_satisfied</mat-icon>
|
||||||
<!-- <mat-icon class="toolbar-icon margin-right" matTooltip="Upload file">attach_file</mat-icon> -->
|
<!-- <mat-icon class="toolbar-icon margin-right" matTooltip="Upload file">attach_file</mat-icon> -->
|
||||||
@ -55,7 +65,8 @@
|
|||||||
</mat-form-field> -->
|
</mat-form-field> -->
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions class="mat-dialog-actions" align="end">
|
<div mat-dialog-actions class="mat-dialog-actions" align="end">
|
||||||
<button mat-stroked-button type="button" (click)="onCancel()">Cancel</button> <button type="button" mat-stroked-button disabled="disabled">Save Draft</button>
|
<button mat-stroked-button type="button" (click)="onCancel()">Cancel</button>
|
||||||
|
<!-- <button type="button" mat-stroked-button disabled="disabled">Save Draft</button> -->
|
||||||
<button mat-flat-button [disabled]="!articleForm.valid" type="submit" color="primary">Publish Article</button>
|
<button mat-flat-button [disabled]="!articleForm.valid" type="submit" color="primary">Publish Article</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -82,7 +93,8 @@
|
|||||||
</mat-form-field> -->
|
</mat-form-field> -->
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions class="mat-dialog-actions" align="end">
|
<div mat-dialog-actions class="mat-dialog-actions" align="end">
|
||||||
<button mat-stroked-button type="button" (click)="onCancel()">Cancel</button> <button mat-stroked-button type="button" disabled="disabled">Save Draft</button>
|
<button mat-stroked-button type="button" (click)="onCancel()">Cancel</button>
|
||||||
|
<!-- <button mat-stroked-button type="button" disabled="disabled">Save Draft</button> -->
|
||||||
<button mat-flat-button [disabled]="!noteForm.valid" type="submit" color="primary">Publish Note</button>
|
<button mat-flat-button [disabled]="!noteForm.valid" type="submit" color="primary">Publish Note</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -7,6 +7,9 @@ import { BlogEvent } from '../services/interfaces';
|
|||||||
import { Event } from 'nostr-tools';
|
import { Event } from 'nostr-tools';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { Utilities } from '../services/utilities';
|
import { Utilities } from '../services/utilities';
|
||||||
|
import { QueueService } from '../services/queue.service';
|
||||||
|
import { ArticleService } from '../services/article';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
|
||||||
export interface NoteDialogData {
|
export interface NoteDialogData {
|
||||||
note: string;
|
note: string;
|
||||||
@ -37,6 +40,7 @@ export class EditorComponent {
|
|||||||
image: [''],
|
image: [''],
|
||||||
slug: [''],
|
slug: [''],
|
||||||
tags: [''],
|
tags: [''],
|
||||||
|
published_at: [''],
|
||||||
});
|
});
|
||||||
|
|
||||||
note: string = '';
|
note: string = '';
|
||||||
@ -55,7 +59,16 @@ export class EditorComponent {
|
|||||||
|
|
||||||
subscriptions: Subscription[] = [];
|
subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
constructor(private utilities: Utilities, private appState: ApplicationState, private location: Location, private fb: FormBuilder, public navigation: NavigationService) {}
|
constructor(
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
public articleService: ArticleService,
|
||||||
|
private queueService: QueueService,
|
||||||
|
private utilities: Utilities,
|
||||||
|
private appState: ApplicationState,
|
||||||
|
private location: Location,
|
||||||
|
private fb: FormBuilder,
|
||||||
|
public navigation: NavigationService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.appState.updateTitle(`Write a note`);
|
this.appState.updateTitle(`Write a note`);
|
||||||
@ -74,10 +87,51 @@ export class EditorComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectedArticle: string = '';
|
||||||
|
|
||||||
|
changedArticle() {
|
||||||
|
const article = this.articleService.get(this.selectedArticle!);
|
||||||
|
|
||||||
|
if (!article) {
|
||||||
|
this.articleForm.reset();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (article.summary == null) {
|
||||||
|
article.summary = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (article.image == null) {
|
||||||
|
article.image = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (article.title == null) {
|
||||||
|
article.title = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.articleForm.setValue({
|
||||||
|
content: article.content,
|
||||||
|
title: article.title,
|
||||||
|
summary: article.summary,
|
||||||
|
image: article.image,
|
||||||
|
slug: article.slug ? article.slug : '',
|
||||||
|
tags: article.metatags ? article.metatags.toString() : '',
|
||||||
|
published_at: article.published_at ? article.published_at.toString() : '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.utilities.unsubscribe(this.subscriptions);
|
this.utilities.unsubscribe(this.subscriptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noteTypeChanged() {
|
||||||
|
// Load all articles for the user when toggling.
|
||||||
|
if (this.eventType == 'article') {
|
||||||
|
this.queueService.enque(this.appState.getPublicKey(), 'Article');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
createSlug(input: string) {
|
createSlug(input: string) {
|
||||||
// convert input to lowercase
|
// convert input to lowercase
|
||||||
input = input.toLowerCase();
|
input = input.toLowerCase();
|
||||||
@ -111,20 +165,11 @@ export class EditorComponent {
|
|||||||
(<any>this.articleContent).nativeElement.focus();
|
(<any>this.articleContent).nativeElement.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
postBlog() {
|
|
||||||
// this.formGroupBlog.controls.
|
|
||||||
|
|
||||||
// this.profileForm.value
|
|
||||||
|
|
||||||
console.log('BLOG:', this.blog);
|
|
||||||
// this.navigation.saveNote(this.note);
|
|
||||||
}
|
|
||||||
|
|
||||||
formatSlug() {
|
formatSlug() {
|
||||||
this.articleForm.controls.slug.setValue(this.createSlug(this.articleForm.controls.slug.value!));
|
this.articleForm.controls.slug.setValue(this.createSlug(this.articleForm.controls.slug.value!));
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmitArticle() {
|
async onSubmitArticle() {
|
||||||
const controls = this.articleForm.controls;
|
const controls = this.articleForm.controls;
|
||||||
|
|
||||||
const blog: BlogEvent = {
|
const blog: BlogEvent = {
|
||||||
@ -136,7 +181,17 @@ export class EditorComponent {
|
|||||||
tags: controls.tags.value!,
|
tags: controls.tags.value!,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.navigation.saveArticle(blog);
|
if (controls.published_at.value) {
|
||||||
|
blog.published_at = Number(controls.published_at.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.navigation.saveArticle(blog);
|
||||||
|
|
||||||
|
this.snackBar.open(`Article was published. Notes does not support viewing articles yet.`, 'Hide', {
|
||||||
|
duration: 2000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'bottom',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmitNote() {
|
onSubmitNote() {
|
||||||
|
55
src/app/services/article.ts
Normal file
55
src/app/services/article.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ApplicationState } from './applicationstate';
|
||||||
|
import { EventService } from './event';
|
||||||
|
import { NostrArticle, NostrEvent } from './interfaces';
|
||||||
|
import { Utilities } from './utilities';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class ArticleService {
|
||||||
|
articles: NostrArticle[] = [];
|
||||||
|
|
||||||
|
constructor(private appState: ApplicationState, private utilities: Utilities, private eventService: EventService) {}
|
||||||
|
|
||||||
|
get(slug: string) {
|
||||||
|
return this.articles.find((a) => a.slug == slug);
|
||||||
|
}
|
||||||
|
|
||||||
|
put(event: NostrEvent) {
|
||||||
|
const article = event as NostrArticle;
|
||||||
|
article.slug = this.eventService.lastDTag(event);
|
||||||
|
article.title = this.eventService.lastTagOfType(event, 'title');
|
||||||
|
article.summary = this.eventService.lastTagOfType(event, 'summary');
|
||||||
|
article.image = this.eventService.lastTagOfType(event, 'image');
|
||||||
|
article.metatags = this.eventService.tagsOfTypeValues(event, 't');
|
||||||
|
article.metatags = this.eventService.tagsOfTypeValues(event, 't');
|
||||||
|
|
||||||
|
const publishedAt = this.eventService.lastTagOfType(event, 'published_at');
|
||||||
|
|
||||||
|
if (publishedAt) {
|
||||||
|
article.published_at = Number(publishedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (article.pubkey == this.appState.getPublicKey()) {
|
||||||
|
const index = this.articles.findIndex((a) => a.slug == article.slug);
|
||||||
|
|
||||||
|
if (index > -1) {
|
||||||
|
const existing = this.articles[index];
|
||||||
|
|
||||||
|
// If the existing is newer, ignore this article.
|
||||||
|
if (existing.created_at > article.created_at) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace when newer.
|
||||||
|
this.articles[index] = article;
|
||||||
|
} else {
|
||||||
|
this.articles.push(article);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: We currently don't support lookup of others articles, when the time comes, update this.
|
||||||
|
debugger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -67,6 +67,54 @@ export class EventService {
|
|||||||
return tags[tags.length - 1][1];
|
return tags[tags.length - 1][1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
titleTag(event: NostrEventDocument | null) {
|
||||||
|
const tags = this.tagsOfType(event, 'title');
|
||||||
|
|
||||||
|
if (tags.length == 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags[tags.length - 1][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
lastDTag(event: NostrEventDocument | null) {
|
||||||
|
const tags = this.tagsOfType(event, 'd');
|
||||||
|
|
||||||
|
if (tags.length == 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags[tags.length - 1][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTagOfType(event: NostrEventDocument | null, type: string) {
|
||||||
|
const tags = this.tagsOfType(event, type);
|
||||||
|
|
||||||
|
if (tags.length == 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags[tags.length - 1][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsOfType(event: NostrEventDocument | null, type: string) {
|
||||||
|
if (!event) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = event.tags.filter((t) => t[0] === type);
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsOfTypeValues(event: NostrEventDocument | null, type: string) {
|
||||||
|
if (!event) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = event.tags.filter((t) => t[0] === type);
|
||||||
|
return tags.map((t) => t[1]);
|
||||||
|
}
|
||||||
|
|
||||||
eTags(event: NostrEventDocument | null) {
|
eTags(event: NostrEventDocument | null) {
|
||||||
if (!event) {
|
if (!event) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -25,7 +25,7 @@ export interface Contact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryJob {
|
export interface QueryJob {
|
||||||
type: 'Profile' | 'Event' | 'Contacts';
|
type: 'Profile' | 'Event' | 'Contacts' | 'Article';
|
||||||
identifier: string;
|
identifier: string;
|
||||||
// callback?: any;
|
// callback?: any;
|
||||||
// limit?: number;
|
// limit?: number;
|
||||||
@ -102,6 +102,15 @@ export interface NostrEvent extends Event {
|
|||||||
tagsCut: boolean;
|
tagsCut: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NostrArticle extends NostrEvent {
|
||||||
|
slug?: string;
|
||||||
|
title?: string;
|
||||||
|
summary?: string;
|
||||||
|
image?: string;
|
||||||
|
published_at: number;
|
||||||
|
metatags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface NostrSub extends Sub {
|
export interface NostrSub extends Sub {
|
||||||
// id: string;
|
// id: string;
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,11 @@ export class NavigationService {
|
|||||||
event.tags.push(['image', blog.image]);
|
event.tags.push(['image', blog.image]);
|
||||||
}
|
}
|
||||||
|
|
||||||
event.tags.push(['published_at', event.created_at.toString()]);
|
if (!blog.published_at) {
|
||||||
|
event.tags.push(['published_at', event.created_at.toString()]);
|
||||||
|
} else {
|
||||||
|
event.tags.push(['published_at', blog.published_at.toString()]);
|
||||||
|
}
|
||||||
|
|
||||||
const tags = blog.tags.split(',').filter((t) => t);
|
const tags = blog.tags.split(',').filter((t) => t);
|
||||||
|
|
||||||
@ -144,7 +148,7 @@ export class NavigationService {
|
|||||||
|
|
||||||
await this.dataService.publishEvent(signedEvent);
|
await this.dataService.publishEvent(signedEvent);
|
||||||
|
|
||||||
this.router.navigate(['/a', signedEvent.id]);
|
// this.router.navigate(['/a', signedEvent.id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
createNote(): void {
|
createNote(): void {
|
||||||
|
@ -21,17 +21,23 @@ export class QueueService {
|
|||||||
this.#queuesChanged.next({ identifier: identifier, type: 'Event' });
|
this.#queuesChanged.next({ identifier: identifier, type: 'Event' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enqueArticle(identifier: string) {
|
||||||
|
this.#queuesChanged.next({ identifier: identifier, type: 'Article' });
|
||||||
|
}
|
||||||
|
|
||||||
enqueContacts(identifier: string) {
|
enqueContacts(identifier: string) {
|
||||||
this.#queuesChanged.next({ identifier: identifier, type: 'Contacts' });
|
this.#queuesChanged.next({ identifier: identifier, type: 'Contacts' });
|
||||||
}
|
}
|
||||||
|
|
||||||
enque(identifier: string, type: 'Profile' | 'Event' | 'Contacts') {
|
enque(identifier: string, type: 'Profile' | 'Event' | 'Contacts' | 'Article') {
|
||||||
if (type === 'Profile') {
|
if (type === 'Profile') {
|
||||||
this.enqueProfile(identifier);
|
this.enqueProfile(identifier);
|
||||||
} else if (type === 'Event') {
|
} else if (type === 'Event') {
|
||||||
this.enqueEvent(identifier);
|
this.enqueEvent(identifier);
|
||||||
} else if (type === 'Contacts') {
|
} else if (type === 'Contacts') {
|
||||||
this.enqueContacts(identifier);
|
this.enqueContacts(identifier);
|
||||||
|
} else if (type === 'Article') {
|
||||||
|
this.enqueArticle(identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,10 @@ export class Queue {
|
|||||||
active: false,
|
active: false,
|
||||||
jobs: [] as QueryJob[],
|
jobs: [] as QueryJob[],
|
||||||
},
|
},
|
||||||
|
article: {
|
||||||
|
active: false,
|
||||||
|
jobs: [] as QueryJob[],
|
||||||
|
},
|
||||||
contacts: {
|
contacts: {
|
||||||
active: false,
|
active: false,
|
||||||
jobs: [] as QueryJob[],
|
jobs: [] as QueryJob[],
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { LoadMoreOptions, NostrEventDocument, NostrRelay, NostrRelayDocument, NostrRelaySubscription, ProfileStatus, QueryJob } from './interfaces';
|
import { LoadMoreOptions, NostrArticle, NostrEventDocument, NostrRelay, NostrRelayDocument, NostrRelaySubscription, ProfileStatus, QueryJob } from './interfaces';
|
||||||
import { Observable, BehaviorSubject, from, merge, timeout, catchError, of, finalize, tap } from 'rxjs';
|
import { Observable, BehaviorSubject, from, merge, timeout, catchError, of, finalize, tap } from 'rxjs';
|
||||||
import { Filter, Kind, Relay, relayInit, Sub } from 'nostr-tools';
|
import { Filter, Kind, Relay, relayInit, Sub } from 'nostr-tools';
|
||||||
import { EventService } from './event';
|
import { EventService } from './event';
|
||||||
@ -16,6 +16,7 @@ import { ImportSheet } from '../shared/import-sheet/import-sheet';
|
|||||||
import { QueueService } from './queue.service';
|
import { QueueService } from './queue.service';
|
||||||
import { UIService } from './ui';
|
import { UIService } from './ui';
|
||||||
import { NostrService } from './nostr';
|
import { NostrService } from './nostr';
|
||||||
|
import { ArticleService } from './article';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -55,6 +56,7 @@ export class RelayService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private articleService: ArticleService,
|
||||||
private nostr: NostrService,
|
private nostr: NostrService,
|
||||||
private ui: UIService,
|
private ui: UIService,
|
||||||
private queue: QueueService,
|
private queue: QueueService,
|
||||||
@ -473,7 +475,9 @@ export class RelayService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.kind == Kind.Metadata) {
|
if (event.kind == Kind.Article) {
|
||||||
|
this.articleService.put(event);
|
||||||
|
} else if (event.kind == Kind.Metadata) {
|
||||||
// This is a profile event, store it.
|
// This is a profile event, store it.
|
||||||
const nostrProfileDocument = this.utilities.mapProfileEvent(event);
|
const nostrProfileDocument = this.utilities.mapProfileEvent(event);
|
||||||
|
|
||||||
|
@ -52,6 +52,8 @@ export class RelayWorker {
|
|||||||
this.queue.queues.contacts.jobs.push(job);
|
this.queue.queues.contacts.jobs.push(job);
|
||||||
} else if (job.type == 'Event') {
|
} else if (job.type == 'Event') {
|
||||||
this.queue.queues.event.jobs.push(job);
|
this.queue.queues.event.jobs.push(job);
|
||||||
|
} else if (job.type == 'Article') {
|
||||||
|
this.queue.queues.article.jobs.push(job);
|
||||||
} else {
|
} else {
|
||||||
throw Error(`This type of job (${job.type}) is currently not supported.`);
|
throw Error(`This type of job (${job.type}) is currently not supported.`);
|
||||||
}
|
}
|
||||||
@ -61,10 +63,11 @@ export class RelayWorker {
|
|||||||
// We always delay the processing in case we receive more.
|
// We always delay the processing in case we receive more.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.process();
|
this.process();
|
||||||
}, 150);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
process() {
|
process() {
|
||||||
|
this.processArticle();
|
||||||
this.processProfiles();
|
this.processProfiles();
|
||||||
this.processContacts();
|
this.processContacts();
|
||||||
this.processEvents();
|
this.processEvents();
|
||||||
@ -135,7 +138,7 @@ export class RelayWorker {
|
|||||||
|
|
||||||
processEvents() {
|
processEvents() {
|
||||||
if (!this.relay || this.relay.status != 1 || this.queue.queues.event.active) {
|
if (!this.relay || this.relay.status != 1 || this.queue.queues.event.active) {
|
||||||
console.log(`${this.url}: processProfiles: Relay not ready or currently active: ${this.queue.queues.event.active}.`, this.relay);
|
console.log(`${this.url}: processEvents: Relay not ready or currently active: ${this.queue.queues.event.active}.`, this.relay);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +162,32 @@ export class RelayWorker {
|
|||||||
this.downloadEvent(eventsToDownload, eventsToDownload.length * 3);
|
this.downloadEvent(eventsToDownload, eventsToDownload.length * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processArticle() {
|
||||||
|
if (!this.relay || this.relay.status != 1 || this.queue.queues.article.active) {
|
||||||
|
console.log(`${this.url}: processArticle: Relay not ready or currently active: ${this.queue.queues.article.active}.`, this.relay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`${this.url}: processArticle: Processing with downloading... Count: ` + this.queue.queues.article.jobs.length);
|
||||||
|
|
||||||
|
if (this.queue.queues.article.jobs.length == 0) {
|
||||||
|
this.queue.queues.article.active = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.queue.queues.article.active = true;
|
||||||
|
|
||||||
|
console.log(this.relay);
|
||||||
|
|
||||||
|
const eventsToDownload = this.queue.queues.article.jobs
|
||||||
|
.splice(0, 500)
|
||||||
|
.map((j) => j.identifier)
|
||||||
|
.filter((v, i, a) => a.indexOf(v) === i); // Unique, it can happen that multiple of same is added.
|
||||||
|
|
||||||
|
console.log('articleToDownload:', eventsToDownload);
|
||||||
|
this.downloadArticle(eventsToDownload, eventsToDownload.length * 3);
|
||||||
|
}
|
||||||
|
|
||||||
/** Provide event to publish and terminate immediately. */
|
/** Provide event to publish and terminate immediately. */
|
||||||
async connect(event?: any) {
|
async connect(event?: any) {
|
||||||
// const relay = relayInit('wss://relay.nostr.info');
|
// const relay = relayInit('wss://relay.nostr.info');
|
||||||
@ -250,6 +279,9 @@ export class RelayWorker {
|
|||||||
eventSub?: NostrSub;
|
eventSub?: NostrSub;
|
||||||
eventTimer?: any;
|
eventTimer?: any;
|
||||||
|
|
||||||
|
articleSub?: NostrSub;
|
||||||
|
articleTimer?: any;
|
||||||
|
|
||||||
clearProfileSub() {
|
clearProfileSub() {
|
||||||
this.profileSub?.unsub();
|
this.profileSub?.unsub();
|
||||||
this.profileSub = undefined;
|
this.profileSub = undefined;
|
||||||
@ -265,6 +297,11 @@ export class RelayWorker {
|
|||||||
this.eventSub = undefined;
|
this.eventSub = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearArticleSub() {
|
||||||
|
this.articleSub?.unsub();
|
||||||
|
this.articleTimer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
downloadProfile(pubkeys: string[], timeoutSeconds: number = 12) {
|
downloadProfile(pubkeys: string[], timeoutSeconds: number = 12) {
|
||||||
console.log('DOWNLOAD PROFILE....');
|
console.log('DOWNLOAD PROFILE....');
|
||||||
let finalizedCalled = false;
|
let finalizedCalled = false;
|
||||||
@ -387,6 +424,64 @@ export class RelayWorker {
|
|||||||
}, timeoutSeconds * 1000);
|
}, timeoutSeconds * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadArticle(ids: string[], timeoutSeconds: number = 12) {
|
||||||
|
console.log('DOWNLOAD ARTICLE....');
|
||||||
|
let finalizedCalled = false;
|
||||||
|
|
||||||
|
if (!this.relay) {
|
||||||
|
debugger;
|
||||||
|
console.warn('This relay does not have active connection and download cannot be executed at this time.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the profilesub already exists, unsub and remove.
|
||||||
|
if (this.articleSub) {
|
||||||
|
console.log('Article sub already existed, unsub before continue.');
|
||||||
|
this.clearArticleSub();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if the subscription is already added.
|
||||||
|
// if (this.subscriptions.findIndex((s) => s.id == id) > -1) {
|
||||||
|
// debugger;
|
||||||
|
// console.log('This subscription is already added!');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
const filter = { kinds: [Kind.Article], authors: ids };
|
||||||
|
const sub = this.relay.sub([filter]) as NostrSub;
|
||||||
|
this.articleSub = sub;
|
||||||
|
|
||||||
|
sub.on('event', (originalEvent: any) => {
|
||||||
|
console.log('POST MESSAGE BACK TO MAIN');
|
||||||
|
postMessage({ url: this.url, type: 'event', data: originalEvent } as RelayResponse);
|
||||||
|
console.log('FINISHED POST MESSAGE BACK TO MAIN');
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.on('eose', () => {
|
||||||
|
console.log('eose on event.');
|
||||||
|
clearTimeout(this.articleTimer);
|
||||||
|
this.clearArticleSub();
|
||||||
|
this.queue.queues.article.active = false;
|
||||||
|
this.processArticle();
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('REGISTER TIMEOUT!!', timeoutSeconds * 1000);
|
||||||
|
|
||||||
|
this.articleTimer = setTimeout(() => {
|
||||||
|
console.warn(`${this.url}: Event download timeout reached.`);
|
||||||
|
this.clearArticleSub();
|
||||||
|
this.queue.queues.article.active = false;
|
||||||
|
this.processArticle();
|
||||||
|
|
||||||
|
postMessage({ url: this.url, type: 'timeout', data: { type: 'Event', identifier: ids } } as RelayResponse);
|
||||||
|
|
||||||
|
// if (!finalizedCalled) {
|
||||||
|
// finalizedCalled = true;
|
||||||
|
// finalized();
|
||||||
|
// }
|
||||||
|
}, timeoutSeconds * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
downloadEvent(ids: string[], timeoutSeconds: number = 12) {
|
downloadEvent(ids: string[], timeoutSeconds: number = 12) {
|
||||||
console.log('DOWNLOAD EVENT....');
|
console.log('DOWNLOAD EVENT....');
|
||||||
let finalizedCalled = false;
|
let finalizedCalled = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user