Create dedicated Nostr Connect component, use nsec.app relay
This commit is contained in:
81
app/components/nostr-connect.gjs
Normal file
81
app/components/nostr-connect.gjs
Normal 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>
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import Icon from '../components/icon';
|
|||||||
import PlaceEditForm from './place-edit-form';
|
import PlaceEditForm from './place-edit-form';
|
||||||
import PlaceListsManager from './place-lists-manager';
|
import PlaceListsManager from './place-lists-manager';
|
||||||
import PlacePhotoUpload from './place-photo-upload';
|
import PlacePhotoUpload from './place-photo-upload';
|
||||||
|
import NostrConnect from './nostr-connect';
|
||||||
import Modal from './modal';
|
import Modal from './modal';
|
||||||
|
|
||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
@@ -17,16 +18,22 @@ import { action } from '@ember/object';
|
|||||||
|
|
||||||
export default class PlaceDetails extends Component {
|
export default class PlaceDetails extends Component {
|
||||||
@service storage;
|
@service storage;
|
||||||
|
@service nostrAuth;
|
||||||
@tracked isEditing = false;
|
@tracked isEditing = false;
|
||||||
@tracked showLists = false;
|
@tracked showLists = false;
|
||||||
@tracked isPhotoUploadModalOpen = false;
|
@tracked isPhotoUploadModalOpen = false;
|
||||||
|
@tracked isNostrConnectModalOpen = false;
|
||||||
|
|
||||||
@action
|
@action
|
||||||
openPhotoUploadModal(e) {
|
openPhotoUploadModal(e) {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
this.isPhotoUploadModalOpen = true;
|
if (!this.nostrAuth.isConnected) {
|
||||||
|
this.isNostrConnectModalOpen = true;
|
||||||
|
} else {
|
||||||
|
this.isPhotoUploadModalOpen = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@@ -34,6 +41,17 @@ export default class PlaceDetails extends Component {
|
|||||||
this.isPhotoUploadModalOpen = false;
|
this.isPhotoUploadModalOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
closeNostrConnectModal() {
|
||||||
|
this.isNostrConnectModalOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onNostrConnected() {
|
||||||
|
this.isNostrConnectModalOpen = false;
|
||||||
|
this.isPhotoUploadModalOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
get isSaved() {
|
get isSaved() {
|
||||||
return this.storage.isPlaceSaved(this.place.id || this.place.osmId);
|
return this.storage.isPlaceSaved(this.place.id || this.place.osmId);
|
||||||
}
|
}
|
||||||
@@ -518,7 +536,7 @@ export default class PlaceDetails extends Component {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.osmUrl}}
|
{{#if this.osmUrl}}
|
||||||
<div class="meta-info">
|
<div class="meta-info">
|
||||||
<p class="content-with-icon">
|
<p class="content-with-icon">
|
||||||
<Icon @name="camera" />
|
<Icon @name="camera" />
|
||||||
<span>
|
<span>
|
||||||
@@ -527,7 +545,7 @@ export default class PlaceDetails extends Component {
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -536,5 +554,11 @@ export default class PlaceDetails extends Component {
|
|||||||
<PlacePhotoUpload @place={{this.saveablePlace}} />
|
<PlacePhotoUpload @place={{this.saveablePlace}} />
|
||||||
</Modal>
|
</Modal>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.isNostrConnectModalOpen}}
|
||||||
|
<Modal @onClose={{this.closeNostrConnectModal}}>
|
||||||
|
<NostrConnect @onConnect={{this.onNostrConnected}} />
|
||||||
|
</Modal>
|
||||||
|
{{/if}}
|
||||||
</template>
|
</template>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,6 @@ export default class PlacePhotoUpload extends Component {
|
|||||||
return this.place.title || 'this place';
|
return this.place.title || 'this place';
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
async login() {
|
|
||||||
try {
|
|
||||||
this.error = '';
|
|
||||||
await this.nostrAuth.login();
|
|
||||||
} catch (e) {
|
|
||||||
this.error = e.message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async uploadPhoto(event) {
|
async uploadPhoto(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -133,36 +123,25 @@ export default class PlacePhotoUpload extends Component {
|
|||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if this.nostrAuth.isConnected}}
|
<form {{on "submit" this.uploadPhoto}}>
|
||||||
<div class="connected-status">
|
{{#if this.photoUrl}}
|
||||||
<strong>Connected:</strong>
|
<div class="preview-group">
|
||||||
{{this.nostrAuth.pubkey}}
|
<p>Photo Preview:</p>
|
||||||
</div>
|
<img src={{this.photoUrl}} alt="Preview" />
|
||||||
|
</div>
|
||||||
<form {{on "submit" this.uploadPhoto}}>
|
<button
|
||||||
{{#if this.photoUrl}}
|
type="button"
|
||||||
<div class="preview-group">
|
class="btn btn-primary"
|
||||||
<p>Photo Preview:</p>
|
{{on "click" this.publish}}
|
||||||
<img src={{this.photoUrl}} alt="Preview" />
|
>
|
||||||
</div>
|
Publish Event (kind: 360)
|
||||||
<button
|
</button>
|
||||||
type="button"
|
{{else}}
|
||||||
class="btn btn-primary"
|
<button type="submit" class="btn btn-secondary">
|
||||||
{{on "click" this.publish}}
|
Mock Upload Photo
|
||||||
>
|
</button>
|
||||||
Publish Event (kind: 360)
|
{{/if}}
|
||||||
</button>
|
</form>
|
||||||
{{else}}
|
|
||||||
<button type="submit" class="btn btn-secondary">
|
|
||||||
Mock Upload Photo
|
|
||||||
</button>
|
|
||||||
{{/if}}
|
|
||||||
</form>
|
|
||||||
{{else}}
|
|
||||||
<button type="button" class="btn btn-primary" {{on "click" this.login}}>
|
|
||||||
Connect Nostr Extension
|
|
||||||
</button>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import { service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import Icon from '#components/icon';
|
import Icon from '#components/icon';
|
||||||
import { on } from '@ember/modifier';
|
import { on } from '@ember/modifier';
|
||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
import { eq } from 'ember-truth-helpers';
|
|
||||||
import Modal from './modal';
|
import Modal from './modal';
|
||||||
|
import NostrConnect from './nostr-connect';
|
||||||
|
|
||||||
export default class UserMenuComponent extends Component {
|
export default class UserMenuComponent extends Component {
|
||||||
@service storage;
|
@service storage;
|
||||||
@@ -46,37 +46,11 @@ export default class UserMenuComponent extends Component {
|
|||||||
this.isNostrConnectModalOpen = false;
|
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
|
@action
|
||||||
disconnectNostr() {
|
disconnectNostr() {
|
||||||
this.nostrAuth.logout();
|
this.nostrAuth.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasExtension() {
|
|
||||||
return typeof window !== 'undefined' && typeof window.nostr !== 'undefined';
|
|
||||||
}
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="user-menu-popover">
|
<div class="user-menu-popover">
|
||||||
<ul class="account-list">
|
<ul class="account-list">
|
||||||
@@ -173,45 +147,7 @@ export default class UserMenuComponent extends Component {
|
|||||||
|
|
||||||
{{#if this.isNostrConnectModalOpen}}
|
{{#if this.isNostrConnectModalOpen}}
|
||||||
<Modal @onClose={{this.closeNostrConnectModal}}>
|
<Modal @onClose={{this.closeNostrConnectModal}}>
|
||||||
<div class="nostr-connect-modal">
|
<NostrConnect @onConnect={{this.closeNostrConnectModal}} />
|
||||||
<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>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export default class NostrAuthService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We use a specific relay for the connection handshake.
|
// 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);
|
localStorage.setItem(STORAGE_KEY_CONNECT_RELAY, relay);
|
||||||
|
|
||||||
this._signerInstance = new NostrConnectSigner({
|
this._signerInstance = new NostrConnectSigner({
|
||||||
@@ -190,7 +190,7 @@ export default class NostrAuthService extends Service {
|
|||||||
STORAGE_KEY_CONNECT_REMOTE_PUBKEY
|
STORAGE_KEY_CONNECT_REMOTE_PUBKEY
|
||||||
);
|
);
|
||||||
const relay =
|
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) {
|
if (!localKeyHex || !remotePubkey) {
|
||||||
throw new Error('Missing Nostr Connect local state.');
|
throw new Error('Missing Nostr Connect local state.');
|
||||||
|
|||||||
Reference in New Issue
Block a user