preview.rs
⎇
Raw
1//! File previews: browser-native media (image/PDF/video/audio), full page,
2//! or a download prompt for kinds with no viewer. Text files open in the
3//! editor instead.
4
5use api_types::FileKind;
6use leptos::prelude::*;
7
8use crate::api;
9use crate::components::icon::Icon;
10use crate::i18n;
11use crate::icons::{IconName, icon_for};
12use crate::views::file_view::FileView;
13
14/// Whether this kind opens in the media preview. Text goes to the editor,
15/// archives and binaries get the "no preview" view.
16pub fn is_media(kind: FileKind) -> bool {
17 matches!(
18 kind,
19 FileKind::Image | FileKind::Pdf | FileKind::Video | FileKind::Audio
20 )
21}
22
23/// The full-page preview (closing lives in the top bar).
24#[component]
25pub fn Preview(target: FileView) -> impl IntoView {
26 let url = api::preview_url(target.root_id, &target.path);
27 let name = target.name.clone();
28 let media = match target.kind {
29 FileKind::Image => view! { <img class="preview-img" src=url alt=name/> }.into_any(),
30 FileKind::Pdf => view! { <iframe class="preview-pdf" src=url title=name/> }.into_any(),
31 FileKind::Video => {
32 view! { <video class="preview-video" src=url controls=true/> }.into_any()
33 }
34 FileKind::Audio => view! {
35 <div class="preview-audio">
36 <audio src=url controls=true/>
37 </div>
38 }
39 .into_any(),
40 FileKind::Dir | FileKind::Archive | FileKind::Text | FileKind::Binary => {
41 return view! { <NoPreview target=target/> }.into_any();
42 }
43 };
44 view! { <div class="preview-body file-preview">{media}</div> }.into_any()
45}
46
47/// Shown for files with no built-in viewer: a message and a download button.
48#[component]
49fn NoPreview(target: FileView) -> impl IntoView {
50 let icon = icon_for(target.kind, &target.name);
51 let name = target.name.clone();
52 let t = target;
53 view! {
54 <div class="file-nopreview">
55 <div class="empty-state">
56 <Icon name=icon class="empty-glyph".to_string()/>
57 <h3>{i18n::tr(i18n::k::NO_PREVIEW)}</h3>
58 <p class="muted">{i18n::t_fmt(i18n::k::NO_PREVIEW_MSG, &name)}</p>
59 <button
60 class="btn btn-primary"
61 on:click=move |_| {
62 let url = api::download_url(t.root_id, &t.path, None);
63 api::trigger_download(&url, &name);
64 }
65 >
66 <Icon name=IconName::Download class="ic-btn".to_string()/>
67 {i18n::tr(i18n::k::DOWNLOAD)}
68 </button>
69 </div>
70 </div>
71 }
72}
73