{{! template-lint-disable no-nested-interactive }}
@@ -40,6 +123,96 @@ export default class AppMenuSettingsNostr extends Component {
+
+
+
+
diff --git a/app/components/place-photo-upload.gjs b/app/components/place-photo-upload.gjs
index 46410cc..7d8b52b 100644
--- a/app/components/place-photo-upload.gjs
+++ b/app/components/place-photo-upload.gjs
@@ -13,6 +13,7 @@ import { or, not } from 'ember-truth-helpers';
export default class PlacePhotoUpload extends Component {
@service nostrAuth;
@service nostrRelay;
+ @service nostrData;
@service blossom;
@service toast;
@@ -181,7 +182,7 @@ export default class PlacePhotoUpload extends Component {
}
const event = await factory.sign(template);
- await this.nostrRelay.publish(event);
+ await this.nostrRelay.publish(this.nostrData.activeWriteRelays, event);
this.toast.show('Photos published successfully');
this.status = '';
diff --git a/app/services/nostr-data.js b/app/services/nostr-data.js
index 455af6e..5a1d257 100644
--- a/app/services/nostr-data.js
+++ b/app/services/nostr-data.js
@@ -6,16 +6,21 @@ import { MailboxesModel } from 'applesauce-core/models/mailboxes';
import { npubEncode } from 'applesauce-core/helpers/pointers';
import { persistEventsToCache } from 'applesauce-core/helpers/event-cache';
import { NostrIDB, openDB } from 'nostr-idb';
+import { normalizeRelayUrl } from '../utils/nostr';
-const BOOTSTRAP_RELAYS = [
+const DIRECTORY_RELAYS = [
'wss://purplepag.es',
'wss://relay.damus.io',
'wss://nos.lol',
];
+const DEFAULT_READ_RELAYS = ['wss://nostr.kosmos.org'];
+const DEFAULT_WRITE_RELAYS = [];
+
export default class NostrDataService extends Service {
@service nostrRelay;
@service nostrAuth;
+ @service settings;
store = new EventStore();
@@ -69,6 +74,45 @@ export default class NostrDataService extends Service {
});
}
+ get defaultReadRelays() {
+ const mailboxes = (this.mailboxes?.inboxes || [])
+ .map(normalizeRelayUrl)
+ .filter(Boolean);
+ const defaults = DEFAULT_READ_RELAYS.map(normalizeRelayUrl).filter(Boolean);
+ return Array.from(new Set([...defaults, ...mailboxes]));
+ }
+
+ get defaultWriteRelays() {
+ const mailboxes = (this.mailboxes?.outboxes || [])
+ .map(normalizeRelayUrl)
+ .filter(Boolean);
+ const defaults =
+ DEFAULT_WRITE_RELAYS.map(normalizeRelayUrl).filter(Boolean);
+ return Array.from(new Set([...defaults, ...mailboxes]));
+ }
+
+ get activeReadRelays() {
+ if (this.settings.nostrReadRelays) {
+ return Array.from(
+ new Set(
+ this.settings.nostrReadRelays.map(normalizeRelayUrl).filter(Boolean)
+ )
+ );
+ }
+ return this.defaultReadRelays;
+ }
+
+ get activeWriteRelays() {
+ if (this.settings.nostrWriteRelays) {
+ return Array.from(
+ new Set(
+ this.settings.nostrWriteRelays.map(normalizeRelayUrl).filter(Boolean)
+ )
+ );
+ }
+ return this.defaultWriteRelays;
+ }
+
async loadProfile(pubkey) {
if (!pubkey) return;
@@ -79,22 +123,6 @@ export default class NostrDataService extends Service {
this._cleanupSubscriptions();
- const relays = new Set(BOOTSTRAP_RELAYS);
-
- // Try to get extension relays
- if (typeof window.nostr !== 'undefined' && window.nostr.getRelays) {
- try {
- const extRelays = await window.nostr.getRelays();
- for (const url of Object.keys(extRelays)) {
- relays.add(url);
- }
- } catch {
- console.warn('Failed to get NIP-07 relays');
- }
- }
-
- const relayList = Array.from(relays);
-
// Setup models to track state reactively FIRST
// This way, if cached events populate the store, the UI updates instantly.
this._profileSub = this.store
@@ -142,8 +170,11 @@ export default class NostrDataService extends Service {
}
// 2. Request new events from the network in the background and dump them into the store
+ const profileRelays = Array.from(
+ new Set([...DIRECTORY_RELAYS, ...this.activeWriteRelays])
+ );
this._requestSub = this.nostrRelay.pool
- .request(relayList, [
+ .request(profileRelays, [
{
authors: [pubkey],
kinds: [0, 10002, 10063],
diff --git a/app/services/nostr-relay.js b/app/services/nostr-relay.js
index 98e7e9f..320a38b 100644
--- a/app/services/nostr-relay.js
+++ b/app/services/nostr-relay.js
@@ -4,13 +4,13 @@ import { RelayPool } from 'applesauce-relay';
export default class NostrRelayService extends Service {
pool = new RelayPool();
- // For Phase 1, we hardcode the local relay
- relays = ['ws://127.0.0.1:7777'];
-
- async publish(event) {
+ async publish(relays, event) {
+ if (!relays || relays.length === 0) {
+ throw new Error('No relays provided to publish the event.');
+ }
// The publish method is a wrapper around the event method that returns a Promise