refactor(ai): extract handleRevertVideo from js/app.js (agent)

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-01-27 03:20:08 +00:00
1 parent 2fd0d5cab6
commit e5f24bf4b0
3 files changed
+281 -86

No files matched your search

+31 -86
View File
@@ -113,6 +113,7 @@ import {
} from "./ui/components/staticModalAccessibility.js";
import LoginModalController from "./ui/loginModalController.js";
import EditModalController from "./ui/editModalController.js";
import RevertModalController from "./ui/revertModalController.js";
import TagPreferenceMenuController from "./ui/tagPreferenceMenuController.js";
import ReactionController from "./ui/reactionController.js";
import { pointerArrayToKey } from "./utils/pointer.js";
@@ -420,6 +421,32 @@ class Application {
},
});
this.revertModalController = new RevertModalController({
revertModal: this.modalManager?.revertModal,
services: {
nostrService: this.nostrService,
nostrClient: nostrClient,
},
state: {
getPubkey: () => this.pubkey,
getBlacklistedEventIds: () => this.blacklistedEventIds,
},
ui: {
showError: (msg) => this.showError(msg),
showSuccess: (msg) => this.showSuccess(msg),
},
callbacks: {
loadVideos: () => this.loadVideos(),
forceRefreshAllProfiles: () => this.forceRefreshAllProfiles(),
isAuthorBlocked: (pubkey) => this.isAuthorBlocked(pubkey),
},
helpers: {
normalizeActionTarget: (t) => this.normalizeActionTarget(t),
resolveVideoActionTarget: (opts) => this.resolveVideoActionTarget(opts),
formatAbsoluteTimestamp: (ts) => this.formatAbsoluteTimestamp(ts),
},
});
this.handleShareNostrSignerChange = () => {
this.updateShareNostrAuthState({ reason: "signer-change" });
};
@@ -7817,96 +7844,14 @@ class Application {
}
async handleRevertVideo(target) {
try {
const normalizedTarget = this.normalizeActionTarget(target);
const { triggerElement } = normalizedTarget;
const activeVideos = await this.nostrService.fetchVideos({
blacklistedEventIds: this.blacklistedEventIds,
isAuthorBlocked: (pubkey) => this.isAuthorBlocked(pubkey),
});
const video = await this.resolveVideoActionTarget({
...normalizedTarget,
preloadedList: activeVideos,
});
if (!this.pubkey) {
this.showError("Please login to revert.");
return;
}
const userPubkey = (this.pubkey || "").toLowerCase();
const videoPubkey = (video?.pubkey || "").toLowerCase();
if (!video || !videoPubkey || videoPubkey !== userPubkey) {
this.showError("You do not own this video.");
return;
}
if (!this.revertModal) {
this.showError("Revert modal is not available right now.");
return;
}
const loaded = await this.revertModal.load();
if (!loaded) {
this.showError("Revert modal is not available right now.");
return;
}
const history = await nostrClient.hydrateVideoHistory(video);
this.revertModal.setHistory(video, history);
this.revertModal.open({ video }, { triggerElement });
} catch (err) {
devLogger.error("Failed to revert video:", err);
this.showError("Failed to load revision history. Please try again.");
if (this.revertModalController) {
return this.revertModalController.open(target);
}
}
async handleRevertModalConfirm(event) {
const detail = event?.detail || {};
const target = detail.target;
const entries = Array.isArray(detail.entries)
? detail.entries.slice()
: [];
if (!target || !entries.length) {
return;
}
if (!this.pubkey) {
this.showError("Please login to revert.");
return;
}
if (!this.revertModal) {
this.showError("Revert modal is not available right now.");
return;
}
this.revertModal.setBusy(true, "Reverting…");
try {
for (const entry of entries) {
await nostrClient.revertVideo(
{
id: entry.id,
pubkey: entry.pubkey,
tags: entry.tags,
},
this.pubkey
);
}
await this.loadVideos();
const timestampLabel = this.formatAbsoluteTimestamp(target.created_at);
this.showSuccess(`Reverted to revision from ${timestampLabel}.`);
this.revertModal.close();
this.forceRefreshAllProfiles();
} catch (err) {
devLogger.error("Failed to revert video:", err);
this.showError("Failed to revert video. Please try again.");
} finally {
this.revertModal.setBusy(false);
if (this.revertModalController) {
return this.revertModalController.handleConfirm(event);
}
}
+118
View File
@@ -0,0 +1,118 @@
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 {
for (const entry of entries) {
await this.services.nostrClient.revertVideo(
{
id: entry.id,
pubkey: entry.pubkey,
tags: entry.tags,
},
pubkey,
);
}
await this.callbacks.loadVideos();
const timestampLabel = this.helpers.formatAbsoluteTimestamp(target.created_at);
this.ui.showSuccess(`Reverted to revision from ${timestampLabel}.`);
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);
}
}
}
+132
View File
@@ -0,0 +1,132 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import RevertModalController from '../js/ui/revertModalController.js';
test('RevertModalController', async (t) => {
const mockVideo = { id: 'v1', pubkey: 'p1' };
const mockHistory = [{ id: 'v1' }, { id: 'v0' }];
const mocks = {
revertModal: {
load: async () => true,
setHistory: () => {},
open: () => {},
close: () => {},
setBusy: () => {},
},
services: {
nostrService: {
fetchVideos: async () => [mockVideo],
},
nostrClient: {
hydrateVideoHistory: async () => mockHistory,
revertVideo: async () => {},
},
},
state: {
getPubkey: () => 'p1', // must match video pubkey
getBlacklistedEventIds: () => new Set(),
},
ui: {
showError: () => {},
showSuccess: () => {},
},
callbacks: {
loadVideos: async () => {},
forceRefreshAllProfiles: () => {},
isAuthorBlocked: () => false,
},
helpers: {
normalizeActionTarget: () => ({ triggerElement: null }),
resolveVideoActionTarget: async () => mockVideo,
formatAbsoluteTimestamp: () => 'timestamp',
},
};
await t.test('open() fetches history and opens modal', async () => {
let historySet = null;
let modalOpened = false;
const controller = new RevertModalController({
...mocks,
revertModal: {
...mocks.revertModal,
setHistory: (_v, h) => {
historySet = h;
},
open: () => {
modalOpened = true;
},
},
});
await controller.open('v1');
assert.deepEqual(historySet, mockHistory, 'History should be set');
assert.equal(modalOpened, true, 'Modal should be opened');
});
await t.test('open() shows error if not logged in', async () => {
let errorMsg = '';
const controller = new RevertModalController({
...mocks,
state: { ...mocks.state, getPubkey: () => null },
ui: {
...mocks.ui,
showError: (m) => {
errorMsg = m;
},
},
});
await controller.open('v1');
assert.match(errorMsg, /login/, 'Should show login error');
});
await t.test('handleConfirm() calls revertVideo and refreshes', async () => {
let revertCalled = false;
let refreshCalled = false;
let modalClosed = false;
let modalBusyState = null;
const controller = new RevertModalController({
...mocks,
services: {
...mocks.services,
nostrClient: {
...mocks.services.nostrClient,
revertVideo: async () => {
revertCalled = true;
},
},
},
callbacks: {
...mocks.callbacks,
loadVideos: async () => {
refreshCalled = true;
},
},
revertModal: {
...mocks.revertModal,
close: () => {
modalClosed = true;
},
setBusy: (busy) => {
modalBusyState = busy;
},
},
});
await controller.handleConfirm({
detail: {
target: mockVideo,
entries: [{ id: 'v1', pubkey: 'p1', tags: [] }],
},
});
assert.equal(revertCalled, true, 'revertVideo should be called');
assert.equal(refreshCalled, true, 'loadVideos should be called');
assert.equal(modalClosed, true, 'Modal should be closed');
assert.equal(modalBusyState, false, 'Modal should not be busy at end');
});
});