use std::{collections::BTreeMap, sync::Arc};
use iced::{
advanced::graphics::core::Element,
font::{Stretch, Weight},
futures::StreamExt,
subscription,
widget::{container, image::Handle, text, Column, Row},
Font, Renderer, Subscription,
};
use internment::Intern;
use url::Url;
use crate::{
oracle::{MediaPlayerSpeaker, Oracle},
subscriptions::download_image,
theme::Icon,
widgets,
};
#[derive(Debug)]
pub struct Room {
oracle: Arc<Oracle>,
room: crate::oracle::Room,
speaker: Option<MediaPlayerSpeaker>,
now_playing_image: Option<Handle>,
lights: BTreeMap<&'static str, Box<str>>,
}
impl Room {
pub fn new(id: &'static str, oracle: Arc<Oracle>) -> Self {
let room = oracle.room(id).clone();
let speaker = room.speaker(&oracle);
let lights = room.light_names(&oracle);
Self {
oracle,
room,
speaker,
now_playing_image: None,
lights,
}
}
pub fn update(&mut self, event: Message) -> Option<Event> {
match event {
Message::LightToggle(_name) => {
None
}
Message::OpenLightOptions(name) => Some(Event::OpenLightContextMenu(name)),
Message::UpdateLightAmount(_name, _v) => {
None
}
Message::NowPlayingImageLoaded(url, handle) => {
if self
.speaker
.as_ref()
.and_then(|v| v.entity_picture.as_ref())
== Some(&url)
{
self.now_playing_image = Some(handle);
}
None
}
Message::UpdateSpeaker => {
let new = self.room.speaker(&self.oracle);
if self
.speaker
.as_ref()
.and_then(|v| v.entity_picture.as_ref())
!= new
.as_ref()
.as_ref()
.and_then(|v| v.entity_picture.as_ref())
{
self.now_playing_image = None;
}
self.speaker = new;
None
}
}
}
pub fn view(&self) -> Element<'_, Message, Renderer> {
let header = text(self.room.name.as_ref()).size(60).font(Font {
weight: Weight::Bold,
stretch: Stretch::Condensed,
..Font::with_name("Helvetica Neue")
});
let light = |id, name| {
widgets::toggle_card::toggle_card(name, false)
.icon(Icon::Bulb)
.on_press(Message::LightToggle(id))
.on_long_press(Message::OpenLightOptions(id))
};
let mut col = Column::new().spacing(20).padding(40).push(header);
if let Some(speaker) = self.speaker.clone() {
col = col.push(
container(widgets::media_player::media_player(
speaker,
self.now_playing_image.clone(),
))
.padding([12, 0, 24, 0]),
);
}
let lights = Row::with_children(
self.lights
.iter()
.map(|(id, name)| light(*id, name))
.map(Element::from)
.collect::<Vec<_>>(),
)
.spacing(10);
col = col.push(lights);
col.into()
}
pub fn subscription(&self) -> Subscription<Message> {
let image_subscription = if let (Some(uri), None) = (
self.speaker
.as_ref()
.and_then(|v| v.entity_picture.as_ref()),
&self.now_playing_image,
) {
download_image(uri.clone(), uri.clone(), Message::NowPlayingImageLoaded)
} else {
Subscription::none()
};
let speaker_subscription =
if let Some(speaker_id) = self.room.speaker_id.map(Intern::as_ref) {
subscription::run_with_id(
speaker_id,
self.oracle
.subscribe_id(speaker_id)
.map(|()| Message::UpdateSpeaker),
)
} else {
Subscription::none()
};
Subscription::batch([image_subscription, speaker_subscription])
}
}
pub enum Event {
OpenLightContextMenu(&'static str),
}
#[derive(Clone, Debug)]
pub enum Message {
NowPlayingImageLoaded(Url, Handle),
LightToggle(&'static str),
OpenLightOptions(&'static str),
UpdateLightAmount(&'static str, u8),
UpdateSpeaker,
}