From 12ee90c793cf5ee3aab0594952b92e82da5e1e29 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 2 Apr 2023 14:07:34 +0200 Subject: [PATCH 1/2] fix(menu): move menu bar down --- ui/menu/src/components/Menu.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/menu/src/components/Menu.svelte b/ui/menu/src/components/Menu.svelte index 79ac724..727d6fe 100644 --- a/ui/menu/src/components/Menu.svelte +++ b/ui/menu/src/components/Menu.svelte @@ -139,7 +139,7 @@ height: 100% z-index: 999999 - padding: 1em 0.4em + padding: 3em 0.4em 0.4em display: flex flex-direction: column From c94915e35120850a0de59ab125a929fd0c066945 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 2 Apr 2023 16:52:50 +0200 Subject: [PATCH 2/2] feat: add last-modified date to all GET responses feat: add last-version tag to PageInfoModal fix: set icon size to 48px --- .gitignore | 2 + src/api.rs | 94 ++++++++++++++----- src/db/mod.rs | 1 + src/db/model.rs | 14 ++- src/icons.rs | 2 +- tests/fixtures/mod.rs | 4 + .../tests__database__delete_website.snap | 6 +- tests/snapshots/tests__database__export.snap | 8 +- .../tests__database__get_website.snap | 3 + .../tests__database__get_websites.snap | 4 + .../tests__database__update_website.snap | 1 + tests/tests.rs | 18 ++-- ui/menu/src/App.svelte | 1 - ui/menu/src/components/PageInfoModal.svelte | 55 ++++++++--- ui/menu/src/style/main.sass | 5 +- ui/menu/src/style/values.sass | 1 + ui/menu/src/util/functions.ts | 23 +++++ 17 files changed, 186 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index ea8c4bf..2c87efa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +*.snap.new +*.pending-snap diff --git a/src/api.rs b/src/api.rs index 16cf13e..0acbc4b 100644 --- a/src/api.rs +++ b/src/api.rs @@ -63,6 +63,8 @@ pub enum ApiError { InvalidArchiveType, #[error("invalid color")] InvalidColor, + #[error("join error: {0}")] + TokioJoin(#[from] tokio::task::JoinError), } impl ResponseError for ApiError { @@ -73,6 +75,7 @@ impl ResponseError for ApiError { | ApiError::InvalidArchiveType | ApiError::InvalidColor => StatusCode::BAD_REQUEST, ApiError::NoAccess => StatusCode::FORBIDDEN, + ApiError::TokioJoin(_) => StatusCode::INTERNAL_SERVER_ERROR, } } } @@ -99,11 +102,17 @@ impl TalonApi { &self, talon: Data<&Talon>, subdomain: Path, - ) -> Result> { + ) -> Result>> { talon .db .get_website(&subdomain) - .map(|w| Json(Website::from((subdomain.0, w)))) + .map(|website| { + let modified = website.updated_at(); + Response::new(Json(Website::from((subdomain.0, website)))).header( + header::LAST_MODIFIED, + httpdate::fmt_http_date(modified.into()), + ) + }) .map_err(Error::from) } @@ -163,9 +172,14 @@ impl TalonApi { ) -> Result<()> { auth.check_subdomain(&subdomain, Access::Modify)?; - talon - .icons - .insert_icon(Cursor::new(data.as_slice()), &subdomain)?; + let t2 = talon.clone(); + let sd = subdomain.clone(); + tokio::task::spawn_blocking(move || { + t2.icons.insert_icon(Cursor::new(data.as_slice()), &sd) + }) + .await + .map_err(ApiError::from)??; + talon.db.update_website( &subdomain, db::model::WebsiteUpdate { @@ -252,7 +266,8 @@ impl TalonApi { /// Mimimum visibility of the websites #[oai(default)] visibility: Query, - ) -> Result>> { + ) -> Result>>> { + let modified = talon.db.websites_last_update()?; talon .db .get_websites() @@ -270,7 +285,10 @@ impl TalonApi { Err(_) => true, }) .collect::, _>>() - .map(Json) + .map(|data| { + Response::new(Json(data)) + .header(header::LAST_MODIFIED, httpdate::fmt_http_date(modified)) + }) .map_err(Error::from) } @@ -280,14 +298,19 @@ impl TalonApi { &self, talon: Data<&Talon>, subdomain: Path, - ) -> Result>> { - talon.db.website_exists(&subdomain)?; + ) -> Result>>> { + let website = talon.db.get_website(&subdomain)?; talon .db .get_website_versions(&subdomain) .map(|r| r.map(Version::from)) .collect::, _>>() - .map(Json) + .map(|data| { + Response::new(Json(data)).header( + header::LAST_MODIFIED, + httpdate::fmt_http_date(website.updated_at().into()), + ) + }) .map_err(Error::from) } @@ -298,11 +321,17 @@ impl TalonApi { talon: Data<&Talon>, subdomain: Path, version: Path, - ) -> Result> { + ) -> Result>> { talon .db .get_version(&subdomain, *version) - .map(|v| Json(Version::from((*version, v)))) + .map(|v| { + let create_date = v.created_at; + Response::new(Json(Version::from((*version, v)))).header( + header::LAST_MODIFIED, + httpdate::fmt_http_date(create_date.into()), + ) + }) .map_err(Error::from) } @@ -313,14 +342,19 @@ impl TalonApi { talon: Data<&Talon>, subdomain: Path, version: Path, - ) -> Result>> { - talon.db.version_exists(&subdomain, *version)?; + ) -> Result>>> { + let v = talon.db.get_version(&subdomain, *version)?; talon .db .get_version_files(&subdomain, *version) .map(|r| r.map(VersionFile::from)) .collect::, _>>() - .map(Json) + .map(|r| { + Response::new(Json(r)).header( + header::LAST_MODIFIED, + httpdate::fmt_http_date(v.created_at.into()), + ) + }) .map_err(Error::from) } @@ -380,15 +414,21 @@ impl TalonApi { // Try to store the uploaded website // If this fails, the new version needs to be deleted - let try_insert = || { + fn try_insert( + talon: &Talon, + data: Binary>, + subdomain: &str, + version: u32, + fallback: Option, + ) -> Result<()> { if data.starts_with(&hex!("1f8b")) { talon .storage - .insert_tgz_archive(data.as_slice(), &subdomain, version)?; + .insert_tgz_archive(data.as_slice(), subdomain, version)?; } else if data.starts_with(&hex!("504b0304")) { talon.storage.insert_zip_archive( Cursor::new(data.as_slice()), - &subdomain, + subdomain, version, )?; } else { @@ -396,20 +436,26 @@ impl TalonApi { } // Validata fallback path - if let Some(fallback) = &fallback.0 { + if let Some(fallback) = &fallback { if let Err(e) = talon .storage - .get_file(&subdomain, version, fallback, &Default::default()) + .get_file(subdomain, version, fallback, &Default::default()) { return Err(Error::from(ApiError::InvalidFallback(e.to_string()))); } } Ok(()) - }; + } - match try_insert() { - Ok(()) => { + let t2 = talon.clone(); + let sd = subdomain.clone(); + + match tokio::task::spawn_blocking(move || try_insert(&t2, data, &sd, version, fallback.0)) + .await + .map_err(|e| Error::from(ApiError::from(e))) + { + Ok(Ok(())) => { talon.db.update_website( &subdomain, db::model::WebsiteUpdate { @@ -419,7 +465,7 @@ impl TalonApi { )?; Ok(()) } - Err(e) => { + Err(e) | Ok(Err(e)) => { // Remove the bad version and decrement the id counter let _ = talon.db.delete_version(&subdomain, version, false); let _ = talon.db.decrement_vid(&subdomain, version); diff --git a/src/db/mod.rs b/src/db/mod.rs index 0c46d61..2b6d9fc 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -208,6 +208,7 @@ impl Db { w.source_url = website.source_url.unwrap_or(w.source_url); w.source_icon = website.source_icon.unwrap_or(w.source_icon); w.has_icon = website.has_icon.unwrap_or(w.has_icon); + w.updated_at = Some(OffsetDateTime::now_utc()); rmp_serde::to_vec(&w).ok() } diff --git a/src/db/model.rs b/src/db/model.rs index 8228538..9fb4732 100644 --- a/src/db/model.rs +++ b/src/db/model.rs @@ -29,13 +29,19 @@ pub struct Website { /// Does the website have an icon? #[serde(default)] pub has_icon: bool, + /// Website update date + #[serde(default)] + pub updated_at: Option, } impl Default for Website { fn default() -> Self { + let created_at = OffsetDateTime::now_utc(); + Self { name: Default::default(), - created_at: OffsetDateTime::now_utc(), + created_at, + updated_at: Some(created_at), latest_version: Default::default(), color: Default::default(), visibility: Default::default(), @@ -47,6 +53,12 @@ impl Default for Website { } } +impl Website { + pub fn updated_at(&self) -> OffsetDateTime { + self.updated_at.unwrap_or(self.created_at) + } +} + /// Update a website in the database with the contained values /// /// Values set to `None` remain unchanged. diff --git a/src/icons.rs b/src/icons.rs index a8ba350..17883bc 100644 --- a/src/icons.rs +++ b/src/icons.rs @@ -37,7 +37,7 @@ impl ResponseError for ImagesError { } } -const IMAGE_SIZE: u32 = 32; +const IMAGE_SIZE: u32 = 48; const MAX_IMAGE_SIZE: u32 = 4000; type Result = std::result::Result; diff --git a/tests/fixtures/mod.rs b/tests/fixtures/mod.rs index 47bfd63..a62c0bc 100644 --- a/tests/fixtures/mod.rs +++ b/tests/fixtures/mod.rs @@ -75,6 +75,7 @@ fn insert_websites(db: &Db) { &Website { name: "ThetaDev".to_owned(), created_at: datetime!(2023-02-18 16:30 +0), + updated_at: Some(datetime!(2023-02-18 16:30 +0)), latest_version: Some(2), color: Some(2068974), visibility: talon::model::Visibility::Featured, @@ -87,6 +88,7 @@ fn insert_websites(db: &Db) { &Website { name: "Spotify-Gender-Ex".to_owned(), created_at: datetime!(2023-02-18 16:30 +0), + updated_at: Some(datetime!(2023-02-18 16:30 +0)), latest_version: Some(1), color: Some(1947988), visibility: talon::model::Visibility::Featured, @@ -101,6 +103,7 @@ fn insert_websites(db: &Db) { &Website { name: "RustyPipe".to_owned(), created_at: datetime!(2023-02-20 18:30 +0), + updated_at: Some(datetime!(2023-02-20 18:30 +0)), latest_version: Some(1), color: Some(7943647), visibility: talon::model::Visibility::Featured, @@ -115,6 +118,7 @@ fn insert_websites(db: &Db) { &Website { name: "SvelteKit SPA".to_owned(), created_at: datetime!(2023-03-03 22:00 +0), + updated_at: Some(datetime!(2023-03-03 22:00 +0)), latest_version: Some(1), color: Some(16727552), visibility: talon::model::Visibility::Hidden, diff --git a/tests/snapshots/tests__database__delete_website.snap b/tests/snapshots/tests__database__delete_website.snap index 99a6cdf..604f8d6 100644 --- a/tests/snapshots/tests__database__delete_website.snap +++ b/tests/snapshots/tests__database__delete_website.snap @@ -2,9 +2,9 @@ source: tests/tests.rs expression: data --- -{"type":"website","key":"rustypipe","value":{"name":"RustyPipe","created_at":[2023,51,18,30,0,0,0,0,0],"latest_version":1,"color":7943647,"visibility":"featured","source_url":"https://code.thetadev.de/ThetaDev/rustypipe","source_icon":"gitea","vid_count":1,"has_icon":false}} -{"type":"website","key":"spa","value":{"name":"SvelteKit SPA","created_at":[2023,62,22,0,0,0,0,0,0],"latest_version":1,"color":16727552,"visibility":"hidden","source_url":null,"source_icon":null,"vid_count":1,"has_icon":false}} -{"type":"website","key":"spotify-gender-ex","value":{"name":"Spotify-Gender-Ex","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":1,"color":1947988,"visibility":"featured","source_url":"https://github.com/Theta-Dev/Spotify-Gender-Ex","source_icon":"github","vid_count":1,"has_icon":false}} +{"type":"website","key":"rustypipe","value":{"name":"RustyPipe","created_at":[2023,51,18,30,0,0,0,0,0],"latest_version":1,"color":7943647,"visibility":"featured","source_url":"https://code.thetadev.de/ThetaDev/rustypipe","source_icon":"gitea","vid_count":1,"has_icon":false,"updated_at":[2023,51,18,30,0,0,0,0,0]}} +{"type":"website","key":"spa","value":{"name":"SvelteKit SPA","created_at":[2023,62,22,0,0,0,0,0,0],"latest_version":1,"color":16727552,"visibility":"hidden","source_url":null,"source_icon":null,"vid_count":1,"has_icon":false,"updated_at":[2023,62,22,0,0,0,0,0,0]}} +{"type":"website","key":"spotify-gender-ex","value":{"name":"Spotify-Gender-Ex","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":1,"color":1947988,"visibility":"featured","source_url":"https://github.com/Theta-Dev/Spotify-Gender-Ex","source_icon":"github","vid_count":1,"has_icon":false,"updated_at":[2023,49,16,30,0,0,0,0,0]}} {"type":"version","key":"rustypipe:1","value":{"created_at":[2023,51,18,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}} {"type":"version","key":"spa:1","value":{"created_at":[2023,62,22,0,0,0,0,0,0],"data":{},"fallback":"200.html","spa":true}} {"type":"version","key":"spotify-gender-ex:1","value":{"created_at":[2023,49,16,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}} diff --git a/tests/snapshots/tests__database__export.snap b/tests/snapshots/tests__database__export.snap index 253a596..fa36114 100644 --- a/tests/snapshots/tests__database__export.snap +++ b/tests/snapshots/tests__database__export.snap @@ -2,10 +2,10 @@ source: tests/tests.rs expression: data --- -{"type":"website","key":"-","value":{"name":"ThetaDev","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":2,"color":2068974,"visibility":"featured","source_url":null,"source_icon":null,"vid_count":2,"has_icon":false}} -{"type":"website","key":"rustypipe","value":{"name":"RustyPipe","created_at":[2023,51,18,30,0,0,0,0,0],"latest_version":1,"color":7943647,"visibility":"featured","source_url":"https://code.thetadev.de/ThetaDev/rustypipe","source_icon":"gitea","vid_count":1,"has_icon":false}} -{"type":"website","key":"spa","value":{"name":"SvelteKit SPA","created_at":[2023,62,22,0,0,0,0,0,0],"latest_version":1,"color":16727552,"visibility":"hidden","source_url":null,"source_icon":null,"vid_count":1,"has_icon":false}} -{"type":"website","key":"spotify-gender-ex","value":{"name":"Spotify-Gender-Ex","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":1,"color":1947988,"visibility":"featured","source_url":"https://github.com/Theta-Dev/Spotify-Gender-Ex","source_icon":"github","vid_count":1,"has_icon":false}} +{"type":"website","key":"-","value":{"name":"ThetaDev","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":2,"color":2068974,"visibility":"featured","source_url":null,"source_icon":null,"vid_count":2,"has_icon":false,"updated_at":[2023,49,16,30,0,0,0,0,0]}} +{"type":"website","key":"rustypipe","value":{"name":"RustyPipe","created_at":[2023,51,18,30,0,0,0,0,0],"latest_version":1,"color":7943647,"visibility":"featured","source_url":"https://code.thetadev.de/ThetaDev/rustypipe","source_icon":"gitea","vid_count":1,"has_icon":false,"updated_at":[2023,51,18,30,0,0,0,0,0]}} +{"type":"website","key":"spa","value":{"name":"SvelteKit SPA","created_at":[2023,62,22,0,0,0,0,0,0],"latest_version":1,"color":16727552,"visibility":"hidden","source_url":null,"source_icon":null,"vid_count":1,"has_icon":false,"updated_at":[2023,62,22,0,0,0,0,0,0]}} +{"type":"website","key":"spotify-gender-ex","value":{"name":"Spotify-Gender-Ex","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":1,"color":1947988,"visibility":"featured","source_url":"https://github.com/Theta-Dev/Spotify-Gender-Ex","source_icon":"github","vid_count":1,"has_icon":false,"updated_at":[2023,49,16,30,0,0,0,0,0]}} {"type":"version","key":"-:1","value":{"created_at":[2023,49,16,30,0,0,0,0,0],"data":{"Deployed by":"https://github.com/Theta-Dev/Talon/actions/runs/1352014628","Version":"v0.1.0"},"fallback":null,"spa":false}} {"type":"version","key":"-:2","value":{"created_at":[2023,49,16,52,0,0,0,0,0],"data":{"Deployed by":"https://github.com/Theta-Dev/Talon/actions/runs/1354755231","Version":"v0.1.1"},"fallback":null,"spa":false}} {"type":"version","key":"rustypipe:1","value":{"created_at":[2023,51,18,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}} diff --git a/tests/snapshots/tests__database__get_website.snap b/tests/snapshots/tests__database__get_website.snap index 9d70532..864e3fe 100644 --- a/tests/snapshots/tests__database__get_website.snap +++ b/tests/snapshots/tests__database__get_website.snap @@ -13,6 +13,7 @@ expression: "vec![ws1, ws2, ws3]" source_icon: None, vid_count: 2, has_icon: false, + updated_at: Some((2023, 49, 16, 30, 0, 0, 0, 0, 0)), ), Website( name: "Spotify-Gender-Ex", @@ -24,6 +25,7 @@ expression: "vec![ws1, ws2, ws3]" source_icon: Some(github), vid_count: 1, has_icon: false, + updated_at: Some((2023, 49, 16, 30, 0, 0, 0, 0, 0)), ), Website( name: "RustyPipe", @@ -35,5 +37,6 @@ expression: "vec![ws1, ws2, ws3]" source_icon: Some(gitea), vid_count: 1, has_icon: false, + updated_at: Some((2023, 51, 18, 30, 0, 0, 0, 0, 0)), ), ] diff --git a/tests/snapshots/tests__database__get_websites.snap b/tests/snapshots/tests__database__get_websites.snap index 1aa368f..d512f7b 100644 --- a/tests/snapshots/tests__database__get_websites.snap +++ b/tests/snapshots/tests__database__get_websites.snap @@ -13,6 +13,7 @@ expression: websites source_icon: None, vid_count: 2, has_icon: false, + updated_at: Some((2023, 49, 16, 30, 0, 0, 0, 0, 0)), )), ("rustypipe", Website( name: "RustyPipe", @@ -24,6 +25,7 @@ expression: websites source_icon: Some(gitea), vid_count: 1, has_icon: false, + updated_at: Some((2023, 51, 18, 30, 0, 0, 0, 0, 0)), )), ("spa", Website( name: "SvelteKit SPA", @@ -35,6 +37,7 @@ expression: websites source_icon: None, vid_count: 1, has_icon: false, + updated_at: Some((2023, 62, 22, 0, 0, 0, 0, 0, 0)), )), ("spotify-gender-ex", Website( name: "Spotify-Gender-Ex", @@ -46,5 +49,6 @@ expression: websites source_icon: Some(github), vid_count: 1, has_icon: false, + updated_at: Some((2023, 49, 16, 30, 0, 0, 0, 0, 0)), )), ] diff --git a/tests/snapshots/tests__database__update_website.snap b/tests/snapshots/tests__database__update_website.snap index 155fa97..4477397 100644 --- a/tests/snapshots/tests__database__update_website.snap +++ b/tests/snapshots/tests__database__update_website.snap @@ -12,4 +12,5 @@ Website( source_icon: Some(link), vid_count: 2, has_icon: false, + updated_at: "[date]", ) diff --git a/tests/tests.rs b/tests/tests.rs index ae0fa25..c996b93 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -8,6 +8,8 @@ use rstest::rstest; use fixtures::*; use talon::db::{Db, DbError}; +const ICON_SIZE: u32 = 48; + mod database { use super::*; @@ -83,7 +85,7 @@ mod database { .unwrap(); let website = db.get_website(SUBDOMAIN_1).unwrap(); - insta::assert_ron_snapshot!(website); + insta::assert_ron_snapshot!(website, {".updated_at" => "[date]"}); } #[rstest] @@ -613,8 +615,8 @@ mod icons { assert!(stored_path.is_file()); let stored_img = ImageReader::open(&stored_path).unwrap().decode().unwrap(); - assert_eq!(stored_img.height(), 32); - assert_eq!(stored_img.width(), 32); + assert_eq!(stored_img.height(), ICON_SIZE); + assert_eq!(stored_img.width(), ICON_SIZE); } #[test] @@ -856,7 +858,7 @@ mod api { resp.assert_status_is_ok(); let ws = tln.db.get_website("test").unwrap(); - insta::assert_ron_snapshot!(ws, {".created_at" => "[date]"}, @r###" + insta::assert_ron_snapshot!(ws, {".created_at" => "[date]", ".updated_at" => "[date]"}, @r###" Website( name: "Test", created_at: "[date]", @@ -867,6 +869,7 @@ mod api { source_icon: Some(git), vid_count: 0, has_icon: false, + updated_at: "[date]", ) "###); } @@ -911,7 +914,7 @@ mod api { resp.assert_status_is_ok(); let ws = tln.db.get_website("-").unwrap(); - insta::assert_ron_snapshot!(ws, @r###" + insta::assert_ron_snapshot!(ws, {".updated_at" => "[date]"}, @r###" Website( name: "Test", created_at: (2023, 49, 16, 30, 0, 0, 0, 0, 0), @@ -922,6 +925,7 @@ mod api { source_icon: Some(git), vid_count: 2, has_icon: false, + updated_at: "[date]", ) "###); } @@ -967,8 +971,8 @@ mod api { .unwrap() .decode() .unwrap(); - assert_eq!(got_icon.height(), 32); - assert_eq!(got_icon.width(), 32); + assert_eq!(got_icon.height(), ICON_SIZE); + assert_eq!(got_icon.width(), ICON_SIZE); } #[rstest] diff --git a/ui/menu/src/App.svelte b/ui/menu/src/App.svelte index 57bc1ec..ca4b1cc 100644 --- a/ui/menu/src/App.svelte +++ b/ui/menu/src/App.svelte @@ -9,7 +9,6 @@ let currentWebsite: Website; currentWebsiteStore.subscribe((ws) => { - console.log("current ws changed", ws); currentWebsite = ws; }); diff --git a/ui/menu/src/components/PageInfoModal.svelte b/ui/menu/src/components/PageInfoModal.svelte index 2267a85..ee68f3e 100644 --- a/ui/menu/src/components/PageInfoModal.svelte +++ b/ui/menu/src/components/PageInfoModal.svelte @@ -2,6 +2,7 @@ import PageIcon from "./PageIcon.svelte"; import { formatDate, + getSubdomainAndVersion, getWebsiteVersionUrl, isUrl, trimCommit, @@ -14,28 +15,37 @@ import Modal from "./Modal.svelte"; import { openModal } from "svelte-modals"; import InstanceInfoModal from "./InstanceInfoModal.svelte"; + import { onMount } from "svelte"; let currentWebsite: Website; currentWebsiteStore.subscribe((ws) => { currentWebsite = ws; }); + const currentVid: number | null = getSubdomainAndVersion()[1]; + export let isOpen: boolean; - $: { - if (isOpen && currentWebsite) { - client - .websiteSubdomainVersionsGet({ subdomain: currentWebsite.subdomain }) - .then((v) => { - versions = v; - if (v && v.length > 0) { - currentVersion = v[v.length - 1]; - } - }); + + onMount(async () => { + const v = await client.websiteSubdomainVersionsGet({ + subdomain: currentWebsite.subdomain, + }); + + versions = v; + if (v && v.length > 0) { + latestVersion = v[v.length - 1]; + + if (currentVid !== null) { + currentVersion = v.find((v) => v.id == currentVid); + } else { + currentVersion = latestVersion; + } } - } + }); let versions: Version[] = []; - let currentVersion: Version = null; + let currentVersion: Version | undefined; + let latestVersion: Version | undefined; function getVersionAttr(version: Version): string | null { return ( @@ -63,6 +73,14 @@

Current version #{currentVersion.id} + + {#if latestVersion && latestVersion !== currentVersion} + Latest: #{latestVersion.id} + {/if}

@@ -95,7 +113,9 @@
Powered by - +
{/if} @@ -111,4 +131,13 @@ font-size: 2em margin-left: 0.25em + .latest-tag + background-color: lime + color: values.$color-text-d1 + margin: 0 1em + overflow: hidden + white-space: nowrap + padding: 0 0.4em + border-radius: 1em + diff --git a/ui/menu/src/style/main.sass b/ui/menu/src/style/main.sass index a93e2eb..7903d5d 100644 --- a/ui/menu/src/style/main.sass +++ b/ui/menu/src/style/main.sass @@ -6,15 +6,16 @@ font-family: sans-serif color: values.$color-text -a, button +a, .link display: inline text-decoration: none cursor: pointer background: none border: none box-shadow: none + padding: 0 -a +.link color: var(--talon-color) filter: brightness(150%) diff --git a/ui/menu/src/style/values.sass b/ui/menu/src/style/values.sass index d3995f4..973379b 100644 --- a/ui/menu/src/style/values.sass +++ b/ui/menu/src/style/values.sass @@ -13,3 +13,4 @@ $color-base-2: color.scale($color-base, $lightness: 20%) $color-primary-light: color.scale($color-primary, $lightness: 15%) $color-primary-dark: color.scale($color-primary, $lightness: -15%) $color-text-1: color.scale($color-text, $lightness: -15%) +$color-text-d1: color.scale($color-base, $lightness: -20%) diff --git a/ui/menu/src/util/functions.ts b/ui/menu/src/util/functions.ts index a0de6a4..4cc867e 100644 --- a/ui/menu/src/util/functions.ts +++ b/ui/menu/src/util/functions.ts @@ -21,6 +21,29 @@ export function getSubdomain(): string { return "-"; } +export function getSubdomainAndVersion(): [string, number | null] { + const hn = window.location.hostname; + const rd_noport = talonConfig.root_domain.split(":", 1)[0]; + + if (hn.endsWith("." + rd_noport)) { + const subdomainSplit = hn + .substring(0, hn.length - rd_noport.length - 1) + .split("--", 2); + const subdomain = subdomainSplit[0]; + + let version = + subdomainSplit.length > 1 ? parseInt(subdomainSplit[1].replace(/^v/, "")) : null; + if (Number.isNaN(version)) version = null; + + if (subdomain === "x") { + return ["-", version]; + } else { + return [subdomain, version]; + } + } + return ["-", null]; +} + export function getWebsiteUrl(subdomain: string): string { const proto = window.location.protocol;