app_ui/recordings_library.rs
1//! Recordings library — open a finished recording in the editor (ED.24).
2//!
3//! The trim bin's shelf: every finished recording in the output folder shows
4//! up as a tile, and clicking one carries it straight to the cutting bench
5//! (the editor). The backend `list_recordings` command scans the folder; the
6//! click reuses the [ED.5](crate::editor_ipc::screen_open_in_editor) handoff
7//! and flips the nav to the editor. A small "edited" badge marks recordings
8//! that already have a saved `.screenproj` beside them (ED.23).
9
10use leptos::prelude::*;
11
12use ui_storybook::components::shell::AppSection;
13
14use crate::editor_ipc::{self, RecordingEntry};
15
16/// One library card. Extracted so the grid stays under clippy's line cap.
17fn card(entry: RecordingEntry, active: RwSignal<AppSection>) -> impl IntoView {
18 let path = entry.path.clone();
19 let badge = entry
20 .has_project
21 .then(|| view! { <span class="library-card-badge">"edited"</span> });
22 view! {
23 <button
24 class="library-card"
25 title=entry.path.clone()
26 on:click=move |_| {
27 let _ = editor_ipc::screen_open_in_editor(&path);
28 active.set(AppSection::Editor);
29 }
30 >
31 <div class="library-card-thumb"></div>
32 <span class="library-card-name">{entry.name}</span>
33 {badge}
34 </button>
35 }
36}
37
38/// The recordings library: a grid of finished recordings; clicking one opens
39/// it in the editor. Refreshes its list each time it mounts.
40#[component]
41pub fn RecordingsLibrary(
42 /// Active nav section — flipped to `AppSection::Editor` when a recording
43 /// is opened.
44 active: RwSignal<AppSection>,
45) -> impl IntoView {
46 let entries =
47 use_context::<RwSignal<Vec<RecordingEntry>>>().unwrap_or_else(|| RwSignal::new(Vec::new()));
48 // Refresh on mount — navigating to Library re-creates this component.
49 editor_ipc::list_recordings();
50 view! {
51 <div class="recordings-library">
52 <h1 class="library-title">"Library"</h1>
53 {move || {
54 let list = entries.get();
55 if list.is_empty() {
56 view! {
57 <p class="library-empty">
58 "No recordings yet — finish a recording to see it here."
59 </p>
60 }
61 .into_any()
62 } else {
63 view! {
64 <div class="library-grid">
65 {list.into_iter().map(|e| card(e, active)).collect_view()}
66 </div>
67 }
68 .into_any()
69 }
70 }}
71 </div>
72 }
73}