Add a protocol handler for nostr

This commit is contained in:
SondreB 2023-01-06 00:03:59 +01:00
parent d5a760e089
commit 579f719cc2
No known key found for this signature in database
GPG Key ID: D6CC44C75005FDBF
7 changed files with 113 additions and 8 deletions

1
package-lock.json generated
View File

@ -43,6 +43,7 @@
"@blockcore/tsconfig": "0.0.1",
"@types/jasmine": "~4.3.0",
"@types/qrcode": "^1.5.0",
"@types/qs": "^6.9.7",
"@types/sanitize-html": "^2.8.0",
"@types/uuid": "^9.0.0",
"jasmine-core": "~4.5.0",

View File

@ -47,6 +47,7 @@
"@blockcore/tsconfig": "0.0.1",
"@types/jasmine": "~4.3.0",
"@types/qrcode": "^1.5.0",
"@types/qs": "^6.9.7",
"@types/sanitize-html": "^2.8.0",
"@types/uuid": "^9.0.0",
"jasmine-core": "~4.5.0",

View File

@ -20,6 +20,7 @@ import { ScrollEvent } from './shared/scroll.directive';
import { NavigationService } from './services/navigation.service';
import { NostrProfileDocument } from './services/interfaces';
import { ThemeService } from './services/theme.service';
import { NostrProtocolRequest } from './common/NostrProtocolRequest';
@Component({
selector: 'app-root',
@ -51,6 +52,25 @@ export class AppComponent {
public navigationService: NavigationService,
public theme: ThemeService
) {
// This must happen in the constructor on app component, or when loading in PWA, it won't
// be possible to read the query parameters.
const queryParam = globalThis.location.search;
if (queryParam) {
const param = Object.fromEntries(new URLSearchParams(queryParam)) as any;
this.appState.params = param;
if (this.appState.params.nostr) {
const protocolRequest = new NostrProtocolRequest();
const protocolData = protocolRequest.decode(this.appState.params.nostr);
if (protocolData && protocolData.scheme && protocolData.address) {
const prefix = protocolData.scheme === 'nevent' ? '/e' : '/p';
this.router.navigate([prefix, protocolData.address!]);
}
}
}
// appState.title = 'Blockcore Notes';
this.authService.authInfo$.subscribe(async (auth) => {
this.authenticated = auth.authenticated();

View File

@ -0,0 +1,61 @@
import * as qs from 'qs';
export class NostrProtocolRequestData {
address?: string;
options: any;
scheme?: string;
}
export class NostrProtocolRequest {
prefix = 'web+nostr:';
removeHandler(uri: string) {
if (uri.indexOf('://') > -1) {
return uri.substring(uri.indexOf('://') + 3);
} else {
return uri;
}
}
decode(uri: string): NostrProtocolRequestData {
if (!uri.startsWith(this.prefix)) {
throw new Error('Invalid Nostr URI: ' + uri);
}
var urnScheme = uri.slice(this.prefix.length, uri.indexOf(':', this.prefix.length)).toLowerCase();
var urnValue = uri.slice(this.prefix.length + urnScheme.length + 1);
var split = urnValue.split('?');
var address = split[0];
// Depending on how the user interacts with the protocol handler, browsers might append / at the end of the URL,
// which is then included on the address value. We must ensure that this is removed.
if (address.indexOf('/') > -1) {
address = address.substring(0, address.length - 1);
}
let options;
if (split.length > 1) {
options = qs.parse(split[1]);
}
return { address: address, scheme: urnScheme, options: options };
}
/** Transform all flattened values into PaymentRequestData. */
transform(data: any): NostrProtocolRequestData {
const address = data.address;
const scheme = data.scheme;
const options = data;
delete options.address;
delete options.scheme;
return { address: address, scheme: scheme, options: options };
}
encode(request: NostrProtocolRequestData): string {
var query = qs.stringify(request.options);
return request.scheme + ':' + request.address + (query ? '?' : '') + query;
}
}

View File

@ -39,6 +39,9 @@ export class ApplicationState {
actions: Action[] = [];
/** Parameters that comes from query string during activation of the extension. */
params: any;
isSmallScreen$: Observable<boolean>;
displayLabels$: Observable<boolean>;

View File

@ -33,7 +33,7 @@
<div class="settings-action-buttons">
<button mat-stroked-button (click)="toggle()"><span *ngIf="!open">Expand All</span><span *ngIf="open">Collapse All</span></button>
<br>
<br />
<button mat-flat-button color="primary" (click)="getRelays()">Append relays from extension</button><button mat-flat-button color="primary" (click)="getDefaultRelays()">Append relays from app</button
><button mat-flat-button color="warn" (click)="deleteRelays()">Delete all relays</button>
</div>
@ -47,13 +47,27 @@
</ng-template>
<ng-template matTabContent>
<div class="page">
<mat-form-field class="input-full-width" appearance="outline">
<mat-label>Choose mode</mat-label>
<mat-select (selectionChange)="theme.darkMode = !theme.darkMode" [value]="theme.darkMode">
<mat-option [value]="true">Dark</mat-option>
<mat-option [value]="false">Light</mat-option>
</mat-select>
</mat-form-field>
<mat-card>
<mat-card-content>
<mat-form-field class="input-full-width" appearance="outline">
<mat-label>Choose mode</mat-label>
<mat-select (selectionChange)="theme.darkMode = !theme.darkMode" [value]="theme.darkMode">
<mat-option [value]="true">Dark</mat-option>
<mat-option [value]="false">Light</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
</mat-card>
<br>
<mat-card>
<mat-card-content>
<button mat-stroked-button (click)="registerHandler('web+nostr', 'nostr')">Register Protocol Handler</button>
<br /><br />
<div class="dimmed">Click the button to make Blockcore Notes handle links on websites for npub, nevent and nprofile.</div>
</mat-card-content>
</mat-card>
</div>
</ng-template>
</mat-tab>

View File

@ -104,6 +104,11 @@ export class SettingsComponent {
];
}
registerHandler(protocol: string, parameter: string) {
// navigator.registerProtocolHandler(protocol, `./index.html?${parameter}=%s`);
navigator.registerProtocolHandler(protocol, `/?${parameter}=%s`);
}
addRelay(): void {
const dialogRef = this.dialog.open(AddRelayDialog, {
data: { read: true, write: true },