feat: Added stuff
This commit is contained in:
@@ -6,12 +6,10 @@ edition = "2024"
|
||||
[dependencies]
|
||||
api = { version = "0.1.0", path = "../api" }
|
||||
blurhash = "0.2.3"
|
||||
bytes = "1.11.0"
|
||||
gpui_util = "0.2.2"
|
||||
iced = { git = "https://github.com/iced-rs/iced", features = [
|
||||
"advanced",
|
||||
"canvas",
|
||||
"image",
|
||||
"tokio",
|
||||
] }
|
||||
iced = { git = "https://github.com/iced-rs/iced", features = ["advanced", "canvas", "image", "sipper", "tokio"] }
|
||||
reqwest = "0.12.24"
|
||||
tap = "1.0.1"
|
||||
tracing = "0.1.41"
|
||||
uuid = "1.18.1"
|
||||
|
||||
@@ -13,6 +13,17 @@ pub struct BlurHash {
|
||||
punch: f32,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for BlurHash {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("BlurHash")
|
||||
.field("hash", &self.hash)
|
||||
.field("width", &self.width)
|
||||
.field("height", &self.height)
|
||||
.field("punch", &self.punch)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl BlurHash {
|
||||
pub fn recompute(&mut self, width: u32, height: u32, punch: f32) {
|
||||
let pixels = blurhash::decode(&self.hash, width, height, punch)
|
||||
|
||||
@@ -4,14 +4,14 @@ use shared_string::SharedString;
|
||||
mod blur_hash;
|
||||
use blur_hash::BlurHash;
|
||||
|
||||
// mod preview;
|
||||
// use preview::Preview;
|
||||
|
||||
use iced::{Alignment, Element, Length, Task, widget::*};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Loading {
|
||||
to: Screen,
|
||||
from: Screen,
|
||||
}
|
||||
pub struct Loading {}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct ItemCache {
|
||||
@@ -89,8 +89,10 @@ pub struct Item {
|
||||
pub enum Screen {
|
||||
#[default]
|
||||
Home,
|
||||
Item(Option<uuid::Uuid>),
|
||||
Search(String),
|
||||
Settings,
|
||||
Profile,
|
||||
User,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
struct State {
|
||||
@@ -100,6 +102,7 @@ struct State {
|
||||
jellyfin_client: api::JellyfinClient,
|
||||
messages: Vec<String>,
|
||||
history: Vec<Option<uuid::Uuid>>,
|
||||
query: Option<String>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -111,6 +114,7 @@ impl State {
|
||||
jellyfin_client,
|
||||
messages: Vec::new(),
|
||||
history: Vec::new(),
|
||||
query: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,6 +123,8 @@ impl State {
|
||||
pub enum Message {
|
||||
OpenSettings,
|
||||
Refresh,
|
||||
Search,
|
||||
SearchQueryChanged(String),
|
||||
OpenItem(Option<uuid::Uuid>),
|
||||
LoadedItem(Option<uuid::Uuid>, Vec<Item>),
|
||||
Error(String),
|
||||
@@ -191,6 +197,23 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
state.current = None;
|
||||
Task::done(Message::Refresh)
|
||||
}
|
||||
Message::SearchQueryChanged(query) => {
|
||||
state.query = Some(query);
|
||||
// Handle search query change
|
||||
Task::none()
|
||||
}
|
||||
Message::Search => {
|
||||
// Handle search action
|
||||
let client = state.jellyfin_client.clone();
|
||||
let query = state.query.clone().unwrap_or_default();
|
||||
Task::perform(async move { client.search(query).await }, |r| match r {
|
||||
Err(e) => Message::Error(format!("Search failed: {}", e)),
|
||||
Ok(items) => {
|
||||
let items = items.into_iter().map(Item::from).collect();
|
||||
Message::LoadedItem(None, items)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,20 +254,15 @@ fn header(state: &State) -> Element<'_, Message> {
|
||||
.align_y(Alignment::Center)
|
||||
.style(container::rounded_box)
|
||||
.into(),
|
||||
container(
|
||||
row([
|
||||
button("Settings").on_press(Message::OpenSettings).into(),
|
||||
button("Refresh").on_press(Message::Refresh).into(),
|
||||
])
|
||||
.spacing(10),
|
||||
)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_x(Alignment::End)
|
||||
.align_y(Alignment::Center)
|
||||
.style(container::rounded_box)
|
||||
.into(),
|
||||
search(state),
|
||||
container(row([button("Refresh").on_press(Message::Refresh).into()]).spacing(10))
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_x(Alignment::End)
|
||||
.align_y(Alignment::Center)
|
||||
.style(container::rounded_box)
|
||||
.into(),
|
||||
])
|
||||
.align_y(Alignment::Center)
|
||||
.width(Length::Fill)
|
||||
@@ -252,6 +270,22 @@ fn header(state: &State) -> Element<'_, Message> {
|
||||
.into()
|
||||
}
|
||||
|
||||
fn search(state: &State) -> Element<'_, Message> {
|
||||
container(
|
||||
TextInput::new("Search...", state.query.as_deref().unwrap_or_default())
|
||||
.padding(10)
|
||||
.size(16)
|
||||
.width(Length::Fill)
|
||||
.on_input(Message::SearchQueryChanged)
|
||||
.on_submit(Message::Search),
|
||||
)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Shrink)
|
||||
.style(container::rounded_box)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn footer(state: &State) -> Element<'_, Message> {
|
||||
container(
|
||||
column(
|
||||
|
||||
105
ui-iced/src/preview.rs
Normal file
105
ui-iced/src/preview.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use iced::{Animation, advanced::image::Handle, widget::image};
|
||||
use reqwest::Method;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::blur_hash::BlurHash;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ImageDownloader {
|
||||
client: reqwest::Client,
|
||||
request_modifier:
|
||||
Option<Arc<dyn Fn(reqwest::RequestBuilder) -> reqwest::RequestBuilder + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl ImageDownloader {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
client: reqwest::Client::new(),
|
||||
request_modifier: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_modifier<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: Fn(reqwest::RequestBuilder) -> reqwest::RequestBuilder + Send + Sync + 'static,
|
||||
{
|
||||
self.request_modifier = Some(Arc::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn download(&self, url: &str) -> reqwest::Result<bytes::Bytes> {
|
||||
use ::tap::*;
|
||||
let response = self
|
||||
.client
|
||||
.request(Method::GET, url)
|
||||
.pipe(|builder| {
|
||||
if let Some(ref modifier) = self.request_modifier {
|
||||
modifier(builder)
|
||||
} else {
|
||||
builder
|
||||
}
|
||||
})
|
||||
.send()
|
||||
.await?;
|
||||
let bytes = response.bytes().await?;
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Preview {
|
||||
Thumbnail {
|
||||
thumbnail: Image,
|
||||
blur_hash: BlurHash,
|
||||
},
|
||||
BlurHash {
|
||||
blur_hash: BlurHash,
|
||||
},
|
||||
}
|
||||
|
||||
// impl Preview {
|
||||
// pub fn thumbnail(image: Image, blur_hash: BlurHash) -> Self {
|
||||
// Preview::Thumbnail {
|
||||
// thumbnail: image,
|
||||
// blur_hash,
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn blur_hash(blur_hash: BlurHash) -> Self {
|
||||
// Preview::BlurHash { blur_hash }
|
||||
// }
|
||||
//
|
||||
// pub fn upgrade(
|
||||
// self,
|
||||
// fut: impl core::future::Future<Output = bytes::Bytes> + 'static + Send,
|
||||
// ) -> iced::Task<PreviewMessage> {
|
||||
// // let sip = iced::task::sipper(async move |mut sender| {
|
||||
// // let bytes = fut.await;
|
||||
// // let handle = Handle::from_bytes(bytes.clone());
|
||||
// // let allocation = image::allocate(handle);
|
||||
// // let image = Image {
|
||||
// // bytes,
|
||||
// // handle,
|
||||
// // allocation,
|
||||
// // fade_in: Animation::new(false),
|
||||
// // };
|
||||
// // let _ = sender.send(image).await;
|
||||
// // });
|
||||
// // iced::Task::sip(sip, ||)
|
||||
// Task::
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// enum PreviewMessage {
|
||||
// BlurHashLoaded(BlurHash),
|
||||
// ThumbnailLoaded(Image),
|
||||
// ThumbnailAllocated(image::Allocation),
|
||||
// }
|
||||
//
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Image {
|
||||
bytes: bytes::Bytes,
|
||||
handle: Handle,
|
||||
allocation: image::Allocation,
|
||||
fade_in: Animation<bool>,
|
||||
}
|
||||
Reference in New Issue
Block a user