diff --git a/package-lock.json b/package-lock.json index 0124793..f232225 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index b2a3b1f..fe58362 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e2895d6..50590ec 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -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(); diff --git a/src/app/common/NostrProtocolRequest.ts b/src/app/common/NostrProtocolRequest.ts new file mode 100644 index 0000000..e1b2287 --- /dev/null +++ b/src/app/common/NostrProtocolRequest.ts @@ -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; + } +} diff --git a/src/app/services/applicationstate.service.ts b/src/app/services/applicationstate.service.ts index b732ed1..856590b 100644 --- a/src/app/services/applicationstate.service.ts +++ b/src/app/services/applicationstate.service.ts @@ -39,6 +39,9 @@ export class ApplicationState { actions: Action[] = []; + /** Parameters that comes from query string during activation of the extension. */ + params: any; + isSmallScreen$: Observable; displayLabels$: Observable; diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index cba50d7..4dfe820 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -33,7 +33,7 @@
-
+
@@ -47,13 +47,27 @@
- - Choose mode - - Dark - Light - - + + + + Choose mode + + Dark + Light + + + + + +
+ + + + +

+
Click the button to make Blockcore Notes handle links on websites for npub, nevent and nprofile.
+
+
diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 0dfcfe4..46e78b8 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -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 },