feat: Refactor event handling in dashboard for improved modal interactions and event resolution

This commit is contained in:
JB
2025-10-11 06:33:47 -07:00
parent 3e7bbd648c
commit 5be4da1cce
2 changed files with 78 additions and 28 deletions
+42 -22
View File
@@ -2360,8 +2360,6 @@ def analyze_pending_job(pending_id: str) -> ResponseReturnValue:
raw_chunks = build_chunks_for_chapters(enabled_overrides, level=chunk_level_literal) raw_chunks = build_chunks_for_chapters(enabled_overrides, level=chunk_level_literal)
analysis_chunks = build_chunks_for_chapters(enabled_overrides, level="sentence") analysis_chunks = build_chunks_for_chapters(enabled_overrides, level="sentence")
analysis_chunks = build_chunks_for_chapters(enabled_overrides, level="sentence")
analysis_chunks = build_chunks_for_chapters(enabled_overrides, level="sentence")
existing_roster: Optional[Mapping[str, Any]] existing_roster: Optional[Mapping[str, Any]]
if getattr(pending, "analysis_requested", False): if getattr(pending, "analysis_requested", False):
@@ -2445,10 +2443,15 @@ def finalize_job(pending_id: str) -> ResponseReturnValue:
active_step="chapters", active_step="chapters",
) )
active_step = (request.form.get("active_step") or "chapters").strip().lower()
raw_chunks = build_chunks_for_chapters(enabled_overrides, level=chunk_level_literal) raw_chunks = build_chunks_for_chapters(enabled_overrides, level=chunk_level_literal)
analysis_chunks = build_chunks_for_chapters(enabled_overrides, level="sentence") analysis_chunks = build_chunks_for_chapters(enabled_overrides, level="sentence")
analysis_active = pending.speaker_mode == "multi" and getattr(pending, "analysis_requested", False) is_multi = pending.speaker_mode == "multi"
if analysis_active: analysis_requested = bool(getattr(pending, "analysis_requested", False))
should_force_speakers = is_multi and active_step != "speakers"
if analysis_requested:
existing_roster: Optional[Mapping[str, Any]] = pending.speakers existing_roster: Optional[Mapping[str, Any]] = pending.speakers
else: else:
narrator_only: Dict[str, Any] = {} narrator_only: Dict[str, Any] = {}
@@ -2457,8 +2460,10 @@ def finalize_job(pending_id: str) -> ResponseReturnValue:
if isinstance(narrator_payload, Mapping): if isinstance(narrator_payload, Mapping):
narrator_only["narrator"] = dict(narrator_payload) narrator_only["narrator"] = dict(narrator_payload)
existing_roster = narrator_only or None existing_roster = narrator_only or None
config_name = pending.applied_speaker_config or selected_config config_name = pending.applied_speaker_config or selected_config
speaker_config_payload = get_config(config_name) if config_name else None speaker_config_payload = get_config(config_name) if config_name else None
run_analysis = is_multi and (should_force_speakers or analysis_requested)
processed_chunks, roster, analysis_payload, config_languages, updated_config = _prepare_speaker_metadata( processed_chunks, roster, analysis_payload, config_languages, updated_config = _prepare_speaker_metadata(
chapters=enabled_overrides, chapters=enabled_overrides,
chunks=raw_chunks, chunks=raw_chunks,
@@ -2468,24 +2473,42 @@ def finalize_job(pending_id: str) -> ResponseReturnValue:
voice_profile=pending.voice_profile, voice_profile=pending.voice_profile,
threshold=pending.speaker_analysis_threshold, threshold=pending.speaker_analysis_threshold,
existing_roster=existing_roster, existing_roster=existing_roster,
run_analysis=analysis_active, run_analysis=run_analysis,
speaker_config=speaker_config_payload, speaker_config=speaker_config_payload,
apply_config=apply_config_requested or bool(speaker_config_payload), apply_config=apply_config_requested or bool(speaker_config_payload),
persist_config=persist_config_requested, persist_config=persist_config_requested,
) )
pending.chunks = processed_chunks pending.chunks = processed_chunks
pending.speakers = roster pending.speakers = roster
pending.speaker_analysis = analysis_payload if analysis_payload:
pending.speaker_analysis = analysis_payload
if run_analysis:
setattr(pending, "analysis_requested", True)
if config_languages: if config_languages:
pending.speaker_voice_languages = list(config_languages) pending.speaker_voice_languages = list(config_languages)
config_name = getattr(pending, "applied_speaker_config", None) config_key = getattr(pending, "applied_speaker_config", None)
if updated_config and isinstance(config_name, str) and config_name: if updated_config and isinstance(config_key, str) and config_key:
configs = load_configs() configs = load_configs()
configs[config_name] = updated_config configs[config_key] = updated_config
save_configs(configs) save_configs(configs)
total_characters = selected_total or pending.total_characters if selected_total:
pending.total_characters = selected_total
if should_force_speakers:
notice_message = "Review speaker assignments before queuing."
if persist_config_requested and config_key:
notice_message = "Configuration saved. Review speaker assignments before queuing."
service.store_pending_job(pending)
return _render_prepare_page(
pending,
notice=notice_message,
active_step="speakers",
)
total_characters = selected_total or pending.total_characters
service.pop_pending_job(pending_id) service.pop_pending_job(pending_id)
job = service.enqueue( job = service.enqueue(
@@ -2503,7 +2526,6 @@ def finalize_job(pending_id: str) -> ResponseReturnValue:
subtitle_format=pending.subtitle_format, subtitle_format=pending.subtitle_format,
total_characters=total_characters, total_characters=total_characters,
chapters=overrides, chapters=overrides,
metadata_tags=pending.metadata_tags,
save_chapters_separately=pending.save_chapters_separately, save_chapters_separately=pending.save_chapters_separately,
merge_chapters_at_end=pending.merge_chapters_at_end, merge_chapters_at_end=pending.merge_chapters_at_end,
separate_chapters_format=pending.separate_chapters_format, separate_chapters_format=pending.separate_chapters_format,
@@ -2511,6 +2533,7 @@ def finalize_job(pending_id: str) -> ResponseReturnValue:
save_as_project=pending.save_as_project, save_as_project=pending.save_as_project,
voice_profile=pending.voice_profile, voice_profile=pending.voice_profile,
max_subtitle_words=pending.max_subtitle_words, max_subtitle_words=pending.max_subtitle_words,
metadata_tags=pending.metadata_tags,
cover_image_path=pending.cover_image_path, cover_image_path=pending.cover_image_path,
cover_image_mime=pending.cover_image_mime, cover_image_mime=pending.cover_image_mime,
chapter_intro_delay=pending.chapter_intro_delay, chapter_intro_delay=pending.chapter_intro_delay,
@@ -2518,22 +2541,19 @@ def finalize_job(pending_id: str) -> ResponseReturnValue:
chunks=processed_chunks, chunks=processed_chunks,
speakers=roster, speakers=roster,
speaker_mode=pending.speaker_mode, speaker_mode=pending.speaker_mode,
speaker_analysis=analysis_payload,
speaker_analysis_threshold=pending.speaker_analysis_threshold,
generate_epub3=pending.generate_epub3, generate_epub3=pending.generate_epub3,
speaker_analysis=pending.speaker_analysis,
speaker_analysis_threshold=pending.speaker_analysis_threshold,
analysis_requested=getattr(pending, "analysis_requested", False), analysis_requested=getattr(pending, "analysis_requested", False),
) )
if config_languages: if config_languages:
job.speaker_voice_languages = list(config_languages) job.speaker_voice_languages = list(config_languages)
config_name = getattr(pending, "applied_speaker_config", None) elif pending.speaker_voice_languages:
if updated_config and isinstance(config_name, str) and config_name: job.speaker_voice_languages = list(pending.speaker_voice_languages)
job.applied_speaker_config = config_name
configs = load_configs() if isinstance(config_key, str) and config_key:
configs[config_name] = updated_config job.applied_speaker_config = config_key
save_configs(configs)
elif isinstance(config_name, str) and config_name:
job.applied_speaker_config = config_name
job.speaker_voice_languages = job.speaker_voice_languages or list(pending.speaker_voice_languages)
return redirect(url_for("web.index", _anchor="queue")) return redirect(url_for("web.index", _anchor="queue"))
+36 -6
View File
@@ -227,17 +227,47 @@ const initDashboard = () => {
} }
}); });
document.addEventListener("click", (event) => { const resolveEventMatch = (event, selector) => {
const rawTarget = event.target; const target = event.target;
const target = rawTarget instanceof Element ? rawTarget : rawTarget?.parentElement || null; if (target instanceof Element) {
if (!target) { const match = target.closest(selector);
return; if (match) {
return match;
}
} }
if (typeof event.composedPath === "function") {
const path = event.composedPath();
for (const node of path) {
if (node instanceof Element) {
if (node.matches(selector)) {
return node;
}
const match = node.closest(selector);
if (match) {
return match;
}
}
}
}
const fallback = target?.parentElement;
if (fallback instanceof Element) {
return fallback.closest(selector);
}
return null;
};
const readerClose = target.closest('[data-role="reader-modal-close"]'); document.addEventListener("click", (event) => {
const readerClose = resolveEventMatch(event, '[data-role="reader-modal-close"]');
if (readerClose) { if (readerClose) {
event.preventDefault(); event.preventDefault();
closeReaderModal(); closeReaderModal();
return;
}
const readerTriggerBtn = resolveEventMatch(event, '[data-role="open-reader"]');
if (readerTriggerBtn instanceof HTMLElement) {
event.preventDefault();
openReaderModal(readerTriggerBtn);
} }
}); });