mirror of
https://codeberg.org/simonrepp/hyper8.git
synced 2026-08-14 13:45:27 +02:00
Iterate on design, displayed details, ordering, search and responsive behavior
This commit is contained in:
@@ -7,7 +7,7 @@ body:not(.light) #toggle-dark {
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: .9rem;
|
||||
color: var(--fg-mg);
|
||||
color: var(--fg-3);
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
height: 1.8rem;
|
||||
|
||||
@@ -82,14 +82,29 @@ function initializeSiteTree() {
|
||||
image.classList.add('placeholder');
|
||||
}
|
||||
|
||||
const spanText = document.createElement('span');
|
||||
spanText.textContent = video.title;
|
||||
const divTitleDetails = document.createElement('div');
|
||||
|
||||
const spanTitle = document.createElement('span');
|
||||
spanTitle.textContent = video.title;
|
||||
divTitleDetails.appendChild(spanTitle);
|
||||
|
||||
const spanDetails = document.createElement('span');
|
||||
if (video.date) {
|
||||
if (video.duration) {
|
||||
spanDetails.textContent = `${video.date}, ${video.duration}`;
|
||||
} else {
|
||||
spanDetails.textContent = video.date;
|
||||
}
|
||||
} else if (video.duration) {
|
||||
spanDetails.textContent = video.duration;
|
||||
}
|
||||
divTitleDetails.appendChild(spanDetails);
|
||||
|
||||
video.element = document.createElement('a');
|
||||
video.element.classList.add('video');
|
||||
video.element.href = rootPrefix + video.sitePath + indexSuffix;
|
||||
video.element.appendChild(image);
|
||||
video.element.appendChild(spanText);
|
||||
video.element.appendChild(divTitleDetails);
|
||||
|
||||
if (video.sitePath === sitePath) {
|
||||
video.element.classList.add('current');
|
||||
@@ -296,10 +311,16 @@ function updateSearch() {
|
||||
|
||||
if (query.length) {
|
||||
const regexp = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
|
||||
|
||||
// If the query is four digits we additionally try to match it against
|
||||
// video dates.
|
||||
const yearQuery = /^\d{4}$/.test(query);
|
||||
|
||||
let shown = 0;
|
||||
|
||||
function updateVideo(video) {
|
||||
const display = regexp.test(video.title);
|
||||
const display = regexp.test(video.title) ||
|
||||
!!(yearQuery && video.date && video.date.includes(query));
|
||||
video.element.classList.toggle('visible', display);
|
||||
if (display) { shown += 1; }
|
||||
return display;
|
||||
|
||||
+79
-58
@@ -13,7 +13,8 @@ a:hover { color: var(--fg-3); }
|
||||
column-gap: .3rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: .3rem .6rem .3rem .5rem;
|
||||
font-size: .9rem;
|
||||
padding: .3rem .4rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.actions a:focus-visible,
|
||||
@@ -111,6 +112,7 @@ button {
|
||||
footer {
|
||||
align-items: center;
|
||||
background: var(--bg-1);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
font-size: .9rem;
|
||||
flex-wrap: wrap;
|
||||
@@ -178,6 +180,7 @@ img {
|
||||
gap: 1rem;
|
||||
}
|
||||
.links {
|
||||
font-size: .9rem;
|
||||
margin-bottom: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
@@ -188,7 +191,7 @@ img {
|
||||
color: var(--fg-1);
|
||||
column-gap: .3rem;
|
||||
display: flex;
|
||||
padding: .3rem .6rem .3rem .5rem;
|
||||
padding: .3rem .4rem;
|
||||
width: max-content;
|
||||
}
|
||||
.links > a:not(:first-child) { margin-top: .5rem; }
|
||||
@@ -203,25 +206,23 @@ img {
|
||||
padding: .5rem;
|
||||
}
|
||||
.medium_font { font-size: 1.2rem; }
|
||||
.outline {
|
||||
flex-shrink: 0;
|
||||
min-width: 20rem;
|
||||
}
|
||||
.parent_context {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
font-size: .9rem;
|
||||
gap: .3rem;
|
||||
margin-bottom: .2rem;
|
||||
}
|
||||
.parent_context a:not(.context_dot) {
|
||||
align-items: center;
|
||||
column-gap: .3rem;
|
||||
display: flex;
|
||||
}
|
||||
.parent_context > div {
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .3rem;
|
||||
font-size: .9rem;
|
||||
gap: .5rem;
|
||||
margin-bottom: .2rem;
|
||||
}
|
||||
.parent_context > div > div:nth-child(2) { display: flex; }
|
||||
.parent_context.inline {
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.parent_context > div:nth-child(2) { display: flex; }
|
||||
.parents {
|
||||
font-size: .9rem;
|
||||
margin-top: .2rem;
|
||||
@@ -349,28 +350,26 @@ img {
|
||||
@media (max-aspect-ratio: 2/3) {
|
||||
.player_wrapper:not(:fullscreen) video { max-height: 76dvh; }
|
||||
}
|
||||
.playlist_context { margin-top: 6.1rem; }
|
||||
.playlist_context > a {
|
||||
align-items: center;
|
||||
border-radius: .3rem;
|
||||
display: flex;
|
||||
outline: none;
|
||||
position: relative;
|
||||
}
|
||||
.playlist_context > a:first-child {
|
||||
border-top-left-radius: .4rem;
|
||||
border-top-right-radius: .4rem;
|
||||
}
|
||||
.playlist_context > a:first-child img { border-top-left-radius: .4rem }
|
||||
.playlist_context > a:last-child {
|
||||
border-bottom-left-radius: .4rem;
|
||||
border-bottom-right-radius: .4rem;
|
||||
}
|
||||
.playlist_context > a:last-child img { border-bottom-left-radius: .4rem }
|
||||
.playlist_context > a:focus-visible,
|
||||
.playlist_context > a:hover { background: var(--bg-2); }
|
||||
.playlist_context > a:not(:last-child) {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
.playlist_context .current { background: var(--bg-3); }
|
||||
.playlist_context .current::after {
|
||||
background: var(--action);
|
||||
border-radius: .1rem;
|
||||
content: '';
|
||||
height: 3rem;
|
||||
margin-left: -.2rem;
|
||||
width: .2rem;
|
||||
}
|
||||
.playlist_context .description {
|
||||
align-items: flex-start;
|
||||
color: var(--fg-1);
|
||||
@@ -380,35 +379,26 @@ img {
|
||||
justify-content: center;
|
||||
padding: .2rem .8rem;
|
||||
}
|
||||
.playlist_context .details { font-size: .8rem; }
|
||||
.playlist_context .duration { color: var(--fg-3); }
|
||||
.playlist_context .label {
|
||||
font-size: .9rem;
|
||||
max-width: 15rem; /* TODO: Resolve this hack, need dynamic fitting into max-width */
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.playlist_context .more {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 4rem;
|
||||
justify-content: center;
|
||||
}
|
||||
.playlist_context .number {
|
||||
background: var(--bg-1);
|
||||
border-bottom-left-radius: .2rem;
|
||||
.playlist_context .details {
|
||||
color: var(--fg-3);
|
||||
font-size: .8rem;
|
||||
padding: 0 .4rem 0 .5rem;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
.playlist_context .label {
|
||||
font-size: .9rem;
|
||||
max-width: 15rem;
|
||||
}
|
||||
.playlist_context .number_context {
|
||||
background: var(--fg-3);
|
||||
border-radius: .2rem;
|
||||
color: var(--bg-1);
|
||||
font-size: .8rem;
|
||||
margin-bottom: .2rem;
|
||||
padding: .15rem .36rem;
|
||||
}
|
||||
.playlist_context img {
|
||||
aspect-ratio: var(--poster-aspect);
|
||||
background: var(--d-1);
|
||||
border-radius: 0;
|
||||
border-radius: .3rem;
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
height: 4rem;
|
||||
@@ -495,6 +485,7 @@ img {
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--bg-3);
|
||||
border-radius: .5em;
|
||||
box-shadow: 0 0 2rem var(--d-1-15);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
max-height: 80dvh;
|
||||
@@ -502,7 +493,7 @@ img {
|
||||
overflow-y: auto;
|
||||
padding: 1rem;
|
||||
position: absolute;
|
||||
top: 3.5rem;
|
||||
top: 3.2rem;
|
||||
z-index: 2;
|
||||
}
|
||||
.site_tree [role="status"]:not(:empty) {
|
||||
@@ -520,13 +511,13 @@ img {
|
||||
display: flex;
|
||||
}
|
||||
.site_tree .elements a.current::before {
|
||||
width: 0.2rem;
|
||||
margin-left: -0.2rem;
|
||||
right: 0;
|
||||
height: 1rem;
|
||||
background: var(--action);
|
||||
border-radius: .1rem;
|
||||
content: '';
|
||||
border-radius: 0.1rem;
|
||||
height: 1rem;
|
||||
margin-left: -.2rem;
|
||||
right: 0;
|
||||
width: .2rem;
|
||||
}
|
||||
.site_tree .elements a:focus-visible,
|
||||
.site_tree .elements a:hover {
|
||||
@@ -551,6 +542,10 @@ img {
|
||||
font-size: .6rem;
|
||||
padding: .2rem;
|
||||
}
|
||||
.site_tree .elements .collection:hover > :nth-child(1) svg,
|
||||
.site_tree .elements .playlist:hover > :nth-child(1) svg {
|
||||
background: var(--bg-1);
|
||||
}
|
||||
.site_tree .elements .collection > :nth-child(1):hover svg,
|
||||
.site_tree .elements .playlist > :nth-child(1):hover svg {
|
||||
background: var(--fg-3);
|
||||
@@ -585,8 +580,15 @@ img {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
.site_tree .elements .video > :nth-child(2) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
margin-left: .7rem;
|
||||
row-gap: .1rem;
|
||||
}
|
||||
.site_tree .elements .video > :nth-child(2) > :nth-child(2) {
|
||||
color: var(--fg-3);
|
||||
font-size: .8rem;
|
||||
}
|
||||
.site_tree .elements .placeholder { background: var(--bg-3); }
|
||||
.site_tree:not(.open) { display: none; }
|
||||
@@ -714,10 +716,19 @@ video {
|
||||
width: 100%;
|
||||
}
|
||||
.video_info { margin-bottom: 1.5rem; }
|
||||
.video_info.top {
|
||||
align-items: center;
|
||||
column-gap: 3rem;
|
||||
display: flex;
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.video_title {
|
||||
color: var(--fg-1);
|
||||
font-size: 1.4rem;
|
||||
margin: 0;
|
||||
margin-bottom: .2em;
|
||||
margin-bottom: .3em;
|
||||
}
|
||||
.videos {
|
||||
column-gap: 1rem;
|
||||
@@ -756,6 +767,14 @@ video {
|
||||
@media (min-width: 30rem) {
|
||||
.videos_grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 40rem) {
|
||||
.parent_context { align-items: flex-start; }
|
||||
.video_info.top {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
.video_title { font-size: 1.2rem; }
|
||||
}
|
||||
@media (min-width: 44rem) {
|
||||
.videos_grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
}
|
||||
@@ -764,6 +783,7 @@ video {
|
||||
background: linear-gradient(var(--bg-1), var(--bg-1-85));
|
||||
border: none;
|
||||
bottom: 0;
|
||||
box-shadow: unset;
|
||||
left: 0;
|
||||
max-height: unset;
|
||||
padding: .5rem;
|
||||
@@ -777,6 +797,7 @@ video {
|
||||
@media (min-width: 55rem) {
|
||||
.layout_split { flex-direction: row; }
|
||||
.layout_split > :nth-child(1) { flex-grow: 1; }
|
||||
.playlist_context { margin-top: 6.3rem; }
|
||||
}
|
||||
@media (min-width: 58rem) {
|
||||
.videos_grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||
|
||||
+42
-18
@@ -16,6 +16,7 @@ use crate::build::widgets::{banner_html, poster_html};
|
||||
use crate::util::{
|
||||
casual_duration,
|
||||
html_escape_outside_attribute,
|
||||
localized_date_d_mmm_yyyy,
|
||||
localized_date_mmm_yyyy
|
||||
};
|
||||
|
||||
@@ -457,7 +458,8 @@ impl TraversalStep<'_> {
|
||||
let label = collection.title_or_slug_or_generic_label(translations);
|
||||
let label_escaped = html_escape_outside_attribute(label);
|
||||
|
||||
let video_bubbles = collection.public_videos()
|
||||
let video_bubbles = collection.public_videos_in_collection_video_order()
|
||||
.iter()
|
||||
.map(|video| {
|
||||
if let Some(poster_meta) = &video.poster.as_ref().and_then(|poster| poster.poster_meta.as_ref()) {
|
||||
let background = poster_meta.lqip.gradient_radial();
|
||||
@@ -471,10 +473,11 @@ impl TraversalStep<'_> {
|
||||
.join("");
|
||||
|
||||
let count = collection.public_videos().count();
|
||||
let inline = if count > 3 { "" } else { "inline" };
|
||||
|
||||
format!(r#"
|
||||
<div>
|
||||
<div><a href="{prefix}{index_suffix_file_only}">{label_escaped}</a></div>
|
||||
<div class="parent_context {inline}">
|
||||
<a href="{prefix}{index_suffix_file_only}">{label_escaped}</a>
|
||||
<div style="--count: {count}">{video_bubbles}</div>
|
||||
</div>
|
||||
"#)
|
||||
@@ -483,7 +486,8 @@ impl TraversalStep<'_> {
|
||||
let label = playlist.title_or_slug_or_generic_label(translations);
|
||||
let label_escaped = html_escape_outside_attribute(label);
|
||||
|
||||
let video_bubbles = playlist.public_videos()
|
||||
let video_bubbles = playlist.public_videos_in_playlist_order()
|
||||
.iter()
|
||||
.map(|video| {
|
||||
if let Some(poster_meta) = &video.poster.as_ref().and_then(|poster| poster.poster_meta.as_ref()) {
|
||||
let background = poster_meta.lqip.gradient_radial();
|
||||
@@ -497,10 +501,11 @@ impl TraversalStep<'_> {
|
||||
.join("");
|
||||
|
||||
let count = playlist.public_videos().count();
|
||||
let inline = if count > 3 { "" } else { "inline" };
|
||||
|
||||
format!(r#"
|
||||
<div>
|
||||
<div><a href="{prefix}{index_suffix_file_only}">{label_escaped}</a></div>
|
||||
<div class="parent_context {inline}">
|
||||
<a href="{prefix}{index_suffix_file_only}">{label_escaped}</a>
|
||||
<div style="--count: {count}">{video_bubbles}</div>
|
||||
</div>
|
||||
"#)
|
||||
@@ -757,28 +762,47 @@ impl TraversalStep<'_> {
|
||||
format!("../{slug}/")
|
||||
};
|
||||
|
||||
let thumbnail_rendered = thumbnail(&site.language.translations, video_iterated, &video_prefix, false);
|
||||
let thumbnail_rendered = thumbnail(translations, video_iterated, &video_prefix, false);
|
||||
|
||||
let (current, href) = if video_iterated.site_path == video.site_path {
|
||||
("current", String::new())
|
||||
let current;
|
||||
let href;
|
||||
let number_context;
|
||||
if video_iterated.site_path == video.site_path {
|
||||
current = "current";
|
||||
href = String::new();
|
||||
let count = videos.len();
|
||||
let number = index + 1;
|
||||
let playlist_label = playlist.title_or_slug_or_generic_label(translations);
|
||||
let t_video_xxx_of_xxx = translations.video_xxx_of_xxx(number, count);
|
||||
number_context = format!(r#"<span class="number_context">{playlist_label} – {t_video_xxx_of_xxx}</span>"#);
|
||||
} else {
|
||||
("", format!("../{slug}{index_suffix}"))
|
||||
};
|
||||
current = "";
|
||||
href = format!("../{slug}{index_suffix}");
|
||||
number_context = String::new();
|
||||
}
|
||||
|
||||
let duration = match &video_iterated.highest_quality_version().and_then(|version| version.video_meta.as_ref()) {
|
||||
Some(video_meta) => casual_duration(video_meta.duration, &site.language.translations),
|
||||
None => String::new()
|
||||
};
|
||||
let mut details = Vec::new();
|
||||
|
||||
if let Some(release_date) = video.release_date {
|
||||
let date = localized_date_d_mmm_yyyy(release_date, site.language.chrono_locale);
|
||||
details.push(date);
|
||||
}
|
||||
|
||||
if let Some(video_meta) = &video_iterated.highest_quality_version().and_then(|version| version.video_meta.as_ref()) {
|
||||
let duration = casual_duration(video_meta.duration, translations);
|
||||
details.push(duration);
|
||||
}
|
||||
|
||||
let details = details.join(", ");
|
||||
|
||||
let number = index + 1;
|
||||
formatdoc!(r#"
|
||||
<a class="{current}" href="{href}">
|
||||
<div class="number">{number}</div>
|
||||
{thumbnail_rendered}
|
||||
<div class="description">
|
||||
{number_context}
|
||||
<span class="label">{label}</span>
|
||||
<span class="details">
|
||||
<span class="duration">{duration}</span>
|
||||
{details}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
+37
-16
@@ -8,13 +8,17 @@ use indoc::formatdoc;
|
||||
use crate::{
|
||||
Collection,
|
||||
Context,
|
||||
Language,
|
||||
Playlist,
|
||||
Site,
|
||||
SiteContent,
|
||||
Translations,
|
||||
Video
|
||||
};
|
||||
use crate::util::url_safe_hash_base64;
|
||||
use crate::util::{
|
||||
casual_duration,
|
||||
localized_date_d_mmm_yyyy,
|
||||
url_safe_hash_base64
|
||||
};
|
||||
|
||||
use super::AssetHashes;
|
||||
|
||||
@@ -44,9 +48,10 @@ pub fn generate_navigation_js(
|
||||
context: &Context,
|
||||
site: &Site
|
||||
) {
|
||||
let translations = &site.language.translations;
|
||||
let language = &site.language;
|
||||
let translations = &language.translations;
|
||||
|
||||
fn video_js(translations: &Translations, video: &Video) -> String {
|
||||
fn video_js(language: &Language, video: &Video) -> String {
|
||||
let image;
|
||||
let lqip;
|
||||
match &video.poster {
|
||||
@@ -70,49 +75,65 @@ pub fn generate_navigation_js(
|
||||
}
|
||||
}
|
||||
|
||||
let date = match video.release_date {
|
||||
Some(release_date) => {
|
||||
let date = localized_date_d_mmm_yyyy(release_date, language.chrono_locale);
|
||||
format!("date:'{date}',")
|
||||
}
|
||||
None => String::new()
|
||||
};
|
||||
|
||||
let duration = match &video.highest_quality_version().and_then(|version| version.video_meta.as_ref()) {
|
||||
Some(video_meta) => {
|
||||
let duration = casual_duration(video_meta.duration, &language.translations);
|
||||
format!("duration:'{duration}',")
|
||||
}
|
||||
None => String::new()
|
||||
};
|
||||
|
||||
let site_path = video.site_path.normalized();
|
||||
let title = video.title_or_slug_or_generic_label(&translations);
|
||||
let title = video.title_or_slug_or_generic_label(&language.translations);
|
||||
let title_escaped = js_escape_inside_single_quoted_string(title);
|
||||
|
||||
format!("{{{lqip}{image}sitePath:'{site_path}',title:'{title_escaped}'}}")
|
||||
format!("{{{date}{duration}{lqip}{image}sitePath:'{site_path}',title:'{title_escaped}'}}")
|
||||
}
|
||||
|
||||
fn playlist_js(playlist: &Playlist, translations: &Translations) -> String {
|
||||
let title = playlist.title_or_slug_or_generic_label(&translations);
|
||||
fn playlist_js(language: &Language, playlist: &Playlist) -> String {
|
||||
let title = playlist.title_or_slug_or_generic_label(&language.translations);
|
||||
let site_path = playlist.site_path.normalized();
|
||||
|
||||
let title_escaped = js_escape_inside_single_quoted_string(title);
|
||||
|
||||
let videos = playlist.public_videos_in_playlist_order()
|
||||
.iter()
|
||||
.map(|video| video_js(translations, &video))
|
||||
.map(|video| video_js(language, &video))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
format!("{{sitePath:'{site_path}',title:'{title_escaped}',videos:[{videos}]}}")
|
||||
}
|
||||
|
||||
fn collection_js(collection: &Collection, translations: &Translations) -> String {
|
||||
let title = collection.title_or_slug_or_generic_label(&translations);
|
||||
fn collection_js(collection: &Collection, language: &Language) -> String {
|
||||
let title = collection.title_or_slug_or_generic_label(&language.translations);
|
||||
let site_path = collection.site_path.normalized();
|
||||
|
||||
let title_escaped = js_escape_inside_single_quoted_string(title);
|
||||
|
||||
let playlists = collection.public_playlists_desc_by_release_date()
|
||||
.iter()
|
||||
.map(|playlist| playlist_js(&playlist, translations))
|
||||
.map(|playlist| playlist_js(language, &playlist))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
let subcollections = collection.public_subcollections_desc_by_release_date()
|
||||
.iter()
|
||||
.map(|subcollection| collection_js(&subcollection, translations))
|
||||
.map(|subcollection| collection_js(&subcollection, language))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
let videos = collection.public_videos_in_collection_video_order()
|
||||
.iter()
|
||||
.map(|video| video_js(translations, &video))
|
||||
.map(|video| video_js(language, &video))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
@@ -127,14 +148,14 @@ pub fn generate_navigation_js(
|
||||
|
||||
let site_content = match &site.content {
|
||||
SiteContent::Collection(collection) => {
|
||||
let r_collection = collection_js(collection, translations);
|
||||
let r_collection = collection_js(collection, language);
|
||||
let site_content = format!("{{collection:{r_collection}}}");
|
||||
Some(site_content)
|
||||
}
|
||||
SiteContent::Empty => None,
|
||||
SiteContent::Invalid { .. } => None,
|
||||
SiteContent::Playlist(playlist) => {
|
||||
let r_playlist = playlist_js(playlist, translations);
|
||||
let r_playlist = playlist_js(language, playlist);
|
||||
let site_content = format!("{{playlist:{r_playlist}}}");
|
||||
Some(site_content)
|
||||
}
|
||||
|
||||
+4
-12
@@ -87,13 +87,7 @@ pub fn video_html(
|
||||
|
||||
let parent_context = if traversal.steps.len() > 1 {
|
||||
let parent_step = &traversal.steps[traversal.steps.len() - 2];
|
||||
let context_with_bubbles = parent_step.context_with_bubbles("../", site);
|
||||
|
||||
formatdoc!(r#"
|
||||
<div class="parent_context">
|
||||
{context_with_bubbles}
|
||||
</div>
|
||||
"#)
|
||||
parent_step.context_with_bubbles("../", site)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
@@ -148,11 +142,9 @@ pub fn video_html(
|
||||
|
||||
let video_content = formatdoc!(r#"
|
||||
<div>
|
||||
<div class="video_info">
|
||||
<div class="align apart flex space wrap" style="margin-top: 2rem;">
|
||||
<h1 class="video_title">{e_title} {r_unlisted_badge}</h1>
|
||||
{parent_context}
|
||||
</div>
|
||||
<div class="video_info top">
|
||||
<h1 class="video_title">{e_title} {r_unlisted_badge}</h1>
|
||||
{parent_context}
|
||||
</div>
|
||||
{player_rendered}
|
||||
<div class="video_info">
|
||||
|
||||
@@ -274,6 +274,7 @@ pub struct Translations {
|
||||
pub video_order: &'static str,
|
||||
pub video_player_widget_for_xxx: &'static str,
|
||||
pub video_settings: &'static str,
|
||||
video_xxx_of_xxx: &'static str,
|
||||
pub videos: &'static str,
|
||||
pub vp9_missing_browser_support_not_recommended: &'static str,
|
||||
pub warm: &'static str,
|
||||
@@ -568,6 +569,7 @@ impl Translations {
|
||||
video_order: "Video Reihenfolge",
|
||||
video_player_widget_for_xxx: r#"Videoplayer-Widget für "{title}""#,
|
||||
video_settings: "Video Einstellungen",
|
||||
video_xxx_of_xxx: "Video {current} von {total}",
|
||||
videos: "Videos",
|
||||
vp9_missing_browser_support_not_recommended: "VP9 – Fehlende Browserunterstützung (Nicht empfohlen)",
|
||||
warm: "Warm",
|
||||
@@ -863,6 +865,7 @@ impl Translations {
|
||||
video_order: "Video Order",
|
||||
video_player_widget_for_xxx: r#"Video player widget for "{title}""#,
|
||||
video_settings: "Video settings",
|
||||
video_xxx_of_xxx: "Video {current} of {total}",
|
||||
videos: "Videos",
|
||||
vp9_missing_browser_support_not_recommended: "VP9 – Missing browser support (Not recommended)",
|
||||
warm: "Warm",
|
||||
@@ -965,6 +968,16 @@ impl Translations {
|
||||
self.video_player_widget_for_xxx.replace("{title}", title)
|
||||
}
|
||||
|
||||
pub fn video_xxx_of_xxx(
|
||||
&self,
|
||||
current: usize,
|
||||
total: usize
|
||||
) -> String {
|
||||
self.video_xxx_of_xxx
|
||||
.replace("{current}", ¤t.to_string())
|
||||
.replace("{total}", &total.to_string())
|
||||
}
|
||||
|
||||
pub fn xxx_characters_maximum(&self, maximum: usize) -> String {
|
||||
self.xxx_characters_maximum.replace("{maximum}", &maximum.to_string())
|
||||
}
|
||||
@@ -1000,6 +1013,8 @@ fn check_translations() {
|
||||
assert!(&translations.updating_video_from_editor_currently_not_allowed.contains("{video_title}"));
|
||||
assert!(&translations.updating_video_from_editor_currently_not_allowed.contains("{manifest_path}"));
|
||||
assert!(&translations.video_player_widget_for_xxx.contains("{title}"));
|
||||
assert!(&translations.video_xxx_of_xxx.contains("{current}"));
|
||||
assert!(&translations.video_xxx_of_xxx.contains("{total}"));
|
||||
assert!(&translations.xxx_characters_maximum.contains("{maximum}"));
|
||||
|
||||
let disallowed_char = |c: char| !c.is_ascii_alphanumeric() && c != '-';
|
||||
|
||||
+19
@@ -196,6 +196,24 @@ pub fn human_size(bytes: u64) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// E.g. 1 Jan 2000
|
||||
pub fn localized_date_d_mmm_yyyy(date: NaiveDate, chrono_locale: ChronoLocale) -> String {
|
||||
use chrono::format::{Fixed, Item, Numeric, Pad};
|
||||
|
||||
let format = [
|
||||
Item::Numeric(Numeric::Day, Pad::None),
|
||||
Item::Space(" "),
|
||||
Item::Fixed(Fixed::ShortMonthName),
|
||||
Item::Space(" "),
|
||||
Item::Numeric(Numeric::Year, Pad::None)
|
||||
];
|
||||
|
||||
date
|
||||
.format_localized_with_items(format.iter(), chrono_locale)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// E.g. 1 January 2000
|
||||
pub fn localized_date_d_mmmm_yyyy(date: NaiveDate, chrono_locale: ChronoLocale) -> String {
|
||||
use chrono::format::{Fixed, Item, Numeric, Pad};
|
||||
|
||||
@@ -212,6 +230,7 @@ pub fn localized_date_d_mmmm_yyyy(date: NaiveDate, chrono_locale: ChronoLocale)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// E.g. Jan 2000
|
||||
pub fn localized_date_mmm_yyyy(date: NaiveDate, chrono_locale: ChronoLocale) -> String {
|
||||
use chrono::format::{Fixed, Item, Numeric, Pad};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user