Create dedicated Nostr Connect component, use nsec.app relay

This commit is contained in:
2026-04-19 15:15:55 +04:00
parent 629a308b79
commit 99d8ca9174
5 changed files with 132 additions and 112 deletions

View File

@@ -0,0 +1,81 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { on } from '@ember/modifier';
import { eq } from 'ember-truth-helpers';
export default class NostrConnectComponent extends Component {
@service nostrAuth;
get hasExtension() {
return typeof window !== 'undefined' && typeof window.nostr !== 'undefined';
}
@action
async connectExtension() {
try {
await this.nostrAuth.login('extension');
if (this.args.onConnect) {
this.args.onConnect();
}
} catch (e) {
console.error(e);
alert(e.message);
}
}
@action
async connectApp() {
try {
await this.nostrAuth.login('connect');
if (this.args.onConnect) {
this.args.onConnect();
}
} catch (e) {
console.error(e);
alert(e.message);
}
}
<template>
<div class="nostr-connect-modal">
<h2>Connect with Nostr</h2>
<div class="nostr-connect-options">
{{#if this.hasExtension}}
<button
class="btn btn-primary"
type="button"
{{on "click" this.connectExtension}}
>
Browser Extension (nos2x, Alby)
</button>
{{else}}
<button
class="btn btn-secondary"
type="button"
disabled
title="No Nostr extension found in your browser."
>
Browser Extension (Not Found)
</button>
{{/if}}
<button
class="btn btn-primary"
type="button"
{{on "click" this.connectApp}}
>
Mobile Signer App (Amber, etc.)
</button>
</div>
{{#if (eq this.nostrAuth.connectStatus "waiting")}}
<div class="alert alert-info nostr-connect-status">
<p>Waiting for you to approve the connection in your mobile signer
app...</p>
</div>
{{/if}}
</div>
</template>
}

View File

@@ -10,6 +10,7 @@ import Icon from '../components/icon';
import PlaceEditForm from './place-edit-form';
import PlaceListsManager from './place-lists-manager';
import PlacePhotoUpload from './place-photo-upload';
import NostrConnect from './nostr-connect';
import Modal from './modal';
import { tracked } from '@glimmer/tracking';
@@ -17,23 +18,40 @@ import { action } from '@ember/object';
export default class PlaceDetails extends Component {
@service storage;
@service nostrAuth;
@tracked isEditing = false;
@tracked showLists = false;
@tracked isPhotoUploadModalOpen = false;
@tracked isNostrConnectModalOpen = false;
@action
openPhotoUploadModal(e) {
if (e) {
e.preventDefault();
}
if (!this.nostrAuth.isConnected) {
this.isNostrConnectModalOpen = true;
} else {
this.isPhotoUploadModalOpen = true;
}
}
@action
closePhotoUploadModal() {
this.isPhotoUploadModalOpen = false;
}
@action
closeNostrConnectModal() {
this.isNostrConnectModalOpen = false;
}
@action
onNostrConnected() {
this.isNostrConnectModalOpen = false;
this.isPhotoUploadModalOpen = true;
}
get isSaved() {
return this.storage.isPlaceSaved(this.place.id || this.place.osmId);
}
@@ -536,5 +554,11 @@ export default class PlaceDetails extends Component {
<PlacePhotoUpload @place={{this.saveablePlace}} />
</Modal>
{{/if}}
{{#if this.isNostrConnectModalOpen}}
<Modal @onClose={{this.closeNostrConnectModal}}>
<NostrConnect @onConnect={{this.onNostrConnected}} />
</Modal>
{{/if}}
</template>
}

View File

@@ -22,16 +22,6 @@ export default class PlacePhotoUpload extends Component {
return this.place.title || 'this place';
}
@action
async login() {
try {
this.error = '';
await this.nostrAuth.login();
} catch (e) {
this.error = e.message;
}
}
@action
async uploadPhoto(event) {
event.preventDefault();
@@ -133,12 +123,6 @@ export default class PlacePhotoUpload extends Component {
</div>
{{/if}}
{{#if this.nostrAuth.isConnected}}
<div class="connected-status">
<strong>Connected:</strong>
{{this.nostrAuth.pubkey}}
</div>
<form {{on "submit" this.uploadPhoto}}>
{{#if this.photoUrl}}
<div class="preview-group">
@@ -158,11 +142,6 @@ export default class PlacePhotoUpload extends Component {
</button>
{{/if}}
</form>
{{else}}
<button type="button" class="btn btn-primary" {{on "click" this.login}}>
Connect Nostr Extension
</button>
{{/if}}
</div>
</template>
}

View File

@@ -1,11 +1,11 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { service } from '@ember/service';
import { inject as service } from '@ember/service';
import Icon from '#components/icon';
import { on } from '@ember/modifier';
import { tracked } from '@glimmer/tracking';
import { eq } from 'ember-truth-helpers';
import Modal from './modal';
import NostrConnect from './nostr-connect';
export default class UserMenuComponent extends Component {
@service storage;
@@ -46,37 +46,11 @@ export default class UserMenuComponent extends Component {
this.isNostrConnectModalOpen = false;
}
@action
async connectNostrExtension() {
try {
await this.nostrAuth.login('extension');
this.closeNostrConnectModal();
} catch (e) {
console.error(e);
alert(e.message);
}
}
@action
async connectNostrApp() {
try {
await this.nostrAuth.login('connect');
this.closeNostrConnectModal();
} catch (e) {
console.error(e);
alert(e.message);
}
}
@action
disconnectNostr() {
this.nostrAuth.logout();
}
get hasExtension() {
return typeof window !== 'undefined' && typeof window.nostr !== 'undefined';
}
<template>
<div class="user-menu-popover">
<ul class="account-list">
@@ -173,45 +147,7 @@ export default class UserMenuComponent extends Component {
{{#if this.isNostrConnectModalOpen}}
<Modal @onClose={{this.closeNostrConnectModal}}>
<div class="nostr-connect-modal">
<h2>Connect with Nostr</h2>
<div class="nostr-connect-options">
{{#if this.hasExtension}}
<button
class="btn btn-primary"
type="button"
{{on "click" this.connectNostrExtension}}
>
Browser Extension (nos2x, Alby)
</button>
{{else}}
<button
class="btn btn-secondary"
type="button"
disabled
title="No Nostr extension found in your browser."
>
Browser Extension (Not Found)
</button>
{{/if}}
<button
class="btn btn-primary"
type="button"
{{on "click" this.connectNostrApp}}
>
Mobile Signer App (Amber, etc.)
</button>
</div>
{{#if (eq this.nostrAuth.connectStatus "waiting")}}
<div class="alert alert-info nostr-connect-status">
<p>Waiting for you to approve the connection in your mobile signer
app...</p>
</div>
{{/if}}
</div>
<NostrConnect @onConnect={{this.closeNostrConnectModal}} />
</Modal>
{{/if}}
</template>

View File

@@ -125,7 +125,7 @@ export default class NostrAuthService extends Service {
}
// We use a specific relay for the connection handshake.
const relay = 'wss://relay.damus.io';
const relay = 'wss://relay.nsec.app';
localStorage.setItem(STORAGE_KEY_CONNECT_RELAY, relay);
this._signerInstance = new NostrConnectSigner({
@@ -190,7 +190,7 @@ export default class NostrAuthService extends Service {
STORAGE_KEY_CONNECT_REMOTE_PUBKEY
);
const relay =
localStorage.getItem(STORAGE_KEY_CONNECT_RELAY) || 'wss://relay.damus.io';
localStorage.getItem(STORAGE_KEY_CONNECT_RELAY) || 'wss://relay.nsec.app';
if (!localKeyHex || !remotePubkey) {
throw new Error('Missing Nostr Connect local state.');