Add media server selection to Nostr settings #82
@@ -4,12 +4,14 @@ import { action } from '@ember/object';
|
|||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
import { service } from '@ember/service';
|
import { service } from '@ember/service';
|
||||||
import { fn } from '@ember/helper';
|
import { fn } from '@ember/helper';
|
||||||
|
import eq from 'ember-truth-helpers/helpers/eq';
|
||||||
import Icon from '#components/icon';
|
import Icon from '#components/icon';
|
||||||
import {
|
import {
|
||||||
excludeRequiredRelays,
|
excludeRequiredRelays,
|
||||||
mergeRequiredRelays,
|
mergeRequiredRelays,
|
||||||
normalizeRelayUrl,
|
normalizeRelayUrl,
|
||||||
} from '../../../utils/nostr';
|
} from '../../../utils/nostr';
|
||||||
|
import { DEFAULT_BLOSSOM_SERVER } from '../../../services/blossom';
|
||||||
|
|
||||||
const stripProtocol = (url) => (url ? url.replace(/^wss?:\/\//, '') : '');
|
const stripProtocol = (url) => (url ? url.replace(/^wss?:\/\//, '') : '');
|
||||||
|
|
||||||
@@ -89,6 +91,15 @@ export default class AppMenuSettingsNostr extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get mediaServerOptions() {
|
||||||
|
const servers = this.nostrData.blossomServers;
|
||||||
|
return servers.length ? servers : [DEFAULT_BLOSSOM_SERVER];
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasMultipleMediaServers() {
|
||||||
|
return this.mediaServerOptions.length > 1;
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
updateNewReadRelay(event) {
|
updateNewReadRelay(event) {
|
||||||
this.newReadRelay = event.target.value;
|
this.newReadRelay = event.target.value;
|
||||||
@@ -342,31 +353,57 @@ export default class AppMenuSettingsNostr extends Component {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="nostr-photo-fallback-uploads">Upload photos to fallback
|
<label for="nostr-media-server">Media server</label>
|
||||||
servers</label>
|
|
||||||
<select
|
<select
|
||||||
id="nostr-photo-fallback-uploads"
|
id="nostr-media-server"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
{{on "change" (fn @onChange "nostrPhotoFallbackUploads")}}
|
{{on "change" (fn @onChange "nostrMediaServer")}}
|
||||||
>
|
>
|
||||||
<option
|
{{#each this.mediaServerOptions as |server|}}
|
||||||
value="true"
|
<option
|
||||||
selected={{if this.settings.nostrPhotoFallbackUploads "selected"}}
|
value={{server}}
|
||||||
>
|
selected={{if
|
||||||
Yes
|
(eq server this.settings.nostrMediaServer)
|
||||||
</option>
|
"selected"
|
||||||
<option
|
}}
|
||||||
value="false"
|
>
|
||||||
selected={{unless
|
{{server}}
|
||||||
this.settings.nostrPhotoFallbackUploads
|
</option>
|
||||||
"selected"
|
{{/each}}
|
||||||
}}
|
|
||||||
>
|
|
||||||
No
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{#if this.hasMultipleMediaServers}}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="nostr-photo-fallback-uploads">Upload photos to fallback
|
||||||
|
servers</label>
|
||||||
|
<select
|
||||||
|
id="nostr-photo-fallback-uploads"
|
||||||
|
class="form-control"
|
||||||
|
{{on "change" (fn @onChange "nostrPhotoFallbackUploads")}}
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value="true"
|
||||||
|
selected={{if
|
||||||
|
this.settings.nostrPhotoFallbackUploads
|
||||||
|
"selected"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="false"
|
||||||
|
selected={{unless
|
||||||
|
this.settings.nostrPhotoFallbackUploads
|
||||||
|
"selected"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Cached data</label>
|
<label>Cached data</label>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -24,14 +24,15 @@ export default class BlossomService extends Service {
|
|||||||
@service settings;
|
@service settings;
|
||||||
|
|
||||||
get servers() {
|
get servers() {
|
||||||
const servers = this.nostrData.blossomServers;
|
const discovered = this.nostrData.blossomServers;
|
||||||
const allServers = servers.length ? servers : [DEFAULT_BLOSSOM_SERVER];
|
const mainServer = this.settings.nostrMediaServer || DEFAULT_BLOSSOM_SERVER;
|
||||||
|
const fallbacks = discovered.filter((server) => server !== mainServer);
|
||||||
|
|
||||||
if (!this.settings.nostrPhotoFallbackUploads) {
|
if (!this.settings.nostrPhotoFallbackUploads) {
|
||||||
return [allServers[0]];
|
return [mainServer];
|
||||||
}
|
}
|
||||||
|
|
||||||
return allServers;
|
return [mainServer, ...fallbacks];
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getAuthHeader(action, hash, serverUrl) {
|
async _getAuthHeader(action, hash, serverUrl) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
uniqNormalizedRelays,
|
uniqNormalizedRelays,
|
||||||
} from '../utils/nostr';
|
} from '../utils/nostr';
|
||||||
import { getGeohashPrefixesInBbox } from '../utils/geohash-coverage';
|
import { getGeohashPrefixesInBbox } from '../utils/geohash-coverage';
|
||||||
|
import { DEFAULT_BLOSSOM_SERVER } from './blossom';
|
||||||
|
|
||||||
const DIRECTORY_RELAYS = [
|
const DIRECTORY_RELAYS = [
|
||||||
'wss://relay.primal.net',
|
'wss://relay.primal.net',
|
||||||
@@ -437,6 +438,16 @@ export default class NostrDataService extends Service {
|
|||||||
} else {
|
} else {
|
||||||
this.blossomServers = [];
|
this.blossomServers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.blossomServers.length > 0) {
|
||||||
|
const current = this.settings.nostrMediaServer;
|
||||||
|
if (
|
||||||
|
current === DEFAULT_BLOSSOM_SERVER ||
|
||||||
|
!this.blossomServers.includes(current)
|
||||||
|
) {
|
||||||
|
this.settings.update('nostrMediaServer', this.blossomServers[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 1. Await cache initialization and populate the EventStore with local data
|
// 1. Await cache initialization and populate the EventStore with local data
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import Service from '@ember/service';
|
import Service from '@ember/service';
|
||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
|
import { DEFAULT_BLOSSOM_SERVER } from './blossom';
|
||||||
|
|
||||||
const DEFAULT_SETTINGS = {
|
const DEFAULT_SETTINGS = {
|
||||||
overpassApi: 'https://overpass-api.de/api/interpreter',
|
overpassApi: 'https://overpass-api.de/api/interpreter',
|
||||||
mapKinetic: true,
|
mapKinetic: true,
|
||||||
photonApi: 'https://photon.komoot.io/api/',
|
photonApi: 'https://photon.komoot.io/api/',
|
||||||
showQuickSearchButtons: true,
|
showQuickSearchButtons: true,
|
||||||
nostrPhotoFallbackUploads: false,
|
nostrPhotoFallbackUploads: false,
|
||||||
|
nostrMediaServer: DEFAULT_BLOSSOM_SERVER,
|
||||||
nostrReadRelays: null,
|
nostrReadRelays: null,
|
||||||
nostrWriteRelays: null,
|
nostrWriteRelays: null,
|
||||||
nostrReadRelayExclusions: null,
|
nostrReadRelayExclusions: null,
|
||||||
@@ -21,6 +24,7 @@ export default class SettingsService extends Service {
|
|||||||
@tracked showQuickSearchButtons = DEFAULT_SETTINGS.showQuickSearchButtons;
|
@tracked showQuickSearchButtons = DEFAULT_SETTINGS.showQuickSearchButtons;
|
||||||
@tracked nostrPhotoFallbackUploads =
|
@tracked nostrPhotoFallbackUploads =
|
||||||
DEFAULT_SETTINGS.nostrPhotoFallbackUploads;
|
DEFAULT_SETTINGS.nostrPhotoFallbackUploads;
|
||||||
|
@tracked nostrMediaServer = DEFAULT_SETTINGS.nostrMediaServer;
|
||||||
@tracked nostrReadRelays = DEFAULT_SETTINGS.nostrReadRelays;
|
@tracked nostrReadRelays = DEFAULT_SETTINGS.nostrReadRelays;
|
||||||
@tracked nostrWriteRelays = DEFAULT_SETTINGS.nostrWriteRelays;
|
@tracked nostrWriteRelays = DEFAULT_SETTINGS.nostrWriteRelays;
|
||||||
@tracked nostrReadRelayExclusions = DEFAULT_SETTINGS.nostrReadRelayExclusions;
|
@tracked nostrReadRelayExclusions = DEFAULT_SETTINGS.nostrReadRelayExclusions;
|
||||||
@@ -114,6 +118,7 @@ export default class SettingsService extends Service {
|
|||||||
this.photonApi = finalSettings.photonApi;
|
this.photonApi = finalSettings.photonApi;
|
||||||
this.showQuickSearchButtons = finalSettings.showQuickSearchButtons;
|
this.showQuickSearchButtons = finalSettings.showQuickSearchButtons;
|
||||||
this.nostrPhotoFallbackUploads = finalSettings.nostrPhotoFallbackUploads;
|
this.nostrPhotoFallbackUploads = finalSettings.nostrPhotoFallbackUploads;
|
||||||
|
this.nostrMediaServer = finalSettings.nostrMediaServer;
|
||||||
this.nostrReadRelays = finalSettings.nostrReadRelays;
|
this.nostrReadRelays = finalSettings.nostrReadRelays;
|
||||||
this.nostrWriteRelays = finalSettings.nostrWriteRelays;
|
this.nostrWriteRelays = finalSettings.nostrWriteRelays;
|
||||||
this.nostrReadRelayExclusions = finalSettings.nostrReadRelayExclusions;
|
this.nostrReadRelayExclusions = finalSettings.nostrReadRelayExclusions;
|
||||||
@@ -132,6 +137,7 @@ export default class SettingsService extends Service {
|
|||||||
photonApi: this.photonApi,
|
photonApi: this.photonApi,
|
||||||
showQuickSearchButtons: this.showQuickSearchButtons,
|
showQuickSearchButtons: this.showQuickSearchButtons,
|
||||||
nostrPhotoFallbackUploads: this.nostrPhotoFallbackUploads,
|
nostrPhotoFallbackUploads: this.nostrPhotoFallbackUploads,
|
||||||
|
nostrMediaServer: this.nostrMediaServer,
|
||||||
nostrReadRelays: this.nostrReadRelays,
|
nostrReadRelays: this.nostrReadRelays,
|
||||||
nostrWriteRelays: this.nostrWriteRelays,
|
nostrWriteRelays: this.nostrWriteRelays,
|
||||||
nostrReadRelayExclusions: this.nostrReadRelayExclusions,
|
nostrReadRelayExclusions: this.nostrReadRelayExclusions,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
mergeRequiredRelays,
|
mergeRequiredRelays,
|
||||||
uniqNormalizedRelays,
|
uniqNormalizedRelays,
|
||||||
} from 'marco/utils/nostr';
|
} from 'marco/utils/nostr';
|
||||||
|
import { DEFAULT_BLOSSOM_SERVER } from 'marco/services/blossom';
|
||||||
|
|
||||||
class MockNostrDataService extends Service {
|
class MockNostrDataService extends Service {
|
||||||
@service settings;
|
@service settings;
|
||||||
@@ -18,6 +19,8 @@ class MockNostrDataService extends Service {
|
|||||||
mailboxReadRelays = ['wss://mailbox.example.com'];
|
mailboxReadRelays = ['wss://mailbox.example.com'];
|
||||||
mailboxWriteRelays = ['wss://mailbox-write.example.com'];
|
mailboxWriteRelays = ['wss://mailbox-write.example.com'];
|
||||||
|
|
||||||
|
blossomServers = [];
|
||||||
|
|
||||||
get configuredReadRelays() {
|
get configuredReadRelays() {
|
||||||
const configured = uniqNormalizedRelays([
|
const configured = uniqNormalizedRelays([
|
||||||
...this.mailboxReadRelays,
|
...this.mailboxReadRelays,
|
||||||
@@ -185,4 +188,33 @@ module('Integration | Component | app-menu/settings/nostr', function (hooks) {
|
|||||||
'wss://mailbox-write.example.com',
|
'wss://mailbox-write.example.com',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('media server dropdown shows only the default server when no blossom servers discovered', async function (assert) {
|
||||||
|
const element = await renderAndOpenDetails(this);
|
||||||
|
const select = element.querySelector('#nostr-media-server');
|
||||||
|
|
||||||
|
assert.dom(select).exists();
|
||||||
|
assert.strictEqual(select.options.length, 1);
|
||||||
|
assert.dom(select.options[0]).hasValue(DEFAULT_BLOSSOM_SERVER);
|
||||||
|
assert.dom(select.options[0]).hasText(DEFAULT_BLOSSOM_SERVER);
|
||||||
|
assert.strictEqual(select.value, DEFAULT_BLOSSOM_SERVER);
|
||||||
|
assert
|
||||||
|
.dom('#nostr-photo-fallback-uploads')
|
||||||
|
.doesNotExist('fallback toggle hidden with single media server');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('media server dropdown lists discovered blossom servers', async function (assert) {
|
||||||
|
const servers = ['https://cdn.example.com', 'https://cdn.satellite.earth'];
|
||||||
|
this.owner.lookup('service:nostrData').blossomServers = servers;
|
||||||
|
|
||||||
|
const element = await renderAndOpenDetails(this);
|
||||||
|
const select = element.querySelector('#nostr-media-server');
|
||||||
|
|
||||||
|
assert.strictEqual(select.options.length, servers.length);
|
||||||
|
assert.dom(select.options[0]).hasValue(servers[0]);
|
||||||
|
assert.dom(select.options[1]).hasValue(servers[1]);
|
||||||
|
assert
|
||||||
|
.dom('#nostr-photo-fallback-uploads')
|
||||||
|
.exists('fallback toggle visible with multiple media servers');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user