timeline rendering
This commit is contained in:
@@ -19,4 +19,44 @@
|
||||
#timeline {
|
||||
grid-area: Timeline;
|
||||
border: 1px solid $lightBackground;
|
||||
|
||||
#tracklist {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
gap: 1px;
|
||||
}
|
||||
.track {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 2px 0;
|
||||
height: 30px;
|
||||
background-color: $lightBackground;
|
||||
/* overflow: hidden; */
|
||||
margin-left: 100px;
|
||||
|
||||
.label {
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
left: -100px;
|
||||
top: 0px;
|
||||
background-color: $featureBackround;
|
||||
}
|
||||
}
|
||||
|
||||
.clip {
|
||||
position: absolute;
|
||||
display: block;
|
||||
height: 30px;
|
||||
top: auto;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
background-color: $mainAccentDark;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use chrono::{Datelike, Days, Local};
|
||||
use chrono::{Datelike, Local};
|
||||
use dioxus::prelude::*;
|
||||
use prost_types::Timestamp;
|
||||
|
||||
@@ -10,10 +10,10 @@ const PLAYER_CSS: Asset = asset!("/assets/styling/player.scss");
|
||||
|
||||
#[component]
|
||||
pub fn Player() -> Element {
|
||||
let entries = use_resource(|| async move {
|
||||
let playbackResult = use_resource(|| async move {
|
||||
let mut client = get_rpc_client();
|
||||
let now = Local::now();
|
||||
let from = Timestamp::date(now.year() as i64, now.month() as u8, now.day() as u8).unwrap();
|
||||
let from = Timestamp::date(now.year() as i64, now.month() as u8, now.day() as u8 - 4).unwrap();
|
||||
let result = client
|
||||
.get_media_playback(MediaPlaybackRequest { date: Some(from) })
|
||||
.await;
|
||||
@@ -26,12 +26,12 @@ pub fn Player() -> Element {
|
||||
return Err(format!("Failed to load results: {msg}"));
|
||||
}
|
||||
});
|
||||
let len = match entries.cloned() {
|
||||
let info = match playbackResult.cloned() {
|
||||
Some(value) => match value {
|
||||
Ok(result) => result.channels.len().to_string(),
|
||||
Err(err) => err,
|
||||
Ok(result) => Some(result),
|
||||
Err(_) => None,
|
||||
},
|
||||
_ => "Not Loaded".to_string(),
|
||||
_ => None,
|
||||
};
|
||||
rsx! {
|
||||
document::Link { rel: "stylesheet", href: PLAYER_CSS }
|
||||
@@ -39,10 +39,9 @@ pub fn Player() -> Element {
|
||||
id: "player",
|
||||
div {
|
||||
id: "head",
|
||||
"r {len}"
|
||||
}
|
||||
Viewport { }
|
||||
Timeline { }
|
||||
Timeline { playbackInfo: info }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,73 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::rpc::azki::{MediaChannel, MediaEntry, PlaybackInfo};
|
||||
|
||||
#[component]
|
||||
pub fn Timeline() -> Element {
|
||||
pub fn Timeline(playbackInfo: Option<PlaybackInfo>) -> Element {
|
||||
return match playbackInfo {
|
||||
Some(info) => rsx! {
|
||||
div{
|
||||
id: "timeline",
|
||||
TrackList { channels: info.channels, start: info.date.unwrap().seconds, zoom: 2.0 }
|
||||
}
|
||||
},
|
||||
None => rsx! {
|
||||
div{
|
||||
id: "timeline",
|
||||
TrackList { channels: Vec::new(), start: 0, zoom: 1.0 }
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn TrackList(channels: Vec<MediaChannel>, start: i64, zoom: f32) -> Element {
|
||||
rsx! {
|
||||
div{
|
||||
id: "timeline"
|
||||
div {
|
||||
id: "tracklist",
|
||||
{channels.iter().map(|c|rsx!{
|
||||
TimelineTrack { channel: c.clone(), start, zoom }
|
||||
})}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn TimelineTrack(channel: MediaChannel, start: i64, zoom: f32) -> Element {
|
||||
rsx! {
|
||||
div{
|
||||
class: "track",
|
||||
style: "width: calc({zoom * 100.0}% - 100px);",
|
||||
TrackLabel { channel: channel.clone() },
|
||||
{channel.videos.iter().map(|m|rsx!{Clip{media: m.clone(), start}})}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn TrackLabel(channel: MediaChannel) -> Element {
|
||||
rsx! {
|
||||
div{
|
||||
class: "label",
|
||||
"Camera {channel.camera_id}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SEC_PER_DAY: f32 = 86400.0;
|
||||
#[component]
|
||||
fn Clip(media: MediaEntry, start: i64) -> Element {
|
||||
let meta = media.metadata.unwrap();
|
||||
let date = media.date.unwrap();
|
||||
let timestamp = date.seconds - start;
|
||||
let duration = (meta.duration as f32 / SEC_PER_DAY) * 100.0;
|
||||
let offset = (timestamp as f32 / SEC_PER_DAY) * 100.0;
|
||||
let time = date.to_string();
|
||||
rsx! {
|
||||
div{
|
||||
class: "clip",
|
||||
style: "width: {duration}%; left: {offset}%",
|
||||
"{timestamp / 60 / 60}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user