diff --git a/AobaClient/Cargo.toml b/AobaClient/Cargo.toml index 25a74af..b561c75 100644 --- a/AobaClient/Cargo.toml +++ b/AobaClient/Cargo.toml @@ -1,37 +1,37 @@ -[package] -name = "aoba-client" -version = "0.1.0" -authors = ["Amatsugu "] -edition = "2024" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { version = "0.6.0", features = ["router"] } -serde = "1.0.219" -serde_repr = "0.1.20" -tonic = { version = "*", default-features = false, features = [ - "codegen", - "prost", -] } -prost = "0.13" -tonic-web-wasm-client = "0.7" -web-sys = { version = "0.3.77", features = ["Storage", "Window"] } - -[build-dependencies] -tonic-build = { version = "*", default-features = false, features = ["prost"] } - -[features] -default = ["web"] -web = ["dioxus/web"] - -[profile] - -[profile.wasm-dev] -inherits = "dev" -opt-level = 1 - -[profile.server-dev] -inherits = "dev" - -[profile.android-dev] -inherits = "dev" +[package] +name = "aoba-client" +version = "0.1.0" +authors = ["Amatsugu "] +edition = "2024" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { version = "0.6.0", features = ["router"] } +serde = "1.0.219" +serde_repr = "0.1.20" +tonic = { version = "*", default-features = false, features = [ + "codegen", + "prost", +] } +prost = "0.13" +tonic-web-wasm-client = "0.7" +web-sys = { version = "0.3.77", features = ["Storage", "Window"] } + +[build-dependencies] +tonic-build = { version = "*", default-features = false, features = ["prost"] } + +[features] +default = ["web"] +web = ["dioxus/web"] + +[profile] + +[profile.wasm-dev] +inherits = "dev" +opt-level = 1 + +[profile.server-dev] +inherits = "dev" + +[profile.android-dev] +inherits = "dev" diff --git a/AobaClient/assets/style/main.scss b/AobaClient/assets/style/main.scss index bd44504..3ad1400 100644 --- a/AobaClient/assets/style/main.scss +++ b/AobaClient/assets/style/main.scss @@ -1,151 +1,154 @@ -@import "mixins"; -@import "colors"; -* { - box-sizing: border-box; -} - -:root { - background-color: $mainBGColor; - color: $mainTextColor; - box-sizing: border-box; - font-family: "Noto Sans", sans-serif; - font-optical-sizing: auto; - font-weight: 400; - font-style: normal; - font-variation-settings: "wdth" 100; -} - -.stickyTop { - top: 0; - position: sticky; -} - -body { - padding: 0; - margin: 0; -} - -#main:has(#content) { - display: grid; - grid-template-columns: $navBarSize 1fr; - grid-template-areas: "Nav Content"; -} - -#content { - grid-area: Content; - overflow-x: hidden; - padding: 10px; - /* margin-left: $navBarSize; */ -} - -$mediaItemSize: 300px; - -.mediaGrid { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: 10px; - margin: 10px 0; - - .mediaItem { - width: $mediaItemSize; - height: $mediaItemSize; - overflow: hidden; - display: grid; - grid-template-columns: $mediaItemSize; - grid-template-areas: "A"; - box-shadow: 0 0 2px #000; - color: $mainTextColor; - text-decoration: none; - transition: - transform 0.25s ease-out, - box-shadow 0.25s ease-out; - - > * { - grid-area: A; - } - - img { - aspect-ratio: 1; - object-fit: cover; - width: 100%; - object-position: center; - background-color: $invertTextColor; - border: 0; - outline: none; - } - - .info { - align-self: end; - backdrop-filter: blur(20px) brightness(0.5); - transition: transform 0.25s ease-out; - transform: translateY(100%); - padding: 2px; - - .name { - text-align: center; - width: 100%; - display: block; - overflow: hidden; - } - .details { - display: flex; - justify-content: space-between; - } - } - - &:hover { - transform: scale(110%) translateZ(2px); - box-shadow: 0 0 8px #000; - - .info { - transform: translateY(0%); - } - } - } -} - -#main:has(#centralModal) { - display: grid; - place-items: center; - height: 100dvh; - width: 100dvw; -} - -#centralModal { - display: flex; - flex-direction: column; -} - -form { - display: flex; - flex-direction: column; - gap: 5px; -} - -.notif { - background-color: red; - display: grid; - grid-template-columns: 50px 1fr; - height: 50px; - border-radius: 20px; - border-bottom-left-radius: 0; - border-top-right-radius: 0; - - .icon { - padding: 10px; - } - - .message { - padding: 10px; - align-self: center; - } -} - -.codeSelect { - line-break: anywhere; - white-space: pre-wrap; - background-color: $featureColor; - padding: 5px; - user-select: all; -} +@import "mixins"; +@import "colors"; +* { + box-sizing: border-box; +} + +:root { + background-color: $mainBGColor; + color: $mainTextColor; + box-sizing: border-box; + font-family: "Noto Sans", sans-serif; + font-optical-sizing: auto; + font-weight: 400; + font-style: normal; + font-variation-settings: "wdth" 100; +} + +.stickyTop { + top: 0; + position: sticky; +} + +body { + padding: 0; + margin: 0; +} + +#main:has(#content) { + display: grid; + grid-template-columns: $navBarSize 1fr; + grid-template-areas: "Nav Content"; +} + +#content { + grid-area: Content; + overflow-x: hidden; + padding: 10px; + /* margin-left: $navBarSize; */ +} + +$mediaItemSize: 300px; + +.mediaGrid { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 10px; + margin: 10px 0; + + .mediaItem { + width: $mediaItemSize; + height: $mediaItemSize; + overflow: hidden; + display: grid; + grid-template-columns: $mediaItemSize; + grid-template-areas: "A"; + box-shadow: 0 0 2px #000; + color: $mainTextColor; + text-decoration: none; + transition: + transform 0.25s ease-out, + box-shadow 0.25s ease-out; + + > * { + grid-area: A; + } + + img { + aspect-ratio: 1; + object-fit: cover; + width: 100%; + object-position: center; + background-color: $invertTextColor; + border: 0; + outline: none; + } + + .info { + align-self: end; + backdrop-filter: blur(20px) brightness(0.5); + transition: transform 0.25s ease-out; + transform: translateY(100%); + padding: 2px; + + .name { + text-align: center; + width: 100%; + display: block; + overflow: hidden; + } + .details { + display: flex; + justify-content: space-between; + } + } + + &:hover { + transform: scale(110%) translateZ(2px); + box-shadow: 0 0 8px #000; + + .info { + transform: translateY(0%); + } + } + + &.placeholder { + } + } +} + +#main:has(#centralModal) { + display: grid; + place-items: center; + height: 100dvh; + width: 100dvw; +} + +#centralModal { + display: flex; + flex-direction: column; +} + +form { + display: flex; + flex-direction: column; + gap: 5px; +} + +.notif { + background-color: red; + display: grid; + grid-template-columns: 50px 1fr; + height: 50px; + border-radius: 20px; + border-bottom-left-radius: 0; + border-top-right-radius: 0; + + .icon { + padding: 10px; + } + + .message { + padding: 10px; + align-self: center; + } +} + +.codeSelect { + line-break: anywhere; + white-space: pre-wrap; + background-color: $featureColor; + padding: 5px; + user-select: all; +} diff --git a/AobaClient/src/components/media_grid.rs b/AobaClient/src/components/media_grid.rs index 0b13849..656634e 100644 --- a/AobaClient/src/components/media_grid.rs +++ b/AobaClient/src/components/media_grid.rs @@ -52,7 +52,7 @@ pub fn MediaGrid(props: MediaGridProps) -> Element { div { class: "mediaGrid", {result.items.iter().map(|itm| rsx!{ - MediaItem { item: itm.clone() } + MediaItem { item: Some(itm.clone()) } })}, } }, @@ -68,9 +68,9 @@ pub fn MediaGrid(props: MediaGridProps) -> Element { None => rsx! { div{ class: "mediaGrid", - div { - "Loading..." - } + {(0..50).map(|_| rsx!{ + MediaItem {} + })} } }, } diff --git a/AobaClient/src/components/media_item.rs b/AobaClient/src/components/media_item.rs index 28be42e..b601560 100644 --- a/AobaClient/src/components/media_item.rs +++ b/AobaClient/src/components/media_item.rs @@ -4,26 +4,41 @@ use crate::{HOST, rpc::aoba::MediaModel}; #[derive(PartialEq, Clone, Props)] pub struct MediaItemProps { - pub item: MediaModel, + pub item: Option, } #[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; + if let Some(item) = props.item { + let mtype = item.media_type().as_str_name(); + let filename = item.file_name; + let id = 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}" } + let src = format!("{HOST}/m/thumb/{id}"); + return 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 { "{item.view_count}" } + } } } - } + }; + } else { + return rsx! { + div { class: "mediaItem placeholder", + img { }, + span { class: "info", + span { class: "name" } + span { class: "details", + span { } + span { } + } + } + } + }; } } diff --git a/AobaCore/AobaCore.csproj b/AobaCore/AobaCore.csproj index 714e7cd..616d0cf 100644 --- a/AobaCore/AobaCore.csproj +++ b/AobaCore/AobaCore.csproj @@ -9,13 +9,14 @@ - + - + - + + diff --git a/AobaCore/Models/Media.cs b/AobaCore/Models/Media.cs index 278e832..8eb43aa 100644 --- a/AobaCore/Models/Media.cs +++ b/AobaCore/Models/Media.cs @@ -15,6 +15,7 @@ public class Media public int ViewCount { get; set; } public ObjectId Owner { get; set; } public DateTime UploadDate { get; set; } + public string[] Tags { get; set; } = []; public static readonly Dictionary KnownTypes = new() @@ -66,6 +67,7 @@ public class Media Owner = owner; Id = ObjectId.GenerateNewId(); UploadDate = DateTime.UtcNow; + Tags = DeriveTags(filename); } public string GetMediaUrl() @@ -85,6 +87,14 @@ public class Media else return MediaType.Raw; } + + public static string[] DeriveTags(string filename) + { + return filename.Split('_') + .SelectMany(v => v.Split('-')) + .SelectMany(v => v.Split(' ')) + .ToArray(); + } } public enum MediaType diff --git a/AobaCore/Services/AobaIndexCreationService.cs b/AobaCore/Services/AobaIndexCreationService.cs index 581d75f..12112a7 100644 --- a/AobaCore/Services/AobaIndexCreationService.cs +++ b/AobaCore/Services/AobaIndexCreationService.cs @@ -17,7 +17,9 @@ public class AobaIndexCreationService(IMongoDatabase db): BackgroundService { BsonSerializer.RegisterSerializer(new EnumSerializer(BsonType.String)); var textKeys = Builders.IndexKeys - .Text(m => m.Filename); + .Text(m => m.Filename) + .Text(m => m.Ext) + .Text(m => m.Tags); var textModel = new CreateIndexModel(textKeys, new CreateIndexOptions { diff --git a/AobaCore/Services/AobaService.cs b/AobaCore/Services/AobaService.cs index b9c08de..59cf5be 100644 --- a/AobaCore/Services/AobaService.cs +++ b/AobaCore/Services/AobaService.cs @@ -95,4 +95,17 @@ public class AobaService(IMongoDatabase db) //ignore if file was not found } } + + public async Task DeriveTagsAsync(CancellationToken cancellationToken = default) + { + var mediaItems = await _media.Find(Builders.Filter.Exists(m => m.Tags, false)) + .ToListAsync(cancellationToken); + Console.WriteLine($"Derving Tag for {mediaItems.Count} items"); + foreach (var mediaItem in mediaItems) + { + mediaItem.Tags = Media.DeriveTags(mediaItem.Filename); + await _media.UpdateOneAsync(m => m.Id == mediaItem.Id, Builders.Update.Set(m => m.Tags, mediaItem.Tags), null, cancellationToken); + } + Console.WriteLine("All Tags Derived"); + } } diff --git a/AobaServer/AobaServer.csproj b/AobaServer/AobaServer.csproj index c50c9e2..6f6047e 100644 --- a/AobaServer/AobaServer.csproj +++ b/AobaServer/AobaServer.csproj @@ -18,7 +18,7 @@ - +