mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Refactor NWC wallet settings into service
This commit is contained in:
1 parent
e05006f0b7
commit
419bb02610
5 files changed
+526
-280
No files matched your search
@@ -33,6 +33,7 @@ import watchHistoryService from "./watchHistoryService.js";
|
||||
import r2Service from "./services/r2Service.js";
|
||||
import PlaybackService from "./services/playbackService.js";
|
||||
import AuthService from "./services/authService.js";
|
||||
import NwcSettingsService from "./services/nwcSettingsService.js";
|
||||
import nostrService from "./services/nostrService.js";
|
||||
import { initQuickR2Upload } from "./r2-quick.js";
|
||||
import { createWatchHistoryRenderer } from "./historyView.js";
|
||||
@@ -46,12 +47,6 @@ import {
|
||||
formatViewCount,
|
||||
ingestLocalViewEvent,
|
||||
} from "./viewCounter.js";
|
||||
import {
|
||||
loadNwcSettings,
|
||||
saveNwcSettings,
|
||||
clearNwcSettings,
|
||||
createDefaultNwcSettings,
|
||||
} from "./nwcSettings.js";
|
||||
import { splitAndZap as splitAndZapDefault } from "./payments/zapSplit.js";
|
||||
import {
|
||||
formatAbsoluteTimestamp as formatAbsoluteTimestampUtil,
|
||||
@@ -111,7 +106,6 @@ const FALLBACK_THUMBNAIL_SRC = "assets/jpg/video-thumbnail-fallback.jpg";
|
||||
const MAX_DISCUSSION_COUNT_VIDEOS = 24;
|
||||
const VIDEO_EVENT_KIND = 30078;
|
||||
const HEX64_REGEX = /^[0-9a-f]{64}$/i;
|
||||
const NWC_URI_SCHEME = "nostr+walletconnect://";
|
||||
/**
|
||||
* Simple IntersectionObserver-based lazy loader for images (or videos).
|
||||
*
|
||||
@@ -200,6 +194,31 @@ class Application {
|
||||
this.splitAndZap =
|
||||
(services.payments && services.payments.splitAndZap) ||
|
||||
splitAndZapDefault;
|
||||
|
||||
this.nwcSettingsService =
|
||||
services.nwcSettingsService ||
|
||||
new NwcSettingsService({
|
||||
normalizeHexPubkey: (value) => this.normalizeHexPubkey(value),
|
||||
getActivePubkey: () => this.pubkey,
|
||||
payments: this.payments,
|
||||
logger: {
|
||||
warn: (...args) => console.warn(...args),
|
||||
},
|
||||
notifyError: (message) => this.showError(message),
|
||||
maxWalletDefaultZap: MAX_WALLET_DEFAULT_ZAP,
|
||||
});
|
||||
if (
|
||||
this.nwcSettingsService &&
|
||||
typeof this.nwcSettingsService.setActivePubkeyGetter === "function"
|
||||
) {
|
||||
this.nwcSettingsService.setActivePubkeyGetter(() => this.pubkey);
|
||||
}
|
||||
if (
|
||||
this.nwcSettingsService &&
|
||||
typeof this.nwcSettingsService.setPayments === "function"
|
||||
) {
|
||||
this.nwcSettingsService.setPayments(this.payments);
|
||||
}
|
||||
if (
|
||||
this.feedEngine &&
|
||||
typeof this.feedEngine.run !== "function" &&
|
||||
@@ -394,13 +413,7 @@ class Application {
|
||||
nostrClient,
|
||||
accessControl,
|
||||
getCurrentUserNpub: () => this.getCurrentUserNpub(),
|
||||
getActiveNwcSettings: () => this.getActiveNwcSettings(),
|
||||
updateActiveNwcSettings: (partial) =>
|
||||
this.updateActiveNwcSettings(partial),
|
||||
hydrateNwcSettingsForPubkey: (pubkey) =>
|
||||
this.hydrateNwcSettingsForPubkey(pubkey),
|
||||
createDefaultNwcSettings: () => createDefaultNwcSettings(),
|
||||
ensureWallet: (options) => this.ensureWallet(options),
|
||||
nwcSettings: this.nwcSettingsService,
|
||||
loadVideos: (forceFetch, context) =>
|
||||
this.loadVideos(forceFetch, context),
|
||||
onVideosShouldRefresh: (context) =>
|
||||
@@ -514,9 +527,8 @@ class Application {
|
||||
this.zapController = new ZapController({
|
||||
videoModal: this.videoModal,
|
||||
getCurrentVideo: () => this.currentVideo,
|
||||
getActiveNwcSettings: () => this.getActiveNwcSettings(),
|
||||
nwcSettings: this.nwcSettingsService,
|
||||
isUserLoggedIn: () => this.isUserLoggedIn(),
|
||||
hasActiveWalletConnection: () => this.hasActiveWalletConnection(),
|
||||
splitAndZap: (...args) => this.splitAndZap(...args),
|
||||
payments: this.payments,
|
||||
callbacks: {
|
||||
@@ -630,7 +642,6 @@ class Application {
|
||||
this.moreMenuGlobalHandlerBound = false;
|
||||
this.boundMoreMenuDocumentClick = null;
|
||||
this.boundMoreMenuDocumentKeydown = null;
|
||||
this.nwcSettings = new Map();
|
||||
this.boundNwcSettingsToastHandler = null;
|
||||
Object.defineProperty(this, "savedProfiles", {
|
||||
configurable: false,
|
||||
@@ -2650,35 +2661,18 @@ class Application {
|
||||
return context;
|
||||
}
|
||||
|
||||
async handleProfileWalletPersist({
|
||||
nwcUri,
|
||||
defaultZap,
|
||||
lastChecked,
|
||||
} = {}) {
|
||||
const partial = {};
|
||||
if (nwcUri !== undefined) {
|
||||
partial.nwcUri = nwcUri;
|
||||
}
|
||||
if (defaultZap !== undefined) {
|
||||
partial.defaultZap = defaultZap;
|
||||
}
|
||||
if (lastChecked !== undefined) {
|
||||
partial.lastChecked = lastChecked;
|
||||
}
|
||||
|
||||
if (!Object.keys(partial).length) {
|
||||
return this.getActiveNwcSettings();
|
||||
}
|
||||
|
||||
return this.updateActiveNwcSettings(partial);
|
||||
async handleProfileWalletPersist(options = {}) {
|
||||
return this.nwcSettingsService.handleProfileWalletPersist(options);
|
||||
}
|
||||
|
||||
async handleProfileWalletTest({ nwcUri, defaultZap } = {}) {
|
||||
return this.ensureWallet({ nwcUri, defaultZap });
|
||||
return this.nwcSettingsService.ensureWallet({ nwcUri, defaultZap });
|
||||
}
|
||||
|
||||
async handleProfileWalletDisconnect() {
|
||||
return this.updateActiveNwcSettings(createDefaultNwcSettings());
|
||||
return this.nwcSettingsService.updateActiveNwcSettings(
|
||||
this.nwcSettingsService.createDefaultNwcSettings(),
|
||||
);
|
||||
}
|
||||
|
||||
handleProfileAdminNotifyError({ error } = {}) {
|
||||
@@ -2725,69 +2719,19 @@ class Application {
|
||||
}
|
||||
|
||||
async hydrateNwcSettingsForPubkey(pubkey) {
|
||||
const normalized = this.normalizeHexPubkey(pubkey);
|
||||
if (!normalized) {
|
||||
return createDefaultNwcSettings();
|
||||
}
|
||||
|
||||
try {
|
||||
const settings = await loadNwcSettings(normalized);
|
||||
const record =
|
||||
settings && typeof settings === "object"
|
||||
? { ...settings }
|
||||
: createDefaultNwcSettings();
|
||||
this.nwcSettings.set(normalized, record);
|
||||
return { ...record };
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[nwcSettings] Failed to load settings for ${normalized}:`,
|
||||
error
|
||||
);
|
||||
const fallback = createDefaultNwcSettings();
|
||||
this.nwcSettings.set(normalized, fallback);
|
||||
return { ...fallback };
|
||||
}
|
||||
return this.nwcSettingsService.hydrateNwcSettingsForPubkey(pubkey);
|
||||
}
|
||||
|
||||
getActiveNwcSettings() {
|
||||
const normalized = this.normalizeHexPubkey(this.pubkey);
|
||||
if (!normalized) {
|
||||
return createDefaultNwcSettings();
|
||||
}
|
||||
const cached = this.nwcSettings.get(normalized);
|
||||
return cached ? { ...cached } : createDefaultNwcSettings();
|
||||
return this.nwcSettingsService.getActiveNwcSettings();
|
||||
}
|
||||
|
||||
hasActiveWalletConnection() {
|
||||
const settings = this.getActiveNwcSettings();
|
||||
const candidate =
|
||||
typeof settings?.nwcUri === "string" ? settings.nwcUri.trim() : "";
|
||||
return candidate.length > 0;
|
||||
return this.nwcSettingsService.hasActiveWalletConnection();
|
||||
}
|
||||
|
||||
validateWalletUri(uri, { requireValue = false } = {}) {
|
||||
const value = typeof uri === "string" ? uri.trim() : "";
|
||||
|
||||
if (!value) {
|
||||
if (requireValue) {
|
||||
return {
|
||||
valid: false,
|
||||
sanitized: "",
|
||||
message: "Enter a wallet connect URI before continuing.",
|
||||
};
|
||||
}
|
||||
return { valid: true, sanitized: "" };
|
||||
}
|
||||
|
||||
if (!value.toLowerCase().startsWith(NWC_URI_SCHEME)) {
|
||||
return {
|
||||
valid: false,
|
||||
sanitized: value,
|
||||
message: `Wallet URI must start with ${NWC_URI_SCHEME}.`,
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true, sanitized: value };
|
||||
validateWalletUri(uri, options) {
|
||||
return this.nwcSettingsService.validateWalletUri(uri, options);
|
||||
}
|
||||
|
||||
isUserLoggedIn() {
|
||||
@@ -2795,96 +2739,15 @@ class Application {
|
||||
}
|
||||
|
||||
async updateActiveNwcSettings(partial = {}) {
|
||||
const normalized = this.normalizeHexPubkey(this.pubkey);
|
||||
if (!normalized) {
|
||||
console.warn(
|
||||
"[nwcSettings] Cannot update settings without an active pubkey."
|
||||
);
|
||||
return createDefaultNwcSettings();
|
||||
}
|
||||
|
||||
try {
|
||||
const updated = await saveNwcSettings(normalized, partial);
|
||||
const record =
|
||||
updated && typeof updated === "object"
|
||||
? { ...updated }
|
||||
: createDefaultNwcSettings();
|
||||
this.nwcSettings.set(normalized, record);
|
||||
return { ...record };
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[nwcSettings] Failed to save settings for ${normalized}:`,
|
||||
error
|
||||
);
|
||||
return this.getActiveNwcSettings();
|
||||
}
|
||||
return this.nwcSettingsService.updateActiveNwcSettings(partial);
|
||||
}
|
||||
|
||||
async ensureWallet({ nwcUri, defaultZap } = {}) {
|
||||
const activeSettings = this.getActiveNwcSettings();
|
||||
const candidateUri =
|
||||
typeof nwcUri === "string" && nwcUri.trim()
|
||||
? nwcUri.trim()
|
||||
: activeSettings.nwcUri || "";
|
||||
const { valid, sanitized, message } = this.validateWalletUri(candidateUri, {
|
||||
requireValue: true,
|
||||
});
|
||||
if (!valid) {
|
||||
throw new Error(message || "Invalid wallet URI provided.");
|
||||
}
|
||||
|
||||
const merged = {
|
||||
...activeSettings,
|
||||
nwcUri: sanitized,
|
||||
};
|
||||
|
||||
if (typeof defaultZap === "number" && Number.isFinite(defaultZap)) {
|
||||
const rounded = Math.max(0, Math.round(defaultZap));
|
||||
merged.defaultZap = Math.min(MAX_WALLET_DEFAULT_ZAP, rounded);
|
||||
} else if (defaultZap === null) {
|
||||
merged.defaultZap = null;
|
||||
}
|
||||
|
||||
if (this.payments && typeof this.payments.ensureWallet === "function") {
|
||||
return this.payments.ensureWallet({ settings: merged });
|
||||
}
|
||||
|
||||
console.warn(
|
||||
"[wallet] Falling back to stub ensureWallet implementation. Returning settings without performing a connection test."
|
||||
);
|
||||
return merged;
|
||||
return this.nwcSettingsService.ensureWallet({ nwcUri, defaultZap });
|
||||
}
|
||||
|
||||
async clearStoredNwcSettings(pubkey, { silent = false } = {}) {
|
||||
const normalized = this.normalizeHexPubkey(pubkey);
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await clearNwcSettings(normalized);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[nwcSettings] Failed to clear settings for ${normalized}:`,
|
||||
error
|
||||
);
|
||||
if (!silent) {
|
||||
this.showError("Failed to clear wallet settings for this account.");
|
||||
}
|
||||
try {
|
||||
await saveNwcSettings(normalized, createDefaultNwcSettings());
|
||||
} catch (persistError) {
|
||||
console.warn(
|
||||
`[nwcSettings] Failed to overwrite settings for ${normalized}:`,
|
||||
persistError
|
||||
);
|
||||
}
|
||||
this.nwcSettings.delete(normalized);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.nwcSettings.delete(normalized);
|
||||
return true;
|
||||
async clearStoredNwcSettings(pubkey, options = {}) {
|
||||
return this.nwcSettingsService.clearStoredNwcSettings(pubkey, options);
|
||||
}
|
||||
|
||||
async handleAuthLogin(detail = {}) {
|
||||
@@ -2896,26 +2759,15 @@ class Application {
|
||||
}
|
||||
}
|
||||
|
||||
const normalizedActive = this.normalizeHexPubkey(
|
||||
detail?.pubkey || this.pubkey
|
||||
);
|
||||
const normalizedPrevious = this.normalizeHexPubkey(detail?.previousPubkey);
|
||||
|
||||
if (detail?.identityChanged) {
|
||||
this.resetViewLoggingState();
|
||||
this.nwcSettings.clear();
|
||||
}
|
||||
|
||||
if (
|
||||
normalizedPrevious &&
|
||||
(!normalizedActive || normalizedPrevious !== normalizedActive)
|
||||
) {
|
||||
await this.clearStoredNwcSettings(normalizedPrevious, { silent: true });
|
||||
}
|
||||
|
||||
if (normalizedActive) {
|
||||
await this.hydrateNwcSettingsForPubkey(normalizedActive);
|
||||
}
|
||||
await this.nwcSettingsService.onLogin({
|
||||
pubkey: detail?.pubkey || this.pubkey,
|
||||
previousPubkey: detail?.previousPubkey,
|
||||
identityChanged: Boolean(detail?.identityChanged),
|
||||
});
|
||||
|
||||
if (this.profileController) {
|
||||
try {
|
||||
@@ -2979,13 +2831,10 @@ class Application {
|
||||
async handleAuthLogout(detail = {}) {
|
||||
this.resetViewLoggingState();
|
||||
|
||||
const normalizedPrevious = this.normalizeHexPubkey(
|
||||
detail?.previousPubkey || detail?.pubkey || this.pubkey
|
||||
);
|
||||
if (normalizedPrevious) {
|
||||
await this.clearStoredNwcSettings(normalizedPrevious, { silent: false });
|
||||
}
|
||||
this.nwcSettings.clear();
|
||||
await this.nwcSettingsService.onLogout({
|
||||
pubkey: detail?.pubkey || this.pubkey,
|
||||
previousPubkey: detail?.previousPubkey,
|
||||
});
|
||||
|
||||
if (this.profileController) {
|
||||
try {
|
||||
@@ -7244,8 +7093,11 @@ class Application {
|
||||
this.boundNwcSettingsToastHandler = null;
|
||||
}
|
||||
|
||||
if (this.nwcSettings instanceof Map) {
|
||||
this.nwcSettings.clear();
|
||||
if (
|
||||
this.nwcSettingsService &&
|
||||
typeof this.nwcSettingsService.clearCache === "function"
|
||||
) {
|
||||
this.nwcSettingsService.clearCache();
|
||||
}
|
||||
|
||||
if (typeof this.watchHistoryPreferenceUnsubscribe === "function") {
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
import {
|
||||
loadNwcSettings,
|
||||
saveNwcSettings,
|
||||
clearNwcSettings,
|
||||
createDefaultNwcSettings,
|
||||
} from "../nwcSettings.js";
|
||||
import { MAX_WALLET_DEFAULT_ZAP } from "../config.js";
|
||||
|
||||
const NWC_URI_SCHEME = "nostr+walletconnect://";
|
||||
|
||||
function isFunction(value) {
|
||||
return typeof value === "function";
|
||||
}
|
||||
|
||||
export default class NwcSettingsService {
|
||||
constructor({
|
||||
normalizeHexPubkey,
|
||||
getActivePubkey,
|
||||
payments = null,
|
||||
logger = console,
|
||||
notifyError,
|
||||
maxWalletDefaultZap = MAX_WALLET_DEFAULT_ZAP,
|
||||
loadSettings = loadNwcSettings,
|
||||
saveSettings = saveNwcSettings,
|
||||
clearSettings = clearNwcSettings,
|
||||
createDefaultSettings = createDefaultNwcSettings,
|
||||
} = {}) {
|
||||
this.normalizeHexPubkey = isFunction(normalizeHexPubkey)
|
||||
? normalizeHexPubkey
|
||||
: (value) => (typeof value === "string" ? value.trim() : null);
|
||||
this.getActivePubkey = isFunction(getActivePubkey)
|
||||
? getActivePubkey
|
||||
: () => null;
|
||||
this.payments = payments || null;
|
||||
this.logger = logger || console;
|
||||
this.notifyError = isFunction(notifyError) ? notifyError : null;
|
||||
this.maxWalletDefaultZap =
|
||||
Number.isFinite(maxWalletDefaultZap) && maxWalletDefaultZap > 0
|
||||
? maxWalletDefaultZap
|
||||
: MAX_WALLET_DEFAULT_ZAP;
|
||||
this.loadSettings = isFunction(loadSettings)
|
||||
? loadSettings
|
||||
: loadNwcSettings;
|
||||
this.saveSettings = isFunction(saveSettings)
|
||||
? saveSettings
|
||||
: saveNwcSettings;
|
||||
this.clearSettings = isFunction(clearSettings)
|
||||
? clearSettings
|
||||
: clearNwcSettings;
|
||||
this.createDefaultSettings = isFunction(createDefaultSettings)
|
||||
? createDefaultSettings
|
||||
: () => createDefaultNwcSettings();
|
||||
this.cache = new Map();
|
||||
}
|
||||
|
||||
cloneSettings(settings) {
|
||||
if (!settings || typeof settings !== "object") {
|
||||
return this.createDefaultNwcSettings();
|
||||
}
|
||||
return { ...settings };
|
||||
}
|
||||
|
||||
setPayments(payments) {
|
||||
this.payments = payments || null;
|
||||
}
|
||||
|
||||
setActivePubkeyGetter(fn) {
|
||||
if (isFunction(fn)) {
|
||||
this.getActivePubkey = fn;
|
||||
}
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
getNormalizedPubkey(pubkey) {
|
||||
try {
|
||||
return this.normalizeHexPubkey(pubkey);
|
||||
} catch (error) {
|
||||
if (this.logger && isFunction(this.logger.warn)) {
|
||||
this.logger.warn("[nwcSettings] Failed to normalize pubkey:", error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async hydrateNwcSettingsForPubkey(pubkey) {
|
||||
const normalized = this.getNormalizedPubkey(pubkey);
|
||||
if (!normalized) {
|
||||
return this.createDefaultNwcSettings();
|
||||
}
|
||||
|
||||
try {
|
||||
const settings = await this.loadSettings(normalized);
|
||||
const record =
|
||||
settings && typeof settings === "object"
|
||||
? { ...settings }
|
||||
: this.createDefaultNwcSettings();
|
||||
this.cache.set(normalized, record);
|
||||
return this.cloneSettings(record);
|
||||
} catch (error) {
|
||||
if (this.logger && isFunction(this.logger.warn)) {
|
||||
this.logger.warn(
|
||||
`[nwcSettings] Failed to load settings for ${normalized}:`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
const fallback = this.createDefaultNwcSettings();
|
||||
this.cache.set(normalized, fallback);
|
||||
return this.cloneSettings(fallback);
|
||||
}
|
||||
}
|
||||
|
||||
getActiveNwcSettings() {
|
||||
const normalized = this.getNormalizedPubkey(this.getActivePubkey());
|
||||
if (!normalized) {
|
||||
return this.createDefaultNwcSettings();
|
||||
}
|
||||
const cached = this.cache.get(normalized);
|
||||
return cached ? this.cloneSettings(cached) : this.createDefaultNwcSettings();
|
||||
}
|
||||
|
||||
createDefaultNwcSettings() {
|
||||
return this.createDefaultSettings();
|
||||
}
|
||||
|
||||
hasActiveWalletConnection() {
|
||||
const settings = this.getActiveNwcSettings();
|
||||
const candidate =
|
||||
typeof settings?.nwcUri === "string" ? settings.nwcUri.trim() : "";
|
||||
return candidate.length > 0;
|
||||
}
|
||||
|
||||
validateWalletUri(uri, { requireValue = false } = {}) {
|
||||
const value = typeof uri === "string" ? uri.trim() : "";
|
||||
|
||||
if (!value) {
|
||||
if (requireValue) {
|
||||
return {
|
||||
valid: false,
|
||||
sanitized: "",
|
||||
message: "Enter a wallet connect URI before continuing.",
|
||||
};
|
||||
}
|
||||
return { valid: true, sanitized: "" };
|
||||
}
|
||||
|
||||
if (!value.toLowerCase().startsWith(NWC_URI_SCHEME)) {
|
||||
return {
|
||||
valid: false,
|
||||
sanitized: value,
|
||||
message: `Wallet URI must start with ${NWC_URI_SCHEME}.`,
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true, sanitized: value };
|
||||
}
|
||||
|
||||
async updateActiveNwcSettings(partial = {}) {
|
||||
const normalized = this.getNormalizedPubkey(this.getActivePubkey());
|
||||
if (!normalized) {
|
||||
if (this.logger && isFunction(this.logger.warn)) {
|
||||
this.logger.warn(
|
||||
"[nwcSettings] Cannot update settings without an active pubkey.",
|
||||
);
|
||||
}
|
||||
return this.createDefaultNwcSettings();
|
||||
}
|
||||
|
||||
try {
|
||||
const updated = await this.saveSettings(normalized, partial);
|
||||
const record =
|
||||
updated && typeof updated === "object"
|
||||
? { ...updated }
|
||||
: this.createDefaultNwcSettings();
|
||||
this.cache.set(normalized, record);
|
||||
return this.cloneSettings(record);
|
||||
} catch (error) {
|
||||
if (this.logger && isFunction(this.logger.warn)) {
|
||||
this.logger.warn(
|
||||
`[nwcSettings] Failed to save settings for ${normalized}:`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
return this.getActiveNwcSettings();
|
||||
}
|
||||
}
|
||||
|
||||
async handleProfileWalletPersist({
|
||||
nwcUri,
|
||||
defaultZap,
|
||||
lastChecked,
|
||||
} = {}) {
|
||||
const partial = {};
|
||||
if (nwcUri !== undefined) {
|
||||
partial.nwcUri = nwcUri;
|
||||
}
|
||||
if (defaultZap !== undefined) {
|
||||
partial.defaultZap = defaultZap;
|
||||
}
|
||||
if (lastChecked !== undefined) {
|
||||
partial.lastChecked = lastChecked;
|
||||
}
|
||||
|
||||
if (!Object.keys(partial).length) {
|
||||
return this.getActiveNwcSettings();
|
||||
}
|
||||
|
||||
return this.updateActiveNwcSettings(partial);
|
||||
}
|
||||
|
||||
async ensureWallet({ nwcUri, defaultZap } = {}) {
|
||||
const activeSettings = this.getActiveNwcSettings();
|
||||
const candidateUri =
|
||||
typeof nwcUri === "string" && nwcUri.trim()
|
||||
? nwcUri.trim()
|
||||
: activeSettings.nwcUri || "";
|
||||
const { valid, sanitized, message } = this.validateWalletUri(candidateUri, {
|
||||
requireValue: true,
|
||||
});
|
||||
if (!valid) {
|
||||
throw new Error(message || "Invalid wallet URI provided.");
|
||||
}
|
||||
|
||||
const merged = {
|
||||
...activeSettings,
|
||||
nwcUri: sanitized,
|
||||
};
|
||||
|
||||
if (typeof defaultZap === "number" && Number.isFinite(defaultZap)) {
|
||||
const rounded = Math.max(0, Math.round(defaultZap));
|
||||
merged.defaultZap = Math.min(this.maxWalletDefaultZap, rounded);
|
||||
} else if (defaultZap === null) {
|
||||
merged.defaultZap = null;
|
||||
}
|
||||
|
||||
if (this.payments && isFunction(this.payments.ensureWallet)) {
|
||||
return this.payments.ensureWallet({ settings: merged });
|
||||
}
|
||||
|
||||
if (this.logger && isFunction(this.logger.warn)) {
|
||||
this.logger.warn(
|
||||
"[wallet] Falling back to stub ensureWallet implementation. Returning settings without performing a connection test.",
|
||||
);
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
async clearStoredNwcSettings(pubkey, { silent = false } = {}) {
|
||||
const normalized = this.getNormalizedPubkey(pubkey);
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.clearSettings(normalized);
|
||||
} catch (error) {
|
||||
if (this.logger && isFunction(this.logger.warn)) {
|
||||
this.logger.warn(
|
||||
`[nwcSettings] Failed to clear settings for ${normalized}:`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
if (!silent && this.notifyError) {
|
||||
this.notifyError("Failed to clear wallet settings for this account.");
|
||||
}
|
||||
try {
|
||||
await this.saveSettings(normalized, this.createDefaultNwcSettings());
|
||||
} catch (persistError) {
|
||||
if (this.logger && isFunction(this.logger.warn)) {
|
||||
this.logger.warn(
|
||||
`[nwcSettings] Failed to overwrite settings for ${normalized}:`,
|
||||
persistError,
|
||||
);
|
||||
}
|
||||
}
|
||||
this.cache.delete(normalized);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.cache.delete(normalized);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onLogin({ pubkey, previousPubkey, identityChanged } = {}) {
|
||||
if (identityChanged) {
|
||||
this.onIdentityChanged();
|
||||
}
|
||||
|
||||
const normalizedActive = this.getNormalizedPubkey(
|
||||
pubkey !== undefined ? pubkey : this.getActivePubkey(),
|
||||
);
|
||||
const normalizedPrevious = this.getNormalizedPubkey(previousPubkey);
|
||||
|
||||
if (
|
||||
normalizedPrevious &&
|
||||
(!normalizedActive || normalizedPrevious !== normalizedActive)
|
||||
) {
|
||||
await this.clearStoredNwcSettings(normalizedPrevious, { silent: true });
|
||||
}
|
||||
|
||||
if (normalizedActive) {
|
||||
await this.hydrateNwcSettingsForPubkey(normalizedActive);
|
||||
}
|
||||
|
||||
return this.getActiveNwcSettings();
|
||||
}
|
||||
|
||||
async onLogout({ pubkey, previousPubkey } = {}) {
|
||||
const normalizedPrevious = this.getNormalizedPubkey(
|
||||
previousPubkey !== undefined ? previousPubkey : pubkey,
|
||||
);
|
||||
if (normalizedPrevious) {
|
||||
await this.clearStoredNwcSettings(normalizedPrevious, { silent: false });
|
||||
}
|
||||
this.clearCache();
|
||||
return this.createDefaultNwcSettings();
|
||||
}
|
||||
|
||||
onIdentityChanged() {
|
||||
this.clearCache();
|
||||
}
|
||||
}
|
||||
+118
-66
@@ -144,52 +144,115 @@ const SERVICE_CONTRACT = [
|
||||
fallback: () => () => null,
|
||||
},
|
||||
{
|
||||
key: "getActiveNwcSettings",
|
||||
type: "function",
|
||||
key: "nwcSettings",
|
||||
type: "object",
|
||||
description:
|
||||
"Reads the active Nostr Wallet Connect settings (uri/default zap) for the logged in profile.",
|
||||
fallback: ({ internalState }) => () => {
|
||||
const current = ensureInternalWalletSettings(internalState);
|
||||
return { ...current };
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "updateActiveNwcSettings",
|
||||
type: "function",
|
||||
description: "Persists updates to the active wallet settings and returns the new state.",
|
||||
fallback: ({ internalState }) => (partial = {}) => {
|
||||
const current = ensureInternalWalletSettings(internalState);
|
||||
const next = {
|
||||
...current,
|
||||
...(partial && typeof partial === "object" ? partial : {}),
|
||||
"Service that manages Nostr Wallet Connect settings and interactions for the active profile.",
|
||||
fallback: ({ internalState }) => {
|
||||
const ensure = () => ensureInternalWalletSettings(internalState);
|
||||
const service = {};
|
||||
|
||||
service.createDefaultNwcSettings = () => createInternalDefaultNwcSettings();
|
||||
|
||||
service.getActiveNwcSettings = () => {
|
||||
const current = ensure();
|
||||
return { ...current };
|
||||
};
|
||||
internalState.walletSettings = next;
|
||||
return { ...next };
|
||||
|
||||
service.updateActiveNwcSettings = (partial = {}) => {
|
||||
const current = ensure();
|
||||
const next = {
|
||||
...current,
|
||||
...(partial && typeof partial === "object" ? partial : {}),
|
||||
};
|
||||
internalState.walletSettings = next;
|
||||
return { ...next };
|
||||
};
|
||||
|
||||
service.hydrateNwcSettingsForPubkey = async () => {
|
||||
return service.getActiveNwcSettings();
|
||||
};
|
||||
|
||||
service.handleProfileWalletPersist = async (options = {}) => {
|
||||
const { nwcUri, defaultZap, lastChecked } =
|
||||
options && typeof options === "object" ? options : {};
|
||||
const partial = {};
|
||||
if (nwcUri !== undefined) {
|
||||
partial.nwcUri = nwcUri;
|
||||
}
|
||||
if (defaultZap !== undefined) {
|
||||
partial.defaultZap = defaultZap;
|
||||
}
|
||||
if (lastChecked !== undefined) {
|
||||
partial.lastChecked = lastChecked;
|
||||
}
|
||||
if (!Object.keys(partial).length) {
|
||||
return service.getActiveNwcSettings();
|
||||
}
|
||||
return service.updateActiveNwcSettings(partial);
|
||||
};
|
||||
|
||||
service.hasActiveWalletConnection = () => {
|
||||
const settings = ensure();
|
||||
const candidate =
|
||||
typeof settings?.nwcUri === "string" ? settings.nwcUri.trim() : "";
|
||||
return candidate.length > 0;
|
||||
};
|
||||
|
||||
service.validateWalletUri = (uri, { requireValue = false } = {}) => {
|
||||
const value = typeof uri === "string" ? uri.trim() : "";
|
||||
|
||||
if (!value) {
|
||||
if (requireValue) {
|
||||
return {
|
||||
valid: false,
|
||||
sanitized: "",
|
||||
message: "Enter a wallet connect URI before continuing.",
|
||||
};
|
||||
}
|
||||
return { valid: true, sanitized: "" };
|
||||
}
|
||||
|
||||
if (!value.toLowerCase().startsWith(NWC_URI_SCHEME)) {
|
||||
return {
|
||||
valid: false,
|
||||
sanitized: value,
|
||||
message: `Wallet URI must start with ${NWC_URI_SCHEME}.`,
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true, sanitized: value };
|
||||
};
|
||||
|
||||
service.ensureWallet = async ({ nwcUri } = {}) => {
|
||||
const settings = service.getActiveNwcSettings();
|
||||
const candidate =
|
||||
typeof nwcUri === "string" && nwcUri.trim()
|
||||
? nwcUri.trim()
|
||||
: settings.nwcUri || "";
|
||||
const { valid, sanitized, message } = service.validateWalletUri(
|
||||
candidate,
|
||||
{ requireValue: true },
|
||||
);
|
||||
if (!valid) {
|
||||
throw new Error(message || "Invalid wallet URI provided.");
|
||||
}
|
||||
return { ...settings, nwcUri: sanitized };
|
||||
};
|
||||
|
||||
service.clearStoredNwcSettings = async () => true;
|
||||
|
||||
service.onLogin = async () => service.getActiveNwcSettings();
|
||||
|
||||
service.onLogout = async () => service.createDefaultNwcSettings();
|
||||
|
||||
service.onIdentityChanged = () => {
|
||||
internalState.walletSettings = createInternalDefaultNwcSettings();
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "hydrateNwcSettingsForPubkey",
|
||||
type: "function",
|
||||
description:
|
||||
"Ensures wallet settings are loaded for a given pubkey before the pane renders.",
|
||||
fallback: ({ internalState }) => async () => {
|
||||
const current = ensureInternalWalletSettings(internalState);
|
||||
return { ...current };
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "createDefaultNwcSettings",
|
||||
type: "function",
|
||||
description: "Creates a baseline wallet settings object when none exists.",
|
||||
fallback: () => () => createInternalDefaultNwcSettings(),
|
||||
},
|
||||
{
|
||||
key: "ensureWallet",
|
||||
type: "function",
|
||||
description:
|
||||
"Guarantees a wallet connection exists before issuing wallet related operations (connect/test).",
|
||||
fallback: () => async () => ({ ok: false, reason: "not-implemented" }),
|
||||
},
|
||||
{
|
||||
key: "loadVideos",
|
||||
type: "function",
|
||||
@@ -2113,9 +2176,9 @@ export class ProfileModalController {
|
||||
return;
|
||||
}
|
||||
|
||||
let settings = this.services.getActiveNwcSettings();
|
||||
let settings = this.services.nwcSettings.getActiveNwcSettings();
|
||||
if (!settings || typeof settings !== "object") {
|
||||
settings = this.services.createDefaultNwcSettings();
|
||||
settings = this.services.nwcSettings.createDefaultNwcSettings();
|
||||
}
|
||||
setInputValue(this.walletUriInput, settings.nwcUri || "");
|
||||
setInputValue(
|
||||
@@ -2367,9 +2430,9 @@ export class ProfileModalController {
|
||||
context.success = true;
|
||||
context.reason = "tested";
|
||||
|
||||
let currentSettings = this.services.getActiveNwcSettings();
|
||||
let currentSettings = this.services.nwcSettings.getActiveNwcSettings();
|
||||
if (!currentSettings || typeof currentSettings !== "object") {
|
||||
currentSettings = this.services.createDefaultNwcSettings();
|
||||
currentSettings = this.services.nwcSettings.createDefaultNwcSettings();
|
||||
}
|
||||
if (currentSettings.nwcUri === sanitized) {
|
||||
await this.persistWalletSettings({
|
||||
@@ -3412,22 +3475,11 @@ export class ProfileModalController {
|
||||
}
|
||||
}
|
||||
|
||||
const partial = {};
|
||||
if (nwcUri !== undefined) {
|
||||
partial.nwcUri = nwcUri;
|
||||
}
|
||||
if (defaultZap !== undefined) {
|
||||
partial.defaultZap = defaultZap;
|
||||
}
|
||||
if (lastChecked !== undefined) {
|
||||
partial.lastChecked = lastChecked;
|
||||
}
|
||||
|
||||
if (!Object.keys(partial).length) {
|
||||
return this.services.getActiveNwcSettings();
|
||||
}
|
||||
|
||||
return this.services.updateActiveNwcSettings(partial);
|
||||
return this.services.nwcSettings.handleProfileWalletPersist({
|
||||
nwcUri,
|
||||
defaultZap,
|
||||
lastChecked,
|
||||
});
|
||||
}
|
||||
|
||||
async testWalletConnection({ nwcUri, defaultZap, activePubkey } = {}) {
|
||||
@@ -3444,7 +3496,7 @@ export class ProfileModalController {
|
||||
}
|
||||
}
|
||||
|
||||
return this.services.ensureWallet({ nwcUri, defaultZap });
|
||||
return this.services.nwcSettings.ensureWallet({ nwcUri, defaultZap });
|
||||
}
|
||||
|
||||
async disconnectWallet({ activePubkey } = {}) {
|
||||
@@ -3456,8 +3508,8 @@ export class ProfileModalController {
|
||||
}
|
||||
}
|
||||
|
||||
return this.services.updateActiveNwcSettings(
|
||||
this.services.createDefaultNwcSettings(),
|
||||
return this.services.nwcSettings.updateActiveNwcSettings(
|
||||
this.services.nwcSettings.createDefaultNwcSettings(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3935,7 +3987,7 @@ export class ProfileModalController {
|
||||
}
|
||||
|
||||
async hydrateActiveWalletSettings(pubkey) {
|
||||
const hydrate = this.services.hydrateNwcSettingsForPubkey;
|
||||
const hydrate = this.services.nwcSettings?.hydrateNwcSettingsForPubkey;
|
||||
if (typeof hydrate !== "function") {
|
||||
return null;
|
||||
}
|
||||
|
||||
+26
-8
@@ -29,6 +29,7 @@ export default class ZapController {
|
||||
constructor({
|
||||
videoModal,
|
||||
getCurrentVideo,
|
||||
nwcSettings,
|
||||
getActiveNwcSettings,
|
||||
isUserLoggedIn,
|
||||
hasActiveWalletConnection,
|
||||
@@ -40,16 +41,33 @@ export default class ZapController {
|
||||
this.videoModal = videoModal || null;
|
||||
this.getCurrentVideo =
|
||||
typeof getCurrentVideo === "function" ? getCurrentVideo : () => null;
|
||||
this.getActiveNwcSettings =
|
||||
typeof getActiveNwcSettings === "function"
|
||||
? getActiveNwcSettings
|
||||
: () => ({});
|
||||
this.nwcSettings =
|
||||
nwcSettings &&
|
||||
typeof nwcSettings === "object" &&
|
||||
typeof nwcSettings.getActiveNwcSettings === "function"
|
||||
? nwcSettings
|
||||
: null;
|
||||
if (this.nwcSettings) {
|
||||
this.getActiveNwcSettings = () =>
|
||||
this.nwcSettings.getActiveNwcSettings();
|
||||
} else if (typeof getActiveNwcSettings === "function") {
|
||||
this.getActiveNwcSettings = getActiveNwcSettings;
|
||||
} else {
|
||||
this.getActiveNwcSettings = () => ({});
|
||||
}
|
||||
this.isUserLoggedIn =
|
||||
typeof isUserLoggedIn === "function" ? isUserLoggedIn : () => false;
|
||||
this.hasActiveWalletConnection =
|
||||
typeof hasActiveWalletConnection === "function"
|
||||
? hasActiveWalletConnection
|
||||
: () => false;
|
||||
if (
|
||||
this.nwcSettings &&
|
||||
typeof this.nwcSettings.hasActiveWalletConnection === "function"
|
||||
) {
|
||||
this.hasActiveWalletConnection = () =>
|
||||
this.nwcSettings.hasActiveWalletConnection();
|
||||
} else if (typeof hasActiveWalletConnection === "function") {
|
||||
this.hasActiveWalletConnection = hasActiveWalletConnection;
|
||||
} else {
|
||||
this.hasActiveWalletConnection = () => false;
|
||||
}
|
||||
this.splitAndZap =
|
||||
typeof splitAndZap === "function" ? splitAndZap : () => Promise.resolve();
|
||||
this.payments = payments || {};
|
||||
|
||||
@@ -349,7 +349,7 @@ await (async () => {
|
||||
const pubkeyHex = "b".repeat(64);
|
||||
app.pubkey = pubkeyHex;
|
||||
const normalized = app.normalizeHexPubkey(pubkeyHex);
|
||||
app.nwcSettings.set(normalized, {
|
||||
app.nwcSettingsService.cache.set(normalized, {
|
||||
nwcUri: "nostr+walletconnect://example",
|
||||
defaultZap: null,
|
||||
lastChecked: null,
|
||||
|
||||
Reference in new issue
Block a user