import Component from '@glimmer/component'; import { on } from '@ember/modifier'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { service } from '@ember/service'; import { fn } from '@ember/helper'; import Icon from '#components/icon'; import { excludeRequiredRelays, mergeRequiredRelays, normalizeRelayUrl, } from '../../../utils/nostr'; const stripProtocol = (url) => (url ? url.replace(/^wss?:\/\//, '') : ''); export default class AppMenuSettingsNostr extends Component { @service settings; @service nostrData; @service toast; @tracked newReadRelay = ''; @tracked newWriteRelay = ''; get customReadRelays() { return excludeRequiredRelays( this.settings.nostrReadRelays || [], this.nostrData.requiredReadRelays ); } get customWriteRelays() { return excludeRequiredRelays( this.settings.nostrWriteRelays || [], this.nostrData.requiredWriteRelays ); } get readRelayExclusions() { return this.settings.nostrReadRelayExclusions || []; } get writeRelayExclusions() { return this.settings.nostrWriteRelayExclusions || []; } get requiredReadRelaySet() { return new Set(this.nostrData.requiredReadRelays.filter(Boolean)); } get requiredWriteRelaySet() { return new Set(this.nostrData.requiredWriteRelays.filter(Boolean)); } get mailboxReadRelaySet() { return new Set(this.nostrData.mailboxReadRelays); } get mailboxWriteRelaySet() { return new Set(this.nostrData.mailboxWriteRelays); } get hasReadOverrides() { return ( this.customReadRelays.length > 0 || this.readRelayExclusions.length > 0 ); } get hasWriteOverrides() { return ( this.customWriteRelays.length > 0 || this.writeRelayExclusions.length > 0 ); } get readRelaysForDisplay() { return this.nostrData.activeReadRelays.map((url) => { return { url, isRequired: this.requiredReadRelaySet.has(url), }; }); } get writeRelaysForDisplay() { return this.nostrData.activeWriteRelays.map((url) => { return { url, isRequired: this.requiredWriteRelaySet.has(url), }; }); } @action updateNewReadRelay(event) { this.newReadRelay = event.target.value; } @action updateNewWriteRelay(event) { this.newWriteRelay = event.target.value; } @action addReadRelay() { const url = normalizeRelayUrl(this.newReadRelay); if (!url) return; const merged = mergeRequiredRelays(this.nostrData.requiredReadRelays, [ ...this.customReadRelays, url, ]); const custom = excludeRequiredRelays( merged, this.nostrData.requiredReadRelays ); const readExclusions = this.readRelayExclusions.filter((relay) => { return normalizeRelayUrl(relay) !== url; }); this.settings.update('nostrReadRelays', custom.length > 0 ? custom : null); this.settings.update( 'nostrReadRelayExclusions', readExclusions.length > 0 ? readExclusions : null ); this.newReadRelay = ''; } @action removeReadRelay(url) { if (this.requiredReadRelaySet.has(url)) { return; } const normalizedUrl = normalizeRelayUrl(url); const remainingCustom = this.customReadRelays.filter((relay) => { return normalizeRelayUrl(relay) !== normalizedUrl; }); const nextExclusions = this.mailboxReadRelaySet.has(normalizedUrl) ? Array.from(new Set([...this.readRelayExclusions, normalizedUrl])) : this.readRelayExclusions; this.settings.update( 'nostrReadRelays', remainingCustom.length > 0 ? remainingCustom : null ); this.settings.update( 'nostrReadRelayExclusions', nextExclusions.length > 0 ? nextExclusions : null ); } @action handleReadRelayKeydown(event) { if (event.key === 'Enter') { this.addReadRelay(); } } @action handleWriteRelayKeydown(event) { if (event.key === 'Enter') { this.addWriteRelay(); } } @action resetReadRelays() { this.settings.update('nostrReadRelays', null); this.settings.update('nostrReadRelayExclusions', null); } @action addWriteRelay() { const url = normalizeRelayUrl(this.newWriteRelay); if (!url) return; const merged = mergeRequiredRelays(this.nostrData.requiredWriteRelays, [ ...this.customWriteRelays, url, ]); const custom = excludeRequiredRelays( merged, this.nostrData.requiredWriteRelays ); const writeExclusions = this.writeRelayExclusions.filter((relay) => { return normalizeRelayUrl(relay) !== url; }); this.settings.update('nostrWriteRelays', custom.length > 0 ? custom : null); this.settings.update( 'nostrWriteRelayExclusions', writeExclusions.length > 0 ? writeExclusions : null ); this.newWriteRelay = ''; } @action removeWriteRelay(url) { if (this.requiredWriteRelaySet.has(url)) { return; } const normalizedUrl = normalizeRelayUrl(url); const remainingCustom = this.customWriteRelays.filter((relay) => { return normalizeRelayUrl(relay) !== normalizedUrl; }); const nextExclusions = this.mailboxWriteRelaySet.has(normalizedUrl) ? Array.from(new Set([...this.writeRelayExclusions, normalizedUrl])) : this.writeRelayExclusions; this.settings.update( 'nostrWriteRelays', remainingCustom.length > 0 ? remainingCustom : null ); this.settings.update( 'nostrWriteRelayExclusions', nextExclusions.length > 0 ? nextExclusions : null ); } @action resetWriteRelays() { this.settings.update('nostrWriteRelays', null); this.settings.update('nostrWriteRelayExclusions', null); } @action async clearCache() { try { await this.nostrData.clearCache(); this.toast.show('Nostr cache cleared'); } catch (e) { this.toast.show(`Failed to clear Nostr cache: ${e.message}`); } } }