mirror of
https://github.com/block-core/blockcore-notes.git
synced 2024-09-28 22:10:42 +00:00
Add connection to relay and display live stream of 100 latest events
This commit is contained in:
parent
2ff68bb702
commit
eb37e154e6
@ -55,3 +55,11 @@ Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To u
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
|
||||
## Notes
|
||||
|
||||
Thoughts and ideas:
|
||||
|
||||
- Validate the content of certain limit and don't render at all if content is too long, or at least cut the content and only render X length. Then allow users to manually retrieve
|
||||
that exact event upon request.
|
||||
|
||||
|
30
angular.json
30
angular.json
@ -20,20 +20,11 @@
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"polyfills": ["zone.js", "src/polyfills.ts"],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/404.html",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"assets": ["src/favicon.ico", "src/404.html", "src/assets", "src/manifest.webmanifest"],
|
||||
"styles": ["src/styles.scss"],
|
||||
"scripts": [],
|
||||
"serviceWorker": true,
|
||||
"ngswConfigPath": "ngsw-config.json"
|
||||
@ -86,20 +77,11 @@
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"polyfills": ["zone.js", "zone.js/testing"],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
|
||||
"styles": ["src/styles.scss"],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
|
1498
package-lock.json
generated
1498
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,13 @@
|
||||
"@fontsource/material-icons": "^4.5.4",
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"@scure/base": "^1.1.1",
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"moment": "^2.29.4",
|
||||
"nostr-tools": "^1.0.0-beta",
|
||||
"rxjs": "~7.5.0",
|
||||
"sanitize-html": "^2.8.1",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
@ -36,6 +42,7 @@
|
||||
"@angular/compiler-cli": "^15.0.0",
|
||||
"@blockcore/tsconfig": "0.0.1",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/sanitize-html": "^2.8.0",
|
||||
"jasmine-core": "~4.5.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
|
@ -37,9 +37,10 @@ import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { LayoutModule } from '@angular/cdk/layout';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { AgoPipe } from './shared/ago.pipe';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, ConnectComponent, LogoutComponent, HomeComponent],
|
||||
declarations: [AppComponent, ConnectComponent, LogoutComponent, HomeComponent, AgoPipe],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
|
@ -1 +1,11 @@
|
||||
Notes!
|
||||
<mat-list>
|
||||
<div mat-subheader>Latest 100 Events</div>
|
||||
<mat-list-item *ngFor="let event of events; trackBy: trackByFn">
|
||||
<mat-icon matListItemIcon>text_snippet</mat-icon>
|
||||
<!-- <div matListItemTitle>{{ event.pubkey }} - {{ event.kind }}-{{ event.id }}</div> -->
|
||||
<div matListItemTitle>{{ event.content }}</div>
|
||||
<div matListItemLine>{{ event.created_at | ago }}</div>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
|
||||
<!-- <div *ngFor="let event of events; trackBy: trackByFn">{{event.content}}</div> -->
|
||||
|
@ -2,13 +2,23 @@ import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationState } from '../services/applicationstate.service';
|
||||
import { Utilities } from '../services/utilities.service';
|
||||
import { relayInit } from 'nostr-tools';
|
||||
import * as moment from 'moment';
|
||||
import { EventValidation } from '../services/eventvalidation.service';
|
||||
import { NostrEvent } from '../services/interfaces';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
})
|
||||
export class HomeComponent {
|
||||
constructor(public appState: ApplicationState, private utilities: Utilities, private router: Router) {}
|
||||
constructor(public appState: ApplicationState, private validator: EventValidation, private utilities: Utilities, private router: Router) {}
|
||||
|
||||
public trackByFn(index: number, item: NostrEvent) {
|
||||
return item.id;
|
||||
}
|
||||
|
||||
events: NostrEvent[] = [];
|
||||
|
||||
async ngOnInit() {
|
||||
const publicKey = localStorage.getItem('blockcore:notes:nostr:pubkey');
|
||||
@ -28,5 +38,54 @@ export class HomeComponent {
|
||||
} else {
|
||||
this.router.navigateByUrl('/connect');
|
||||
}
|
||||
|
||||
// const relay = relayInit('wss://relay.nostr.info');
|
||||
const relay = relayInit('wss://relay.damus.io');
|
||||
|
||||
relay.on('connect', () => {
|
||||
console.log(`connected to ${relay.url}, ${relay.status}`);
|
||||
});
|
||||
|
||||
relay.on('disconnect', () => {
|
||||
console.log(`DISCONNECTED! ${relay.url}`);
|
||||
});
|
||||
|
||||
relay.on('notice', () => {
|
||||
console.log(`NOTICE FROM ${relay.url}`);
|
||||
});
|
||||
|
||||
relay.connect();
|
||||
|
||||
const hourAgo = moment().subtract(1, 'hours').unix();
|
||||
const fiveMinutesAgo = moment().subtract(5, 'minutes').unix();
|
||||
|
||||
//const sub = relay.sub([{ ids: ['d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027'] }], { });
|
||||
const sub = relay.sub([{ kinds: [1], since: fiveMinutesAgo }], {});
|
||||
|
||||
this.events = [];
|
||||
|
||||
sub.on('event', (event: any) => {
|
||||
// Validate the event:
|
||||
const valid = this.validator.validateEvent(event);
|
||||
|
||||
if (!valid) {
|
||||
debugger;
|
||||
console.log('INVALID EVENT!');
|
||||
return;
|
||||
}
|
||||
|
||||
const parsed = this.validator.sanitizeEvent(event);
|
||||
// console.log('we got the event we wanted:', parsed);
|
||||
|
||||
this.events.unshift(parsed);
|
||||
|
||||
if (this.events.length > 100) {
|
||||
this.events.length = 80;
|
||||
}
|
||||
});
|
||||
|
||||
// sub.on('eose', () => {
|
||||
// sub.unsub();
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
@ -1 +1,5 @@
|
||||
Notes!
|
||||
Notes!
|
||||
|
||||
asd
|
||||
fas
|
||||
<dfn><aside>fd</aside></dfn>
|
@ -1,7 +1,52 @@
|
||||
import { Component } from '@angular/core';
|
||||
// import { relayInit, validateEvent, verifySignature, signEvent, getEventHash, getPublicKey } from 'nostr-tools';
|
||||
import { relayInit } from 'nostr-tools';
|
||||
|
||||
@Component({
|
||||
selector: 'app-notes',
|
||||
templateUrl: './notes.component.html',
|
||||
})
|
||||
export class NotesComponent {}
|
||||
export class NotesComponent {
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {
|
||||
// const pool = relayPool();
|
||||
// pool.addRelay('wss://relay.nostr.info', { read: true, write: true });
|
||||
// pool.addRelay('wss://nostr.openchain.fr', { read: true, write: true });
|
||||
// // pool.addRelay('wss://relay.damus.io', {read: true, write: true});
|
||||
// pool.addRelay('wss://nostr-relay.wlvs.space', { read: true, write: true });
|
||||
// pool.addRelay('wss://relay.nostr.ch', { read: true, write: true });
|
||||
// pool.addRelay('wss://nostr.sandwich.farm', { read: true, write: true });
|
||||
|
||||
// console.log('DOES THIS HAPPEN?!?!');
|
||||
|
||||
// const relay = relayInit('wss://relay.nostr.info');
|
||||
|
||||
// relay.on('connect', () => {
|
||||
// console.log(`connected to ${relay.url}`);
|
||||
// });
|
||||
|
||||
// relay.on('disconnect', () => {
|
||||
// console.log(`DISCONNECTED! ${relay.url}`);
|
||||
// });
|
||||
|
||||
// relay.on('notice', () => {
|
||||
// console.log(`NOTICE FROM ${relay.url}`);
|
||||
// });
|
||||
|
||||
// relay.connect();
|
||||
|
||||
// // let's query for an event that exists
|
||||
// let sub = relay.sub([
|
||||
// {
|
||||
// ids: ['d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027'],
|
||||
// },
|
||||
// ]);
|
||||
// sub.on('event', (event) => {
|
||||
// console.log('we got the event we wanted:', event);
|
||||
// });
|
||||
// sub.on('eose', () => {
|
||||
// sub.unsub();
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
63
src/app/services/eventvalidation.service.ts
Normal file
63
src/app/services/eventvalidation.service.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NostrEvent } from './interfaces';
|
||||
import * as sanitizeHtml from 'sanitize-html';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class EventValidation {
|
||||
contentLimit = 280;
|
||||
tagsLimit = 10;
|
||||
|
||||
sanitizeEvent(event: NostrEvent) {
|
||||
// Allow only a super restricted set of tags and attributes
|
||||
const clean = sanitizeHtml(event.content, {
|
||||
allowedTags: ['b', 'i', 'em', 'strong', 'a', 'img'],
|
||||
allowedAttributes: {
|
||||
a: ['href'],
|
||||
img: ['src'], // Only allow src and nothing else on images.
|
||||
},
|
||||
allowedIframeHostnames: ['www.youtube.com'],
|
||||
});
|
||||
|
||||
event.content = clean;
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
/** Returns true if valid, false if not valid. Does not throw error for optimization purposes. */
|
||||
validateEvent(event: NostrEvent) {
|
||||
if (event.pubkey.length < 60 || event.pubkey.length > 70) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!event.sig || !event.id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (event.sig.length < 100 || event.pubkey.length > 150) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (event.id.length !== 64) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof event.kind !== 'number' || typeof event.created_at !== 'number') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Reduce the content length to reduce system resource usage and improve UI experience.
|
||||
if (event.content.length > this.contentLimit) {
|
||||
event.content = event.content.substring(0, this.contentLimit);
|
||||
event.contentCut = true;
|
||||
}
|
||||
|
||||
if (event.tags && event.tags.length > this.tagsLimit) {
|
||||
event.tags = event.tags.splice(0, this.tagsLimit);
|
||||
event.tagsCut = true;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
}
|
6
src/app/services/interfaces.ts
Normal file
6
src/app/services/interfaces.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { Event } from 'nostr-tools';
|
||||
|
||||
export interface NostrEvent extends Event {
|
||||
contentCut: boolean;
|
||||
tagsCut: boolean;
|
||||
}
|
10
src/app/shared/ago.pipe.ts
Normal file
10
src/app/shared/ago.pipe.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
|
||||
@Pipe({ name: 'ago' })
|
||||
export class AgoPipe implements PipeTransform {
|
||||
transform(value: number): string {
|
||||
const date = moment.unix(value);
|
||||
return date.fromNow();
|
||||
}
|
||||
}
|
9
src/polyfills.ts
Normal file
9
src/polyfills.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
(window as any).global = window;
|
||||
global.Buffer = Buffer;
|
||||
global.process = {
|
||||
env: { DEBUG: undefined },
|
||||
version: '',
|
||||
nextTick: require('next-tick'),
|
||||
} as any;
|
@ -6,7 +6,8 @@
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
"src/main.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
|
@ -1,8 +1,11 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "@blockcore/tsconfig",
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"crypto": ["./node_modules/crypto-browserify"],
|
||||
"stream": ["./node_modules/stream-browserify"]
|
||||
},
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
Loading…
Reference in New Issue
Block a user