mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
signAndPublishEvent (video publish/edit/repost/mirror), rebroadcastEvent, and
revertVideo now use assertAnyRelayAcceptedOrUnconfirmed: when every relay
times out without rejecting, the event was sent and almost always persisted,
so it no longer surfaces as "Failed to share video". Explicit rejections
still throw.
Messaging stays honest at every consumer:
- relay tally carries `unconfirmed` (now attached on edits too);
describePublishOutcome maps 0-accepted+unconfirmed to a WARNING ("sent,
no relay confirmed yet") — never a fake relay count, never a hard error
- edit/revert controllers say "sent, not confirmed yet" when unconfirmed
- deletes stay CONSERVATIVE: "deleted successfully" only when >=1 relay
ACKed a tombstone or kind-5 (anyRelayAcceptedInSummaries); an unconfirmed
revert no longer aborts deleteAllVersions midway, so the kind-5 always runs
Tests: tests/relay-ack-unconfirmed-content.test.mjs (6).
Co-Authored-By: Claude Fable 5 <[email protected]>
133 lines
3.8 KiB
JavaScript
133 lines
3.8 KiB
JavaScript
import { devLogger } from "../utils/logger.js";
|
|
|
|
export default class RevertModalController {
|
|
constructor({
|
|
revertModal,
|
|
services = {},
|
|
state = {},
|
|
ui = {},
|
|
callbacks = {},
|
|
helpers = {},
|
|
}) {
|
|
this.revertModal = revertModal;
|
|
this.services = services;
|
|
this.state = state;
|
|
this.ui = ui;
|
|
this.callbacks = callbacks;
|
|
this.helpers = helpers;
|
|
}
|
|
|
|
async open(target) {
|
|
try {
|
|
const normalizedTarget = this.helpers.normalizeActionTarget(target);
|
|
const { triggerElement } = normalizedTarget;
|
|
|
|
const activeVideos = await this.services.nostrService.fetchVideos({
|
|
blacklistedEventIds: this.state.getBlacklistedEventIds(),
|
|
isAuthorBlocked: (pubkey) => this.callbacks.isAuthorBlocked(pubkey),
|
|
});
|
|
|
|
const video = await this.helpers.resolveVideoActionTarget({
|
|
...normalizedTarget,
|
|
preloadedList: activeVideos,
|
|
});
|
|
|
|
const pubkey = this.state.getPubkey();
|
|
if (!pubkey) {
|
|
this.ui.showError("Please login to revert.");
|
|
return;
|
|
}
|
|
|
|
const userPubkey = (pubkey || "").toLowerCase();
|
|
const videoPubkey = (video?.pubkey || "").toLowerCase();
|
|
if (!video || !videoPubkey || videoPubkey !== userPubkey) {
|
|
this.ui.showError("You do not own this video.");
|
|
return;
|
|
}
|
|
|
|
if (!this.revertModal) {
|
|
this.ui.showError("Revert modal is not available right now.");
|
|
return;
|
|
}
|
|
|
|
const loaded = await this.revertModal.load();
|
|
if (!loaded) {
|
|
this.ui.showError("Revert modal is not available right now.");
|
|
return;
|
|
}
|
|
|
|
const history = await this.services.nostrClient.hydrateVideoHistory(video);
|
|
|
|
this.revertModal.setHistory(video, history);
|
|
this.revertModal.open({ video }, { triggerElement });
|
|
} catch (err) {
|
|
devLogger.error("Failed to revert video:", err);
|
|
this.ui.showError("Failed to load revision history. Please try again.");
|
|
}
|
|
}
|
|
|
|
async handleConfirm(event) {
|
|
const detail = event?.detail || {};
|
|
const target = detail.target;
|
|
const entries = Array.isArray(detail.entries)
|
|
? detail.entries.slice()
|
|
: [];
|
|
|
|
if (!target || !entries.length) {
|
|
return;
|
|
}
|
|
|
|
const pubkey = this.state.getPubkey();
|
|
if (!pubkey) {
|
|
this.ui.showError("Please login to revert.");
|
|
return;
|
|
}
|
|
|
|
if (!this.revertModal) {
|
|
this.ui.showError("Revert modal is not available right now.");
|
|
return;
|
|
}
|
|
|
|
this.revertModal.setBusy(true, "Reverting…");
|
|
|
|
try {
|
|
// #49: revertVideo no longer hard-fails when every relay merely times
|
|
// out (summary.unconfirmed) — track whether ANY relay actually ACKed so
|
|
// the success message stays honest.
|
|
let anyAccepted = false;
|
|
for (const entry of entries) {
|
|
const revertResult = await this.services.nostrClient.revertVideo(
|
|
{
|
|
id: entry.id,
|
|
pubkey: entry.pubkey,
|
|
tags: entry.tags,
|
|
},
|
|
pubkey,
|
|
);
|
|
const accepted = revertResult?.summary?.accepted;
|
|
if (Array.isArray(accepted) && accepted.length > 0) {
|
|
anyAccepted = true;
|
|
}
|
|
}
|
|
|
|
await this.callbacks.loadVideos();
|
|
|
|
const timestampLabel = this.helpers.formatAbsoluteTimestamp(target.created_at);
|
|
if (anyAccepted) {
|
|
this.ui.showSuccess(`Reverted to revision from ${timestampLabel}.`);
|
|
} else {
|
|
this.ui.showSuccess(
|
|
`Revert to the revision from ${timestampLabel} was sent, but no relay has confirmed it yet — check again in a moment.`,
|
|
);
|
|
}
|
|
this.revertModal.close();
|
|
this.callbacks.forceRefreshAllProfiles();
|
|
} catch (err) {
|
|
devLogger.error("Failed to revert video:", err);
|
|
this.ui.showError("Failed to revert video. Please try again.");
|
|
} finally {
|
|
this.revertModal.setBusy(false);
|
|
}
|
|
}
|
|
}
|