prepare for private demos

This commit is contained in:
Robin Appelman 2025-04-02 20:37:03 +02:00
commit ffb230045c
6 changed files with 58 additions and 14 deletions

View file

@ -1,6 +1,6 @@
{ {
"db_name": "PostgreSQL", "db_name": "PostgreSQL",
"query": "SELECT\n demos.id, demos.name, url, map, red, blu, uploader, duration, demos.created_at,\n \"scoreRed\" as score_red, \"scoreBlue\" as score_blue, server, nick,\n \"playerCount\" as player_count,\n users_named.name as uploader_name_preferred,\n users.steamid as \"uploader_steam_id?: SteamId\",\n users.name as \"uploader_name?\"\n FROM demos\n LEFT JOIN users_named ON uploader = users_named.id\n LEFT JOIN users ON uploader = users.id\n WHERE deleted_at IS NULL AND demos.id = $1", "query": "SELECT\n demos.id, demos.name, url, map, red, blu, uploader, duration, demos.created_at,\n \"scoreRed\" as score_red, \"scoreBlue\" as score_blue, server, nick,\n \"playerCount\" as player_count,\n users_named.name as uploader_name_preferred,\n users.steamid as \"uploader_steam_id?: SteamId\",\n users.name as \"uploader_name?\",\n demos.private_until\n FROM demos\n LEFT JOIN users_named ON uploader = users_named.id\n LEFT JOIN users ON uploader = users.id\n WHERE deleted_at IS NULL AND demos.id = $1",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@ -87,6 +87,11 @@
"ordinal": 16, "ordinal": 16,
"name": "uploader_name?", "name": "uploader_name?",
"type_info": "Varchar" "type_info": "Varchar"
},
{
"ordinal": 17,
"name": "private_until",
"type_info": "Timestamptz"
} }
], ],
"parameters": { "parameters": {
@ -109,8 +114,9 @@
false, false,
true, true,
false, false,
false false,
true
] ]
}, },
"hash": "68e8265e13596ad6de3e3d346b6d2fb62a68eda3e98f690e7e8d954627d8025c" "hash": "7ea7c5f61e8c77898d8b816fce80f066026822e03d6f74bd4ae19d7c9d914426"
} }

View file

@ -10,10 +10,7 @@
inputs.flakelight.follows = "flakelight"; inputs.flakelight.follows = "flakelight";
}; };
}; };
outputs = { outputs = {mill-scale, ...}:
mill-scale,
...
}:
mill-scale ./. { mill-scale ./. {
packageOpts = {demostf-frontend-node-modules, ...}: { packageOpts = {demostf-frontend-node-modules, ...}: {
preBuild = '' preBuild = ''

View file

@ -40,6 +40,7 @@ pub struct Demo {
pub player_count: i32, pub player_count: i32,
pub players: Vec<Player>, pub players: Vec<Player>,
pub chat: Vec<Chat>, pub chat: Vec<Chat>,
pub private_until: Option<OffsetDateTime>,
} }
impl Debug for Demo { impl Debug for Demo {
@ -74,6 +75,7 @@ impl Demo {
pub server: String, pub server: String,
pub nick: String, pub nick: String,
pub player_count: i32, pub player_count: i32,
pub private_until: Option<OffsetDateTime>,
} }
let Some(raw) = query_as!( let Some(raw) = query_as!(
@ -84,7 +86,8 @@ impl Demo {
"playerCount" as player_count, "playerCount" as player_count,
users_named.name as uploader_name_preferred, users_named.name as uploader_name_preferred,
users.steamid as "uploader_steam_id?: SteamId", users.steamid as "uploader_steam_id?: SteamId",
users.name as "uploader_name?" users.name as "uploader_name?",
demos.private_until
FROM demos FROM demos
LEFT JOIN users_named ON uploader = users_named.id LEFT JOIN users_named ON uploader = users_named.id
LEFT JOIN users ON uploader = users.id LEFT JOIN users ON uploader = users.id
@ -120,6 +123,7 @@ impl Demo {
player_count: raw.player_count, player_count: raw.player_count,
players, players,
chat, chat,
private_until: raw.private_until,
})) }))
} }
@ -149,6 +153,37 @@ impl Demo {
pub fn viewer_url(&self) -> ViewerUrl { pub fn viewer_url(&self) -> ViewerUrl {
ViewerUrl(self.id) ViewerUrl(self.id)
} }
pub fn is_private(&self) -> bool {
if let Some(private_until) = self.private_until {
let now = OffsetDateTime::now_utc();
now < private_until
} else {
false
}
}
pub fn url(&self) -> &str {
if self.is_private() {
""
} else {
self.url.as_str()
}
}
pub fn private_until_text(&self) -> Cow<'static, str> {
if let Some(private_until) = self.private_until {
let now = OffsetDateTime::now_utc();
let days = (private_until - now).whole_days();
if days <= 1 {
"by tomorrow".into()
} else {
format!("in {days} days").into()
}
} else {
"".into()
}
}
} }
pub struct ViewerUrl(i32); pub struct ViewerUrl(i32);

View file

@ -23,7 +23,9 @@ impl Page for DemoPage {
fn render(&self) -> Markup { fn render(&self) -> Markup {
let style_url = ClassIconsStyle::url(); let style_url = ClassIconsStyle::url();
html! { html! {
@if self.demo.url.is_empty() { @if self.demo.is_private() {
h3.warning { "This demo is private, it will be available for download " (self.demo.private_until_text()) }
} @else if self.demo.url.is_empty() {
h3.warning { "This demo has been deleted and is no longer available for download." } h3.warning { "This demo has been deleted and is no longer available for download." }
} }
h2 { (self.demo.server) " - " (self.demo.red) " vs " (self.demo.blu) } h2 { (self.demo.server) " - " (self.demo.red) " vs " (self.demo.blu) }
@ -104,8 +106,8 @@ impl Page for DemoPage {
span.time { (self.demo.duration()) } span.time { (self.demo.duration()) }
} }
p.demo-download { p.demo-download {
@if !self.demo.url.is_empty() { @if !self.demo.url().is_empty() {
a.button.button-primary href = (self.demo.url) download = (self.demo.name) rel = "nofollow" { "Download" } a.button.button-primary href = (self.demo.url()) download = (self.demo.name) rel = "nofollow" { "Download" }
a.button href = (self.demo.viewer_url()) rel = "nofollow" { "View" } a.button href = (self.demo.viewer_url()) rel = "nofollow" { "View" }
} }
@if !self.demo.chat.is_empty() { @if !self.demo.chat.is_empty() {

View file

@ -53,8 +53,12 @@ impl Page for ViewerPage<'_> {
html! { html! {
.viewer-page data-maps = (maps) data-sync = (sync) { .viewer-page data-maps = (maps) data-sync = (sync) {
@if let Some(demo) = self.demo.as_ref() { @if let Some(demo) = self.demo.as_ref() {
@if demo.is_private() {
h3.warning { "This demo is private, it will be available for download " (demo.private_until_text()) }
} @else {
input type = "hidden" name = "url" value = (demo.url) {} input type = "hidden" name = "url" value = (demo.url) {}
progress.download min = "0" max = "100" value = "0" {} progress.download min = "0" max = "100" value = "0" {}
}
} @else { } @else {
.dropzone role = "button" { .dropzone role = "button" {
noscript { noscript {