Compare commits
5 Commits
a89ba904c8
...
b23d54d74f
| Author | SHA1 | Date | |
|---|---|---|---|
|
b23d54d74f
|
|||
|
5bd4dba907
|
|||
|
54ba99673f
|
|||
|
54445f249b
|
|||
|
9828ad2714
|
@@ -1,129 +1,25 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { on } from '@ember/modifier';
|
||||
import { service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import Icon from '#components/icon';
|
||||
import eq from 'ember-truth-helpers/helpers/eq';
|
||||
import AppMenuSettingsMapUi from './settings/map-ui';
|
||||
import AppMenuSettingsApis from './settings/apis';
|
||||
import AppMenuSettingsNostr from './settings/nostr';
|
||||
|
||||
export default class AppMenuSettings extends Component {
|
||||
@service settings;
|
||||
<template>
|
||||
<div class="sidebar-header">
|
||||
<button type="button" class="back-btn" {{on "click" @onBack}}>
|
||||
<Icon @name="arrow-left" @size={{20}} @color="#333" />
|
||||
</button>
|
||||
<h2>Settings</h2>
|
||||
<button type="button" class="close-btn" {{on "click" @onClose}}>
|
||||
<Icon @name="x" @size={{20}} @color="#333" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@action
|
||||
updateApi(event) {
|
||||
this.settings.updateOverpassApi(event.target.value);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleKinetic(event) {
|
||||
this.settings.updateMapKinetic(event.target.value === 'true');
|
||||
}
|
||||
|
||||
@action
|
||||
toggleQuickSearchButtons(event) {
|
||||
this.settings.updateShowQuickSearchButtons(event.target.value === 'true');
|
||||
}
|
||||
|
||||
@action
|
||||
updatePhotonApi(event) {
|
||||
this.settings.updatePhotonApi(event.target.value);
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="sidebar-header">
|
||||
<button type="button" class="back-btn" {{on "click" @onBack}}>
|
||||
<Icon @name="arrow-left" @size={{20}} @color="#333" />
|
||||
</button>
|
||||
<h2>Settings</h2>
|
||||
<button type="button" class="close-btn" {{on "click" @onClose}}>
|
||||
<Icon @name="x" @size={{20}} @color="#333" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-content">
|
||||
<section class="settings-section">
|
||||
<div class="form-group">
|
||||
<label for="show-quick-search">Quick search buttons visible</label>
|
||||
<select
|
||||
id="show-quick-search"
|
||||
class="form-control"
|
||||
{{on "change" this.toggleQuickSearchButtons}}
|
||||
>
|
||||
<option
|
||||
value="true"
|
||||
selected={{if this.settings.showQuickSearchButtons "selected"}}
|
||||
>
|
||||
Yes
|
||||
</option>
|
||||
<option
|
||||
value="false"
|
||||
selected={{unless
|
||||
this.settings.showQuickSearchButtons
|
||||
"selected"
|
||||
}}
|
||||
>
|
||||
No
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="map-kinetic">Map Inertia (Kinetic Panning)</label>
|
||||
<select
|
||||
id="map-kinetic"
|
||||
class="form-control"
|
||||
{{on "change" this.toggleKinetic}}
|
||||
>
|
||||
<option
|
||||
value="true"
|
||||
selected={{if this.settings.mapKinetic "selected"}}
|
||||
>
|
||||
On
|
||||
</option>
|
||||
<option
|
||||
value="false"
|
||||
selected={{unless this.settings.mapKinetic "selected"}}
|
||||
>
|
||||
Off
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="overpass-api">Overpass API Provider</label>
|
||||
<select
|
||||
id="overpass-api"
|
||||
class="form-control"
|
||||
{{on "change" this.updateApi}}
|
||||
>
|
||||
{{#each this.settings.overpassApis as |api|}}
|
||||
<option
|
||||
value={{api.url}}
|
||||
selected={{if
|
||||
(eq api.url this.settings.overpassApi)
|
||||
"selected"
|
||||
}}
|
||||
>
|
||||
{{api.name}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="photon-api">Photon API Provider</label>
|
||||
<select
|
||||
id="photon-api"
|
||||
class="form-control"
|
||||
{{on "change" this.updatePhotonApi}}
|
||||
>
|
||||
{{#each this.settings.photonApis as |api|}}
|
||||
<option
|
||||
value={{api.url}}
|
||||
selected={{if (eq api.url this.settings.photonApi) "selected"}}
|
||||
>
|
||||
{{api.name}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
<div class="sidebar-content">
|
||||
<section class="settings-section">
|
||||
<AppMenuSettingsMapUi />
|
||||
<AppMenuSettingsApis />
|
||||
<AppMenuSettingsNostr />
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
69
app/components/app-menu/settings/apis.gjs
Normal file
69
app/components/app-menu/settings/apis.gjs
Normal file
@@ -0,0 +1,69 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { on } from '@ember/modifier';
|
||||
import { service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import Icon from '#components/icon';
|
||||
import eq from 'ember-truth-helpers/helpers/eq';
|
||||
|
||||
export default class AppMenuSettingsApis extends Component {
|
||||
@service settings;
|
||||
|
||||
@action
|
||||
updateApi(event) {
|
||||
this.settings.updateOverpassApi(event.target.value);
|
||||
}
|
||||
|
||||
@action
|
||||
updatePhotonApi(event) {
|
||||
this.settings.updatePhotonApi(event.target.value);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-nested-interactive }}
|
||||
<details>
|
||||
<summary>
|
||||
<Icon @name="server" @size={{20}} />
|
||||
<span>API Providers</span>
|
||||
</summary>
|
||||
<div class="details-content">
|
||||
<div class="form-group">
|
||||
<label for="overpass-api">Overpass API Provider</label>
|
||||
<select
|
||||
id="overpass-api"
|
||||
class="form-control"
|
||||
{{on "change" this.updateApi}}
|
||||
>
|
||||
{{#each this.settings.overpassApis as |api|}}
|
||||
<option
|
||||
value={{api.url}}
|
||||
selected={{if
|
||||
(eq api.url this.settings.overpassApi)
|
||||
"selected"
|
||||
}}
|
||||
>
|
||||
{{api.name}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="photon-api">Photon API Provider</label>
|
||||
<select
|
||||
id="photon-api"
|
||||
class="form-control"
|
||||
{{on "change" this.updatePhotonApi}}
|
||||
>
|
||||
{{#each this.settings.photonApis as |api|}}
|
||||
<option
|
||||
value={{api.url}}
|
||||
selected={{if (eq api.url this.settings.photonApi) "selected"}}
|
||||
>
|
||||
{{api.name}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</template>
|
||||
}
|
||||
76
app/components/app-menu/settings/map-ui.gjs
Normal file
76
app/components/app-menu/settings/map-ui.gjs
Normal file
@@ -0,0 +1,76 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { on } from '@ember/modifier';
|
||||
import { service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import Icon from '#components/icon';
|
||||
|
||||
export default class AppMenuSettingsMapUi extends Component {
|
||||
@service settings;
|
||||
|
||||
@action
|
||||
toggleKinetic(event) {
|
||||
this.settings.updateMapKinetic(event.target.value === 'true');
|
||||
}
|
||||
|
||||
@action
|
||||
toggleQuickSearchButtons(event) {
|
||||
this.settings.updateShowQuickSearchButtons(event.target.value === 'true');
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-nested-interactive }}
|
||||
<details>
|
||||
<summary>
|
||||
<Icon @name="map" @size={{20}} />
|
||||
<span>Map & UI</span>
|
||||
</summary>
|
||||
<div class="details-content">
|
||||
<div class="form-group">
|
||||
<label for="show-quick-search">Quick search buttons visible</label>
|
||||
<select
|
||||
id="show-quick-search"
|
||||
class="form-control"
|
||||
{{on "change" this.toggleQuickSearchButtons}}
|
||||
>
|
||||
<option
|
||||
value="true"
|
||||
selected={{if this.settings.showQuickSearchButtons "selected"}}
|
||||
>
|
||||
Yes
|
||||
</option>
|
||||
<option
|
||||
value="false"
|
||||
selected={{unless
|
||||
this.settings.showQuickSearchButtons
|
||||
"selected"
|
||||
}}
|
||||
>
|
||||
No
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="map-kinetic">Map Inertia (Kinetic Panning)</label>
|
||||
<select
|
||||
id="map-kinetic"
|
||||
class="form-control"
|
||||
{{on "change" this.toggleKinetic}}
|
||||
>
|
||||
<option
|
||||
value="true"
|
||||
selected={{if this.settings.mapKinetic "selected"}}
|
||||
>
|
||||
On
|
||||
</option>
|
||||
<option
|
||||
value="false"
|
||||
selected={{unless this.settings.mapKinetic "selected"}}
|
||||
>
|
||||
Off
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</template>
|
||||
}
|
||||
53
app/components/app-menu/settings/nostr.gjs
Normal file
53
app/components/app-menu/settings/nostr.gjs
Normal file
@@ -0,0 +1,53 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { on } from '@ember/modifier';
|
||||
import { service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import Icon from '#components/icon';
|
||||
|
||||
export default class AppMenuSettingsNostr extends Component {
|
||||
@service settings;
|
||||
|
||||
@action
|
||||
togglePhotoFallbackUploads(event) {
|
||||
this.settings.updateNostrPhotoFallbackUploads(
|
||||
event.target.value === 'true'
|
||||
);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-nested-interactive }}
|
||||
<details>
|
||||
<summary>
|
||||
<Icon @name="zap" @size={{20}} />
|
||||
<span>Nostr</span>
|
||||
</summary>
|
||||
<div class="details-content">
|
||||
<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" this.togglePhotoFallbackUploads}}
|
||||
>
|
||||
<option
|
||||
value="true"
|
||||
selected={{if this.settings.nostrPhotoFallbackUploads "selected"}}
|
||||
>
|
||||
Yes
|
||||
</option>
|
||||
<option
|
||||
value="false"
|
||||
selected={{unless
|
||||
this.settings.nostrPhotoFallbackUploads
|
||||
"selected"
|
||||
}}
|
||||
>
|
||||
No
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</template>
|
||||
}
|
||||
@@ -551,7 +551,10 @@ export default class PlaceDetails extends Component {
|
||||
|
||||
{{#if this.isPhotoUploadModalOpen}}
|
||||
<Modal @onClose={{this.closePhotoUploadModal}}>
|
||||
<PlacePhotoUpload @place={{this.saveablePlace}} />
|
||||
<PlacePhotoUpload
|
||||
@place={{this.saveablePlace}}
|
||||
@onClose={{this.closePhotoUploadModal}}
|
||||
/>
|
||||
</Modal>
|
||||
{{/if}}
|
||||
|
||||
|
||||
@@ -183,11 +183,16 @@ export default class PlacePhotoUpload extends Component {
|
||||
const event = await factory.sign(template);
|
||||
await this.nostrRelay.publish(event);
|
||||
|
||||
this.status = 'Published successfully!';
|
||||
this.toast.show('Photos published successfully');
|
||||
this.status = '';
|
||||
|
||||
// Clear out the files so user can upload more or be done
|
||||
this.files = [];
|
||||
this.uploadedPhotos = [];
|
||||
|
||||
if (this.args.onClose) {
|
||||
this.args.onClose();
|
||||
}
|
||||
} catch (e) {
|
||||
this.error = 'Failed to publish: ' + e.message;
|
||||
this.status = '';
|
||||
|
||||
@@ -21,10 +21,17 @@ function getBlossomUrl(serverUrl, path) {
|
||||
export default class BlossomService extends Service {
|
||||
@service nostrAuth;
|
||||
@service nostrData;
|
||||
@service settings;
|
||||
|
||||
get servers() {
|
||||
const servers = this.nostrData.blossomServers;
|
||||
return servers.length ? servers : [DEFAULT_BLOSSOM_SERVER];
|
||||
const allServers = servers.length ? servers : [DEFAULT_BLOSSOM_SERVER];
|
||||
|
||||
if (!this.settings.nostrPhotoFallbackUploads) {
|
||||
return [allServers[0]];
|
||||
}
|
||||
|
||||
return allServers;
|
||||
}
|
||||
|
||||
async _getAuthHeader(action, hash, serverUrl) {
|
||||
|
||||
@@ -4,6 +4,8 @@ import { EventStore } from 'applesauce-core/event-store';
|
||||
import { ProfileModel } from 'applesauce-core/models/profile';
|
||||
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';
|
||||
|
||||
const BOOTSTRAP_RELAYS = [
|
||||
'wss://purplepag.es',
|
||||
@@ -26,10 +28,40 @@ export default class NostrDataService extends Service {
|
||||
_blossomSub = null;
|
||||
|
||||
_requestSub = null;
|
||||
_cachePromise = null;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
// Initialize the IndexedDB cache
|
||||
this._cachePromise = openDB('applesauce-events').then(async (db) => {
|
||||
this.cache = new NostrIDB(db, {
|
||||
cacheIndexes: 1000,
|
||||
maxEvents: 10000,
|
||||
});
|
||||
|
||||
await this.cache.start();
|
||||
|
||||
// Automatically persist new events to the cache
|
||||
this._stopPersisting = persistEventsToCache(
|
||||
this.store,
|
||||
async (events) => {
|
||||
// Only cache profiles, mailboxes, and blossom servers
|
||||
const toCache = events.filter(
|
||||
(e) => e.kind === 0 || e.kind === 10002 || e.kind === 10063
|
||||
);
|
||||
|
||||
if (toCache.length > 0) {
|
||||
await Promise.all(toCache.map((event) => this.cache.add(event)));
|
||||
}
|
||||
},
|
||||
{
|
||||
batchTime: 1000, // Batch writes every 1 second
|
||||
maxBatchSize: 100,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Feed events from the relay pool into the event store
|
||||
this.nostrRelay.pool.relays$.subscribe(() => {
|
||||
// Setup relay subscription tracking if needed, or we just rely on request()
|
||||
@@ -63,24 +95,8 @@ export default class NostrDataService extends Service {
|
||||
|
||||
const relayList = Array.from(relays);
|
||||
|
||||
// Request events and dump them into the store
|
||||
this._requestSub = this.nostrRelay.pool
|
||||
.request(relayList, [
|
||||
{
|
||||
authors: [pubkey],
|
||||
kinds: [0, 10002, 10063],
|
||||
},
|
||||
])
|
||||
.subscribe({
|
||||
next: (event) => {
|
||||
this.store.add(event);
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error fetching profile events:', err);
|
||||
},
|
||||
});
|
||||
|
||||
// Setup models to track state reactively
|
||||
// Setup models to track state reactively FIRST
|
||||
// This way, if cached events populate the store, the UI updates instantly.
|
||||
this._profileSub = this.store
|
||||
.model(ProfileModel, pubkey)
|
||||
.subscribe((profileContent) => {
|
||||
@@ -104,6 +120,43 @@ export default class NostrDataService extends Service {
|
||||
this.blossomServers = [];
|
||||
}
|
||||
});
|
||||
|
||||
// 1. Await cache initialization and populate the EventStore with local data
|
||||
try {
|
||||
await this._cachePromise;
|
||||
|
||||
const cachedEvents = await this.cache.query([
|
||||
{
|
||||
authors: [pubkey],
|
||||
kinds: [0, 10002, 10063],
|
||||
},
|
||||
]);
|
||||
|
||||
if (cachedEvents && cachedEvents.length > 0) {
|
||||
for (const event of cachedEvents) {
|
||||
this.store.add(event);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to read from local Nostr IDB cache', e);
|
||||
}
|
||||
|
||||
// 2. Request new events from the network in the background and dump them into the store
|
||||
this._requestSub = this.nostrRelay.pool
|
||||
.request(relayList, [
|
||||
{
|
||||
authors: [pubkey],
|
||||
kinds: [0, 10002, 10063],
|
||||
},
|
||||
])
|
||||
.subscribe({
|
||||
next: (event) => {
|
||||
this.store.add(event);
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error fetching profile events:', err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
get userDisplayName() {
|
||||
@@ -154,5 +207,13 @@ export default class NostrDataService extends Service {
|
||||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
this._cleanupSubscriptions();
|
||||
|
||||
if (this._stopPersisting) {
|
||||
this._stopPersisting();
|
||||
}
|
||||
|
||||
if (this.cache) {
|
||||
this.cache.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export default class SettingsService extends Service {
|
||||
@tracked mapKinetic = true;
|
||||
@tracked photonApi = 'https://photon.komoot.io/api/';
|
||||
@tracked showQuickSearchButtons = true;
|
||||
@tracked nostrPhotoFallbackUploads = false;
|
||||
|
||||
overpassApis = [
|
||||
{
|
||||
@@ -64,6 +65,14 @@ export default class SettingsService extends Service {
|
||||
if (savedShowQuickSearch !== null) {
|
||||
this.showQuickSearchButtons = savedShowQuickSearch === 'true';
|
||||
}
|
||||
|
||||
const savedNostrPhotoFallbackUploads = localStorage.getItem(
|
||||
'marco:nostr-photo-fallback-uploads'
|
||||
);
|
||||
if (savedNostrPhotoFallbackUploads !== null) {
|
||||
this.nostrPhotoFallbackUploads =
|
||||
savedNostrPhotoFallbackUploads === 'true';
|
||||
}
|
||||
}
|
||||
|
||||
updateOverpassApi(url) {
|
||||
@@ -84,4 +93,9 @@ export default class SettingsService extends Service {
|
||||
updatePhotonApi(url) {
|
||||
this.photonApi = url;
|
||||
}
|
||||
|
||||
updateNostrPhotoFallbackUploads(enabled) {
|
||||
this.nostrPhotoFallbackUploads = enabled;
|
||||
localStorage.setItem('marco:nostr-photo-fallback-uploads', String(enabled));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,7 +595,7 @@ body {
|
||||
display: block;
|
||||
font-size: 0.85rem;
|
||||
color: #666;
|
||||
margin-bottom: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
@@ -967,6 +967,7 @@ abbr[title] {
|
||||
display: inline-flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: -6px 0;
|
||||
}
|
||||
|
||||
.app-logo-icon svg {
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
"blurhash": "^2.0.5",
|
||||
"ember-concurrency": "^5.2.0",
|
||||
"ember-lifeline": "^7.0.0",
|
||||
"nostr-idb": "^5.0.0",
|
||||
"oauth2-pkce": "^2.1.3",
|
||||
"qrcode": "^1.5.4",
|
||||
"rxjs": "^7.8.2"
|
||||
|
||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -35,6 +35,9 @@ importers:
|
||||
ember-lifeline:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0(@ember/test-helpers@5.4.1(@babel/core@7.28.6))
|
||||
nostr-idb:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
oauth2-pkce:
|
||||
specifier: ^2.1.3
|
||||
version: 2.1.3
|
||||
@@ -3709,6 +3712,9 @@ packages:
|
||||
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
idb@8.0.3:
|
||||
resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==}
|
||||
|
||||
ignore@5.3.2:
|
||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||
engines: {node: '>= 4'}
|
||||
@@ -4405,6 +4411,9 @@ packages:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
nostr-idb@5.0.0:
|
||||
resolution: {integrity: sha512-w5y4AnHefZIwCCL11NryfM2xp3U0Ka4qVNQEYAjnQbPwyoV+bZTdwuPXHCdRDWvhOFP2bZr1WBegcsAmkBjrxQ==}
|
||||
|
||||
nostr-signer-capacitor-plugin@0.0.5:
|
||||
resolution: {integrity: sha512-/EvqWz71HZ5cWmzvfXWTm48AWZtbeZDbOg3vLwXyXPjnIp1DR7Wurww/Mo41ORNu1DNPlqH20l7kIXKO6vR5og==}
|
||||
peerDependencies:
|
||||
@@ -10286,6 +10295,8 @@ snapshots:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
idb@8.0.3: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
|
||||
ignore@7.0.5: {}
|
||||
@@ -11021,6 +11032,13 @@ snapshots:
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
nostr-idb@5.0.0:
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
idb: 8.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
nostr-signer-capacitor-plugin@0.0.5(@capacitor/core@7.6.2):
|
||||
dependencies:
|
||||
'@capacitor/core': 7.6.2
|
||||
|
||||
Reference in New Issue
Block a user