JJ Colocate

This commit is contained in:
2025-06-30 14:23:20 -04:00
parent 360fa53439
commit 24abf5607f
77 changed files with 5700 additions and 5700 deletions

View File

@@ -1,30 +1,30 @@
use dioxus::prelude::*;
#[derive(PartialEq, Clone, Props)]
pub struct ButtonProps {
pub variant: Option<ButtonVariant>,
pub text: String,
pub onclick: Option<EventHandler<Event<MouseData>>>,
}
#[derive(PartialEq, Clone)]
pub enum ButtonVariant {
Base,
Muted,
Accented,
}
#[component]
pub fn Button(props: ButtonProps) -> Element {
rsx! {
button {
onclick: move |event| {
event.prevent_default();
if let Some(h) = props.onclick {
h.call(event);
}
},
"{props.text}"
}
}
}
use dioxus::prelude::*;
#[derive(PartialEq, Clone, Props)]
pub struct ButtonProps {
pub variant: Option<ButtonVariant>,
pub text: String,
pub onclick: Option<EventHandler<Event<MouseData>>>,
}
#[derive(PartialEq, Clone)]
pub enum ButtonVariant {
Base,
Muted,
Accented,
}
#[component]
pub fn Button(props: ButtonProps) -> Element {
rsx! {
button {
onclick: move |event| {
event.prevent_default();
if let Some(h) = props.onclick {
h.call(event);
}
},
"{props.text}"
}
}
}

View File

@@ -1,35 +1,35 @@
use dioxus::prelude::*;
#[derive(PartialEq, Clone, Props)]
pub struct InputProps {
pub r#type: Option<String>,
pub value: Option<Signal<String>>,
pub label: Option<String>,
pub placeholder: Option<String>,
pub name: String,
pub oninput: Option<EventHandler<FormEvent>>,
pub required: Option<bool>,
}
#[component]
pub fn Input(props: InputProps) -> Element {
let label = props.label.unwrap_or("".into());
let ph = props.placeholder.unwrap_or(label.clone());
rsx! {
label {
"{label}"
input {
r#type: props.r#type.unwrap_or("text".into()),
value: props.value,
oninput: move |e| {
if let Some(mut s) = props.value {
s.set(e.value());
}
},
name: props.name,
placeholder: ph,
required: props.required,
}
}
}
}
use dioxus::prelude::*;
#[derive(PartialEq, Clone, Props)]
pub struct InputProps {
pub r#type: Option<String>,
pub value: Option<Signal<String>>,
pub label: Option<String>,
pub placeholder: Option<String>,
pub name: String,
pub oninput: Option<EventHandler<FormEvent>>,
pub required: Option<bool>,
}
#[component]
pub fn Input(props: InputProps) -> Element {
let label = props.label.unwrap_or("".into());
let ph = props.placeholder.unwrap_or(label.clone());
rsx! {
label {
"{label}"
input {
r#type: props.r#type.unwrap_or("text".into()),
value: props.value,
oninput: move |e| {
if let Some(mut s) = props.value {
s.set(e.value());
}
},
name: props.name,
placeholder: ph,
required: props.required,
}
}
}
}

View File

@@ -1,4 +1,4 @@
mod button;
mod input;
pub use button::*;
pub use input::*;
mod button;
mod input;
pub use button::*;
pub use input::*;

View File

@@ -1,52 +1,52 @@
use dioxus::prelude::*;
#[component]
pub fn Info() -> Element {
rsx! {
svg {
class: "size-6",
fill: "currentColor",
view_box: "0 0 24 24",
xmlns: "http://www.w3.org/2000/svg",
path {
clip_rule: "evenodd",
d: "M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 0 1 .67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 1 1-.671-1.34l.041-.022ZM12 9a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
fill_rule: "evenodd",
}
}
}
}
#[component]
pub fn Warn() -> Element {
rsx! {
svg {
class: "size-6",
fill: "currentColor",
view_box: "0 0 24 24",
xmlns: "http://www.w3.org/2000/svg",
path {
clip_rule: "evenodd",
d: "M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
fill_rule: "evenodd",
}
}
}
}
#[component]
pub fn Error() -> Element {
rsx! {
svg {
class: "size-6",
fill: "currentColor",
view_box: "0 0 24 24",
xmlns: "http://www.w3.org/2000/svg",
path {
clip_rule: "evenodd",
d: "M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
fill_rule: "evenodd",
}
}
}
}
use dioxus::prelude::*;
#[component]
pub fn Info() -> Element {
rsx! {
svg {
class: "size-6",
fill: "currentColor",
view_box: "0 0 24 24",
xmlns: "http://www.w3.org/2000/svg",
path {
clip_rule: "evenodd",
d: "M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 0 1 .67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 1 1-.671-1.34l.041-.022ZM12 9a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
fill_rule: "evenodd",
}
}
}
}
#[component]
pub fn Warn() -> Element {
rsx! {
svg {
class: "size-6",
fill: "currentColor",
view_box: "0 0 24 24",
xmlns: "http://www.w3.org/2000/svg",
path {
clip_rule: "evenodd",
d: "M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
fill_rule: "evenodd",
}
}
}
}
#[component]
pub fn Error() -> Element {
rsx! {
svg {
class: "size-6",
fill: "currentColor",
view_box: "0 0 24 24",
xmlns: "http://www.w3.org/2000/svg",
path {
clip_rule: "evenodd",
d: "M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
fill_rule: "evenodd",
}
}
}
}

View File

@@ -1,77 +1,77 @@
use dioxus::prelude::*;
use tonic::IntoRequest;
use crate::{
components::MediaItem,
rpc::{aoba::PageFilter, get_rpc_client},
};
#[derive(PartialEq, Clone, Props)]
pub struct MediaGridProps {
pub query: Option<String>,
#[props(default = Some(1))]
pub page: Option<i32>,
#[props(default = Some(100))]
pub page_size: Option<i32>,
}
impl IntoRequest<PageFilter> for MediaGridProps {
fn into_request(self) -> tonic::Request<PageFilter> {
let f: PageFilter = self.into();
f.into_request()
}
}
impl Into<PageFilter> for MediaGridProps {
fn into(self) -> PageFilter {
PageFilter {
page: self.page,
page_size: self.page_size,
query: self.query,
}
}
}
#[component]
pub fn MediaGrid(props: MediaGridProps) -> Element {
let media_result = use_resource(use_reactive!(|(props)| async move {
let mut client = get_rpc_client();
let result = client.list_media(props.into_request()).await;
if let Ok(items) = result {
return Ok(items.into_inner());
} else {
let err = result.err().unwrap();
let message = err.message();
return Err(format!("Failed to load results: {message}"));
}
}));
match media_result.cloned() {
Some(value) => match value {
Ok(result) => rsx! {
div {
class: "mediaGrid",
{result.items.iter().map(|itm| rsx!{
MediaItem { item: itm.clone() }
})},
}
},
Err(msg) => rsx! {
div {
class: "mediaGrid",
div {
"Failed to load results: {msg}"
}
}
},
},
None => rsx! {
div{
class: "mediaGrid",
div {
"Loading..."
}
}
},
}
}
use dioxus::prelude::*;
use tonic::IntoRequest;
use crate::{
components::MediaItem,
rpc::{aoba::PageFilter, get_rpc_client},
};
#[derive(PartialEq, Clone, Props)]
pub struct MediaGridProps {
pub query: Option<String>,
#[props(default = Some(1))]
pub page: Option<i32>,
#[props(default = Some(100))]
pub page_size: Option<i32>,
}
impl IntoRequest<PageFilter> for MediaGridProps {
fn into_request(self) -> tonic::Request<PageFilter> {
let f: PageFilter = self.into();
f.into_request()
}
}
impl Into<PageFilter> for MediaGridProps {
fn into(self) -> PageFilter {
PageFilter {
page: self.page,
page_size: self.page_size,
query: self.query,
}
}
}
#[component]
pub fn MediaGrid(props: MediaGridProps) -> Element {
let media_result = use_resource(use_reactive!(|(props)| async move {
let mut client = get_rpc_client();
let result = client.list_media(props.into_request()).await;
if let Ok(items) = result {
return Ok(items.into_inner());
} else {
let err = result.err().unwrap();
let message = err.message();
return Err(format!("Failed to load results: {message}"));
}
}));
match media_result.cloned() {
Some(value) => match value {
Ok(result) => rsx! {
div {
class: "mediaGrid",
{result.items.iter().map(|itm| rsx!{
MediaItem { item: itm.clone() }
})},
}
},
Err(msg) => rsx! {
div {
class: "mediaGrid",
div {
"Failed to load results: {msg}"
}
}
},
},
None => rsx! {
div{
class: "mediaGrid",
div {
"Loading..."
}
}
},
}
}

View File

@@ -1,29 +1,29 @@
use dioxus::prelude::*;
use crate::{HOST, rpc::aoba::MediaModel};
#[derive(PartialEq, Clone, Props)]
pub struct MediaItemProps {
pub item: MediaModel,
}
#[component]
pub fn MediaItem(props: MediaItemProps) -> Element {
let mtype = props.item.media_type().as_str_name();
let filename = props.item.file_name;
let id = props.item.media_id.unwrap().value;
let src = format!("{HOST}/m/thumb/{id}");
rsx! {
a { class: "mediaItem", href: "{HOST}/m/{id}", target: "_blank",
img { src }
span { class: "info",
span { class: "name", "{filename}" }
span { class: "details",
span { "{mtype}" }
span { "{props.item.view_count}" }
}
}
}
}
}
use dioxus::prelude::*;
use crate::{HOST, rpc::aoba::MediaModel};
#[derive(PartialEq, Clone, Props)]
pub struct MediaItemProps {
pub item: MediaModel,
}
#[component]
pub fn MediaItem(props: MediaItemProps) -> Element {
let mtype = props.item.media_type().as_str_name();
let filename = props.item.file_name;
let id = props.item.media_id.unwrap().value;
let src = format!("{HOST}/m/thumb/{id}");
rsx! {
a { class: "mediaItem", href: "{HOST}/m/{id}", target: "_blank",
img { src }
span { class: "info",
span { class: "name", "{filename}" }
span { class: "details",
span { "{mtype}" }
span { "{props.item.view_count}" }
}
}
}
}
}

View File

@@ -1,12 +1,12 @@
pub mod basic;
mod media_grid;
mod media_item;
mod navbar;
mod notif;
mod search;
pub use media_grid::*;
pub use media_item::*;
pub use navbar::*;
pub use notif::*;
pub use search::*;
mod icons;
pub mod basic;
mod media_grid;
mod media_item;
mod navbar;
mod notif;
mod search;
pub use media_grid::*;
pub use media_item::*;
pub use navbar::*;
pub use notif::*;
pub use search::*;
mod icons;

View File

@@ -1,56 +1,56 @@
use dioxus::prelude::*;
use crate::{Route, contexts::AuthContext};
const NAV_CSS: Asset = asset!("/assets/style/nav.scss");
const NAV_ICON: Asset = asset!("/assets/favicon.ico");
#[component]
pub fn Navbar() -> Element {
rsx! {
document::Link { rel: "stylesheet", href: NAV_CSS }
nav {
Branding {}
MainNaviagation {}
Widgets {}
Utils {}
}
}
}
#[component]
pub fn MainNaviagation() -> Element {
rsx! {
div { class: "mainNav",
Link { class: "navItem", to: Route::Home {}, "Home" }
Link { class: "navItem", to: Route::Settings {}, "Settings" }
}
}
}
#[component]
pub fn Branding() -> Element {
rsx! {
div { class: "branding",
img { src: NAV_ICON, alt: "Aoba" }
}
}
}
#[component]
pub fn Widgets() -> Element {
rsx! {
div { class: "widgets" }
}
}
#[component]
pub fn Utils() -> Element {
let mut auth_context = use_context::<AuthContext>();
rsx! {
div { class: "utils",
div { onclick: move |_| auth_context.logout(), "Logout" }
}
}
}
use dioxus::prelude::*;
use crate::{Route, contexts::AuthContext};
const NAV_CSS: Asset = asset!("/assets/style/nav.scss");
const NAV_ICON: Asset = asset!("/assets/favicon.ico");
#[component]
pub fn Navbar() -> Element {
rsx! {
document::Link { rel: "stylesheet", href: NAV_CSS }
nav {
Branding {}
MainNaviagation {}
Widgets {}
Utils {}
}
}
}
#[component]
pub fn MainNaviagation() -> Element {
rsx! {
div { class: "mainNav",
Link { class: "navItem", to: Route::Home {}, "Home" }
Link { class: "navItem", to: Route::Settings {}, "Settings" }
}
}
}
#[component]
pub fn Branding() -> Element {
rsx! {
div { class: "branding",
img { src: NAV_ICON, alt: "Aoba" }
}
}
}
#[component]
pub fn Widgets() -> Element {
rsx! {
div { class: "widgets" }
}
}
#[component]
pub fn Utils() -> Element {
let mut auth_context = use_context::<AuthContext>();
rsx! {
div { class: "utils",
div { onclick: move |_| auth_context.logout(), "Logout" }
}
}
}

View File

@@ -1,39 +1,39 @@
use dioxus::prelude::*;
use crate::components::icons;
#[derive(PartialEq, Clone, Props)]
pub struct NotifProps {
r#type: Option<NotifType>,
message: String,
}
#[derive(PartialEq, Clone)]
pub enum NotifType {
Notice,
Error,
Warning,
}
#[component]
pub fn Notif(props: NotifProps) -> Element {
let t = props.r#type.unwrap_or(NotifType::Notice);
let type_class = match t {
NotifType::Notice => "notice",
NotifType::Error => "error",
NotifType::Warning => "warning",
};
let m = props.message;
rsx! {
div { class: "notif {type_class}",
div { class: "icon",
match t {
NotifType::Notice => icons::Error(),
NotifType::Error => icons::Error(),
NotifType::Warning => icons::Warn(),
}
}
div { class: "message", "{m}" }
}
}
}
use dioxus::prelude::*;
use crate::components::icons;
#[derive(PartialEq, Clone, Props)]
pub struct NotifProps {
r#type: Option<NotifType>,
message: String,
}
#[derive(PartialEq, Clone)]
pub enum NotifType {
Notice,
Error,
Warning,
}
#[component]
pub fn Notif(props: NotifProps) -> Element {
let t = props.r#type.unwrap_or(NotifType::Notice);
let type_class = match t {
NotifType::Notice => "notice",
NotifType::Error => "error",
NotifType::Warning => "warning",
};
let m = props.message;
rsx! {
div { class: "notif {type_class}",
div { class: "icon",
match t {
NotifType::Notice => icons::Error(),
NotifType::Error => icons::Error(),
NotifType::Warning => icons::Warn(),
}
}
div { class: "message", "{m}" }
}
}
}

View File

@@ -1,15 +1,15 @@
use dioxus::prelude::*;
#[component]
pub fn Search(query: Signal<String>) -> Element {
rsx! {
div { class: "searchBar stickyTop",
input {
r#type: "search",
placeholder: "Search Files",
value: query,
oninput: move |event| query.set(event.value()),
}
}
}
}
use dioxus::prelude::*;
#[component]
pub fn Search(query: Signal<String>) -> Element {
rsx! {
div { class: "searchBar stickyTop",
input {
r#type: "search",
placeholder: "Search Files",
value: query,
oninput: move |event| query.set(event.value()),
}
}
}
}