Skip to main content

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}