mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Defer block list permission prompts during login
This commit is contained in:
1 parent
deec2cd573
commit
7ef420e1e2
3 files changed
+133
-33
No files matched your search
@@ -31,7 +31,7 @@ import { collectVideoTags } from "./utils/videoTags.js";
|
||||
import { normalizeHashtag } from "./utils/hashtagNormalization.js";
|
||||
import { sanitizeProfileMediaUrl } from "./utils/profileMedia.js";
|
||||
import { ADMIN_INITIAL_EVENT_BLACKLIST } from "./lists.js";
|
||||
import { userBlocks } from "./userBlocks.js";
|
||||
import { userBlocks, USER_BLOCK_EVENTS } from "./userBlocks.js";
|
||||
import { relayManager } from "./relayManager.js";
|
||||
import {
|
||||
createFeedEngine,
|
||||
@@ -2134,6 +2134,9 @@ class Application {
|
||||
case "subscriptions-permission-required":
|
||||
this.capturePermissionPromptRequirement("subscriptions");
|
||||
break;
|
||||
case "user-blocklist-permission-required":
|
||||
this.capturePermissionPromptRequirement("user-blocks");
|
||||
break;
|
||||
case "hashtag-preferences-permission-required":
|
||||
this.capturePermissionPromptRequirement("hashtag-preferences");
|
||||
break;
|
||||
@@ -2145,10 +2148,23 @@ class Application {
|
||||
resolvePermissionPromptMessage() {
|
||||
const needsSubscriptions = this.permissionPromptPending.has("subscriptions");
|
||||
const needsHashtags = this.permissionPromptPending.has("hashtag-preferences");
|
||||
const needsBlocks = this.permissionPromptPending.has("user-blocks");
|
||||
|
||||
if (needsBlocks && needsSubscriptions && needsHashtags) {
|
||||
return "Unlock private lists and enable permissions to load your subscriptions and hashtag preferences.";
|
||||
}
|
||||
if (needsBlocks && needsSubscriptions) {
|
||||
return "Unlock private lists and enable permissions to load your subscriptions.";
|
||||
}
|
||||
if (needsBlocks && needsHashtags) {
|
||||
return "Unlock private lists and enable permissions to load your hashtag preferences.";
|
||||
}
|
||||
if (needsSubscriptions && needsHashtags) {
|
||||
return "Enable permissions to load your subscriptions and hashtag preferences.";
|
||||
}
|
||||
if (needsBlocks) {
|
||||
return "Unlock private lists to load your block list.";
|
||||
}
|
||||
if (needsSubscriptions) {
|
||||
return "Enable permissions to load your subscriptions.";
|
||||
}
|
||||
@@ -2599,6 +2615,14 @@ class Application {
|
||||
});
|
||||
}
|
||||
|
||||
if (userBlocks && typeof userBlocks.on === "function") {
|
||||
userBlocks.on(USER_BLOCK_EVENTS.STATUS, (detail) => {
|
||||
if (detail?.status === "permission-required") {
|
||||
this.capturePermissionPromptRequirement("user-blocks");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.appChromeController) {
|
||||
this.appChromeController.initialize();
|
||||
return;
|
||||
@@ -4134,8 +4158,9 @@ class Application {
|
||||
|
||||
const needsSubscriptions = this.permissionPromptPending.has("subscriptions");
|
||||
const needsHashtags = this.permissionPromptPending.has("hashtag-preferences");
|
||||
const needsBlocks = this.permissionPromptPending.has("user-blocks");
|
||||
|
||||
if (!needsSubscriptions && !needsHashtags) {
|
||||
if (!needsSubscriptions && !needsHashtags && !needsBlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4175,6 +4200,32 @@ class Application {
|
||||
tasks.push(subscriptionTask);
|
||||
}
|
||||
|
||||
if (needsBlocks && userBlocks?.loadBlocks) {
|
||||
let permissionRequired = false;
|
||||
const blocksTask = userBlocks
|
||||
.loadBlocks(activePubkey, {
|
||||
allowPermissionPrompt: true,
|
||||
statusCallback: (detail) => {
|
||||
if (detail?.status === "permission-required") {
|
||||
permissionRequired = true;
|
||||
}
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
if (!permissionRequired) {
|
||||
this.permissionPromptPending.delete("user-blocks");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
devLogger.warn(
|
||||
"[Application] Failed to refresh block list after permission prompt:",
|
||||
error,
|
||||
);
|
||||
this.capturePermissionPromptFromError(error);
|
||||
});
|
||||
tasks.push(blocksTask);
|
||||
}
|
||||
|
||||
if (needsHashtags && this.hashtagPreferences?.load) {
|
||||
const hashtagTask = this.hashtagPreferences
|
||||
.load(activePubkey, { allowPermissionPrompt: true })
|
||||
@@ -4361,13 +4412,15 @@ class Application {
|
||||
const tasks = [];
|
||||
if (activePubkey && typeof this.authService.loadBlocksForPubkey === "function") {
|
||||
tasks.push(
|
||||
this.authService.loadBlocksForPubkey(activePubkey).catch((error) => {
|
||||
devLogger.warn(
|
||||
"[Application] Failed to load blocks during login:",
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}),
|
||||
this.authService
|
||||
.loadBlocksForPubkey(activePubkey, { allowPermissionPrompt: false })
|
||||
.catch((error) => {
|
||||
devLogger.warn(
|
||||
"[Application] Failed to load blocks during login:",
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -778,14 +778,18 @@ export default class AuthService {
|
||||
return { detail, completionPromise };
|
||||
}
|
||||
|
||||
createBlocksLoadOperation(activePubkey, schedule = (callback) => Promise.resolve().then(() => callback())) {
|
||||
createBlocksLoadOperation(
|
||||
activePubkey,
|
||||
schedule = (callback) => Promise.resolve().then(() => callback()),
|
||||
options = {},
|
||||
) {
|
||||
if (!this.userBlocks || typeof this.userBlocks.loadBlocks !== "function") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
name: "blocksLoaded",
|
||||
promise: schedule(() => this.userBlocks.loadBlocks(activePubkey)),
|
||||
promise: schedule(() => this.userBlocks.loadBlocks(activePubkey, options)),
|
||||
onFulfilled: () => true,
|
||||
onRejected: (error) => {
|
||||
this.log("[AuthService] Failed to load block list", error);
|
||||
@@ -801,14 +805,14 @@ export default class AuthService {
|
||||
};
|
||||
}
|
||||
|
||||
async loadBlocksForPubkey(pubkey) {
|
||||
async loadBlocksForPubkey(pubkey, options = {}) {
|
||||
const normalized = this.normalizeHexPubkey(pubkey) ||
|
||||
(typeof pubkey === "string" ? pubkey.trim() : "");
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const operation = this.createBlocksLoadOperation(normalized);
|
||||
const operation = this.createBlocksLoadOperation(normalized, undefined, options);
|
||||
if (!operation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+63
-20
@@ -80,6 +80,7 @@ const FAST_BLOCKLIST_RELAY_LIMIT = 3;
|
||||
const FAST_BLOCKLIST_TIMEOUT_MS = 2500;
|
||||
const BACKGROUND_BLOCKLIST_TIMEOUT_MS = 6000;
|
||||
const DECRYPT_TIMEOUT_MS = 15000;
|
||||
const BACKGROUND_DECRYPT_TIMEOUT_MS = 2500;
|
||||
const DECRYPT_RETRY_DELAY_MS = 10000;
|
||||
const MAX_BLOCKLIST_ENTRIES = 5000;
|
||||
|
||||
@@ -871,6 +872,15 @@ class UserBlockListManager {
|
||||
: null;
|
||||
const statusCallback =
|
||||
typeof options?.statusCallback === "function" ? options.statusCallback : null;
|
||||
const loadMode = options?.mode === "background" ? "background" : "interactive";
|
||||
const allowPermissionPrompt =
|
||||
options?.allowPermissionPrompt !== false && loadMode !== "background";
|
||||
const decryptTimeoutMs = Number.isFinite(options?.decryptTimeoutMs)
|
||||
? Math.max(0, Math.floor(options.decryptTimeoutMs))
|
||||
: allowPermissionPrompt
|
||||
? DECRYPT_TIMEOUT_MS
|
||||
: BACKGROUND_DECRYPT_TIMEOUT_MS;
|
||||
const nip07DecryptTimeoutMs = allowPermissionPrompt ? 12000 : 3000;
|
||||
|
||||
const emitStatus = (detail) => {
|
||||
if (!detail || typeof detail !== "object") {
|
||||
@@ -968,25 +978,34 @@ class UserBlockListManager {
|
||||
|
||||
let permissionError = null;
|
||||
if ((!signerHasNip44 && requiresNip44) || (!signerHasNip04 && requiresNip04)) {
|
||||
try {
|
||||
const permissionResult = await requestDefaultExtensionPermissions(
|
||||
DEFAULT_NIP07_ENCRYPTION_METHODS,
|
||||
if (!allowPermissionPrompt) {
|
||||
const error = new Error(
|
||||
"Encryption permissions are required to decrypt the block list.",
|
||||
);
|
||||
if (!permissionResult?.ok) {
|
||||
const error =
|
||||
permissionResult?.error instanceof Error
|
||||
? permissionResult.error
|
||||
: new Error(
|
||||
"Extension encryption permissions are required to use the browser decryptor.",
|
||||
);
|
||||
error.code = "user-blocklist-permission-required";
|
||||
permissionError = error;
|
||||
error.code = "user-blocklist-permission-required";
|
||||
error.permissionRequestDeferred = true;
|
||||
permissionError = error;
|
||||
} else {
|
||||
try {
|
||||
const permissionResult = await requestDefaultExtensionPermissions(
|
||||
DEFAULT_NIP07_ENCRYPTION_METHODS,
|
||||
);
|
||||
if (!permissionResult?.ok) {
|
||||
const error =
|
||||
permissionResult?.error instanceof Error
|
||||
? permissionResult.error
|
||||
: new Error(
|
||||
"Extension encryption permissions are required to use the browser decryptor.",
|
||||
);
|
||||
error.code = "user-blocklist-permission-required";
|
||||
permissionError = error;
|
||||
}
|
||||
} catch (error) {
|
||||
const wrapped =
|
||||
error instanceof Error ? error : new Error(String(error));
|
||||
wrapped.code = "user-blocklist-permission-required";
|
||||
permissionError = wrapped;
|
||||
}
|
||||
} catch (error) {
|
||||
const wrapped =
|
||||
error instanceof Error ? error : new Error(String(error));
|
||||
wrapped.code = "user-blocklist-permission-required";
|
||||
permissionError = wrapped;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1033,7 +1052,7 @@ class UserBlockListManager {
|
||||
const nostrApi = typeof window !== "undefined" ? window?.nostr : null;
|
||||
const decrypterOptions = {
|
||||
priority: NIP07_PRIORITY.HIGH,
|
||||
timeoutMs: 12000,
|
||||
timeoutMs: nip07DecryptTimeoutMs,
|
||||
retryMultiplier: 1,
|
||||
};
|
||||
|
||||
@@ -1094,6 +1113,10 @@ class UserBlockListManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (permissionError) {
|
||||
permissionError.signerStatus = signerStatus;
|
||||
}
|
||||
|
||||
return {
|
||||
decryptors,
|
||||
sources,
|
||||
@@ -1291,13 +1314,13 @@ class UserBlockListManager {
|
||||
setTimeout(
|
||||
() => {
|
||||
const timeoutError = new Error(
|
||||
`Decryption timed out after ${DECRYPT_TIMEOUT_MS / 1000}s`,
|
||||
`Decryption timed out after ${decryptTimeoutMs / 1000}s`,
|
||||
);
|
||||
timeoutError.code = "user-blocklist-decrypt-timeout";
|
||||
timeoutError.signerStatus = resolveSignerStatus();
|
||||
reject(timeoutError);
|
||||
},
|
||||
DECRYPT_TIMEOUT_MS,
|
||||
decryptTimeoutMs,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1314,6 +1337,26 @@ class UserBlockListManager {
|
||||
}
|
||||
|
||||
if (decryptionError) {
|
||||
if (decryptionError.code === "user-blocklist-permission-required") {
|
||||
const signerStatus =
|
||||
decryptionError.signerStatus || resolveSignerStatus();
|
||||
this.blockedPubkeys = previousState.blockedPubkeys;
|
||||
this.blockEventId = previousState.blockEventId;
|
||||
this.blockEventCreatedAt = previousState.blockEventCreatedAt;
|
||||
this.loaded = true;
|
||||
emitStatus({
|
||||
status: "permission-required",
|
||||
signerStatus,
|
||||
source,
|
||||
reason: "decryptor-permission",
|
||||
allowPermissionPrompt,
|
||||
mode: loadMode,
|
||||
events: [newestMute?.id, newestBlock?.id].filter(Boolean),
|
||||
error: decryptionError,
|
||||
});
|
||||
emitStatus({ status: "settled" });
|
||||
return;
|
||||
}
|
||||
if (decryptionError.code === "user-blocklist-decrypt-timeout") {
|
||||
const signerStatus =
|
||||
decryptionError.signerStatus || resolveSignerStatus();
|
||||
|
||||
Reference in new issue
Block a user