mirror of
https://github.com/glanceapp/glance.git
synced 2025-06-22 19:01:25 +02:00
Refactor
Delay showing page content until JS has finished setting up page elements That then allows the following: Leave relative time to be rendered on the client Leave collapsible lists to be rendered on the client Which massively simplfies the backend templates which were error prone
This commit is contained in:
parent
de965dace8
commit
36f8eac3e4
@ -57,6 +57,14 @@
|
|||||||
font-size: var(--font-size-h4);
|
font-size: var(--font-size-h4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-content, .page.content-ready .page-loading-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page.content-ready > .page-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.page-column-full .size-title-dynamic {
|
.page-column-full .size-title-dynamic {
|
||||||
font-size: var(--font-size-h3);
|
font-size: var(--font-size-h3);
|
||||||
}
|
}
|
||||||
@ -117,6 +125,14 @@
|
|||||||
padding-top: var(--list-half-gap);
|
padding-top: var(--list-half-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-collapsible:not(.list-collapsible-expanded) > .list-collapsible-item {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-collapsible-item {
|
||||||
|
animation: listItemReveal 0.3s backwards;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes listItemReveal {
|
@keyframes listItemReveal {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@ -124,56 +140,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-collapsible-item {
|
|
||||||
display: none;
|
|
||||||
animation: listItemReveal 0.3s backwards;
|
|
||||||
animation-delay: var(--animation-delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-collapsible-label {
|
.list-collapsible-label {
|
||||||
display: flex;
|
font: inherit;
|
||||||
align-items: center;
|
border: 0;
|
||||||
gap: 1rem;
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
color: var(--color-text-base);
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: var(--font-size-h4);
|
||||||
padding: var(--widget-content-vertical-padding) 0;
|
padding: var(--widget-content-vertical-padding) 0;
|
||||||
background: var(--color-widget-background);
|
background: var(--color-widget-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-collapsible-label:has(.list-collapsible-input:checked) {
|
.list-collapsible-label-expanded {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-collapsible:has(+ .list-collapsible-label > .list-collapsible-input:checked) .list-collapsible-item {
|
.list-collapsible-label-icon {
|
||||||
display: block;
|
display: inline-block;
|
||||||
|
margin-left: 1rem;
|
||||||
|
position: relative;
|
||||||
|
top: -.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-collapsible-input {
|
.list-collapsible-label-icon::before {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-collapsible-label::before, .list-collapsible-label::after {
|
|
||||||
cursor: pointer;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-collapsible-label::before {
|
|
||||||
content: 'SHOW MORE';
|
|
||||||
font-size: var(--font-size-h4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-collapsible-label:has(.list-collapsible-input:checked)::before {
|
|
||||||
content: 'SHOW LESS';
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-collapsible-label::after {
|
|
||||||
content: '';
|
content: '';
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
display: inline-block;
|
||||||
transition: transform 0.3s;
|
transition: transform 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-collapsible-label:has(.list-collapsible-input:checked)::after {
|
.list-collapsible-label-expanded .list-collapsible-label-icon::before {
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1107,7 +1109,7 @@ body {
|
|||||||
box-shadow: 0 calc(var(--spacing) * -1) 0 0 currentColor, 0 var(--spacing) 0 0 currentColor;
|
box-shadow: 0 calc(var(--spacing) * -1) 0 0 currentColor, 0 var(--spacing) 0 0 currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-collapsible-label:has(.list-collapsible-input:checked) {
|
.list-collapsible-label-expanded {
|
||||||
bottom: var(--mobile-navigation-height);
|
bottom: var(--mobile-navigation-height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ function throttledDebounce(callback, maxDebounceTimes, debounceDelay) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
async function fetchPageContents (pageSlug) {
|
async function fetchPageContent(pageSlug) {
|
||||||
// TODO: handle non 200 status codes/time outs
|
// TODO: handle non 200 status codes/time outs
|
||||||
// TODO: add retries
|
// TODO: add retries
|
||||||
const response = await fetch(`/api/pages/${pageSlug}/content/`);
|
const response = await fetch(`/api/pages/${pageSlug}/content/`);
|
||||||
@ -33,6 +33,10 @@ async function fetchPageContents (pageSlug) {
|
|||||||
function setupCarousels() {
|
function setupCarousels() {
|
||||||
const carouselElements = document.getElementsByClassName("carousel-container");
|
const carouselElements = document.getElementsByClassName("carousel-container");
|
||||||
|
|
||||||
|
if (carouselElements.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < carouselElements.length; i++) {
|
for (let i = 0; i < carouselElements.length; i++) {
|
||||||
const carousel = carouselElements[i];
|
const carousel = carouselElements[i];
|
||||||
carousel.classList.add("show-right-cutoff");
|
carousel.classList.add("show-right-cutoff");
|
||||||
@ -57,7 +61,7 @@ function setupCarousels() {
|
|||||||
itemsContainer.addEventListener("scroll", determineSideCutoffsRateLimited);
|
itemsContainer.addEventListener("scroll", determineSideCutoffsRateLimited);
|
||||||
document.addEventListener("resize", determineSideCutoffsRateLimited);
|
document.addEventListener("resize", determineSideCutoffsRateLimited);
|
||||||
|
|
||||||
determineSideCutoffs();
|
setTimeout(determineSideCutoffs, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +112,8 @@ function setupDynamicRelativeTime() {
|
|||||||
const updateInterval = 60 * 1000;
|
const updateInterval = 60 * 1000;
|
||||||
let lastUpdateTime = Date.now();
|
let lastUpdateTime = Date.now();
|
||||||
|
|
||||||
|
updateRelativeTimeForElements(elements);
|
||||||
|
|
||||||
const updateElementsAndTimestamp = () => {
|
const updateElementsAndTimestamp = () => {
|
||||||
updateRelativeTimeForElements(elements);
|
updateRelativeTimeForElements(elements);
|
||||||
lastUpdateTime = Date.now();
|
lastUpdateTime = Date.now();
|
||||||
@ -154,6 +160,7 @@ function setupLazyImages() {
|
|||||||
image.classList.add("finished-transition");
|
image.classList.add("finished-transition");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
for (let i = 0; i < images.length; i++) {
|
for (let i = 0; i < images.length; i++) {
|
||||||
const image = images[i];
|
const image = images[i];
|
||||||
|
|
||||||
@ -168,21 +175,86 @@ function setupLazyImages() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupCollapsibleLists() {
|
||||||
|
const collapsibleListElements = document.getElementsByClassName("list-collapsible");
|
||||||
|
|
||||||
|
if (collapsibleListElements.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const showMoreText = "Show more";
|
||||||
|
const showLessText = "Show less";
|
||||||
|
|
||||||
|
const attachExpandToggleButton = (listElement) => {
|
||||||
|
let expanded = false;
|
||||||
|
const button = document.createElement("button");
|
||||||
|
const arrowElement = document.createElement("span");
|
||||||
|
arrowElement.classList.add("list-collapsible-label-icon");
|
||||||
|
const textNode = document.createTextNode(showMoreText);
|
||||||
|
button.classList.add("list-collapsible-label");
|
||||||
|
button.append(textNode, arrowElement);
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
if (expanded) {
|
||||||
|
listElement.classList.remove("list-collapsible-expanded");
|
||||||
|
button.classList.remove("list-collapsible-label-expanded");
|
||||||
|
textNode.nodeValue = showMoreText;
|
||||||
|
} else {
|
||||||
|
listElement.classList.add("list-collapsible-expanded");
|
||||||
|
button.classList.add("list-collapsible-label-expanded");
|
||||||
|
textNode.nodeValue = showLessText;
|
||||||
|
}
|
||||||
|
|
||||||
|
expanded = !expanded;
|
||||||
|
});
|
||||||
|
|
||||||
|
listElement.after(button);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < collapsibleListElements.length; i++) {
|
||||||
|
const listElement = collapsibleListElements[i];
|
||||||
|
|
||||||
|
if (listElement.dataset.collapseAfter === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const collapseAfter = parseInt(listElement.dataset.collapseAfter);
|
||||||
|
|
||||||
|
if (listElement.children.length <= collapseAfter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
attachExpandToggleButton(listElement);
|
||||||
|
|
||||||
|
for (let c = collapseAfter; c < listElement.children.length; c++) {
|
||||||
|
const child = listElement.children[c];
|
||||||
|
child.classList.add("list-collapsible-item");
|
||||||
|
child.style.animationDelay = ((c - collapseAfter) * 20).toString() + "ms";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupPage() {
|
async function setupPage() {
|
||||||
const pageElement = document.getElementById("page");
|
const pageElement = document.getElementById("page");
|
||||||
const pageContents = await fetchPageContents(pageData.slug);
|
const pageContentElement = document.getElementById("page-content");
|
||||||
|
const pageContent = await fetchPageContent(pageData.slug);
|
||||||
|
|
||||||
pageElement.innerHTML = pageContents;
|
pageContentElement.innerHTML = pageContent;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.body.classList.add("animate-element-transition");
|
document.body.classList.add("animate-element-transition");
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
setTimeout(setupLazyImages, 5);
|
try {
|
||||||
|
setupLazyImages();
|
||||||
setupCarousels();
|
setupCarousels();
|
||||||
|
setupCollapsibleLists();
|
||||||
setupDynamicRelativeTime();
|
setupDynamicRelativeTime();
|
||||||
|
} finally {
|
||||||
|
pageElement.classList.add("content-ready");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
{{ template "widget-base.html" . }}
|
{{ template "widget-base.html" . }}
|
||||||
|
|
||||||
{{ define "widget-content" }}
|
{{ define "widget-content" }}
|
||||||
<ul class="list list-gap-14 list-collapsible">
|
<ul class="list list-gap-14 list-collapsible" data-collapse-after="{{ .CollapseAfter }}">
|
||||||
{{ range $i, $post := .Posts }}
|
{{ range .Posts }}
|
||||||
<li {{ if shouldCollapse $i $.CollapseAfter }}class="list-collapsible-item" style="--animation-delay: {{ itemAnimationDelay $i $.CollapseAfter }};"{{ end }}>
|
<li>
|
||||||
<div class="forum-post-list-item thumbnail-container">
|
<div class="forum-post-list-item thumbnail-container">
|
||||||
{{ if $.ShowThumbnails }}
|
{{ if $.ShowThumbnails }}
|
||||||
{{ if ne $post.ThumbnailUrl "" }}
|
{{ if ne .ThumbnailUrl "" }}
|
||||||
<img class="forum-post-list-thumbnail thumbnail" src="{{ $post.ThumbnailUrl }}" alt="" loading="lazy">
|
<img class="forum-post-list-thumbnail thumbnail" src="{{ .ThumbnailUrl }}" alt="" loading="lazy">
|
||||||
{{ else if $post.HasTargetUrl }}
|
{{ else if .HasTargetUrl }}
|
||||||
<svg class="forum-post-list-thumbnail hide-on-mobile" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-9 -8 40 40" stroke-width="1.5" stroke="var(--color-text-subdue)">
|
<svg class="forum-post-list-thumbnail hide-on-mobile" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-9 -8 40 40" stroke-width="1.5" stroke="var(--color-text-subdue)">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
|
||||||
</svg>
|
</svg>
|
||||||
@ -19,13 +19,13 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<a href="{{ $post.DiscussionUrl }}" class="size-h3 color-primary-if-not-visited" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
<a href="{{ .DiscussionUrl }}" class="size-h3 color-primary-if-not-visited" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
||||||
<ul class="list-horizontal-text">
|
<ul class="list-horizontal-text">
|
||||||
<li title="{{ $post.TimePosted | formatTime }}" {{ dynamicRelativeTimeAttrs $post.TimePosted }}>{{ $post.TimePosted | relativeTime }}</li>
|
<li {{ dynamicRelativeTimeAttrs .TimePosted }}></li>
|
||||||
<li>{{ $post.Score | formatNumber }} points</li>
|
<li>{{ .Score | formatNumber }} points</li>
|
||||||
<li>{{ $post.CommentCount | formatNumber }} comments</li>
|
<li>{{ .CommentCount | formatNumber }} comments</li>
|
||||||
{{ if $post.HasTargetUrl }}
|
{{ if .HasTargetUrl }}
|
||||||
<li class="shrink min-width-0"><a class="visited-indicator text-truncate block" href="{{ .TargetUrl }}" target="_blank" rel="noreferrer">{{ $post.TargetUrlDomain }}</a></li>
|
<li class="shrink min-width-0"><a class="visited-indicator text-truncate block" href="{{ .TargetUrl }}" target="_blank" rel="noreferrer">{{ .TargetUrlDomain }}</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -33,7 +33,4 @@
|
|||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
{{ if gt (len .Posts) $.CollapseAfter }}
|
|
||||||
<label class="list-collapsible-label"><input type="checkbox" autocomplete="off" class="list-collapsible-input"></label>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
|
|
||||||
<div class="content-bounds">
|
<div class="content-bounds">
|
||||||
<div class="page" id="page">
|
<div class="page" id="page">
|
||||||
|
<div class="page-content" id="page-content"></div>
|
||||||
<div class="page-loading-container">
|
<div class="page-loading-container">
|
||||||
<!-- TODO: add a bigger/better loading indicator -->
|
<!-- TODO: add a bigger/better loading indicator -->
|
||||||
<div class="loading-icon"></div>
|
<div class="loading-icon"></div>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
<a href="{{ .DiscussionUrl }}" title="{{ .Title }}" class="text-truncate-3-lines color-primary-if-not-visited margin-top-7 margin-bottom-auto" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
<a href="{{ .DiscussionUrl }}" title="{{ .Title }}" class="text-truncate-3-lines color-primary-if-not-visited margin-top-7 margin-bottom-auto" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
||||||
<ul class="list-horizontal-text margin-top-7">
|
<ul class="list-horizontal-text margin-top-7">
|
||||||
<li title="{{ .TimePosted | formatTime }}" {{ dynamicRelativeTimeAttrs .TimePosted }}>{{ .TimePosted | relativeTime }}</li>
|
<li {{ dynamicRelativeTimeAttrs .TimePosted }}></li>
|
||||||
<li>{{ .Score | formatNumber }} points</li>
|
<li>{{ .Score | formatNumber }} points</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
<a href="{{ .DiscussionUrl }}" title="{{ .Title }}" class="text-truncate-3-lines color-primary-if-not-visited margin-top-7" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
<a href="{{ .DiscussionUrl }}" title="{{ .Title }}" class="text-truncate-3-lines color-primary-if-not-visited margin-top-7" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
||||||
<ul class="list-horizontal-text margin-top-7">
|
<ul class="list-horizontal-text margin-top-7">
|
||||||
<li title="{{ .TimePosted | formatTime }}" {{ dynamicRelativeTimeAttrs .TimePosted }}>{{ .TimePosted | relativeTime }}</li>
|
<li {{ dynamicRelativeTimeAttrs .TimePosted }}></li>
|
||||||
<li>{{ .Score | formatNumber }} points</li>
|
<li>{{ .Score | formatNumber }} points</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<li {{ if shouldCollapse $i $.CollapseAfter }}class="list-collapsible-item" style="--animation-delay: {{ itemAnimationDelay $i $.CollapseAfter }};"{{ end }}>
|
<li {{ if shouldCollapse $i $.CollapseAfter }}class="list-collapsible-item" style="--animation-delay: {{ itemAnimationDelay $i $.CollapseAfter }};"{{ end }}>
|
||||||
<a class="size-h4 block text-truncate color-primary-if-not-visited" href="{{ $release.NotesUrl }}" target="_blank" rel="noreferrer">{{ .Name }}</a>
|
<a class="size-h4 block text-truncate color-primary-if-not-visited" href="{{ $release.NotesUrl }}" target="_blank" rel="noreferrer">{{ .Name }}</a>
|
||||||
<ul class="list-horizontal-text">
|
<ul class="list-horizontal-text">
|
||||||
<li title="{{ $release.TimeReleased | formatTime }}" {{ dynamicRelativeTimeAttrs $release.TimeReleased }}>{{ $release.TimeReleased | relativeTime }}</li>
|
<li {{ dynamicRelativeTimeAttrs $release.TimeReleased }}></li>
|
||||||
<li>{{ $release.Version }}</li>
|
<li>{{ $release.Version }}</li>
|
||||||
{{ if gt $release.Downvotes 3 }}
|
{{ if gt $release.Downvotes 3 }}
|
||||||
<li>{{ $release.Downvotes | formatNumber }} ⚠</li>
|
<li>{{ $release.Downvotes | formatNumber }} ⚠</li>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<div class="flex gap-7 size-h5 margin-top-3">
|
<div class="flex gap-7 size-h5 margin-top-3">
|
||||||
<ul class="list list-gap-2">
|
<ul class="list list-gap-2">
|
||||||
{{ range .RepositoryDetails.PullRequests }}
|
{{ range .RepositoryDetails.PullRequests }}
|
||||||
<li title="{{ .CreatedAt | formatTime }}" {{ dynamicRelativeTimeAttrs .CreatedAt }}>{{ .CreatedAt | relativeTime }}</li>
|
<li {{ dynamicRelativeTimeAttrs .CreatedAt }}></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="list list-gap-2 min-width-0">
|
<ul class="list list-gap-2 min-width-0">
|
||||||
@ -30,7 +30,7 @@
|
|||||||
<div class="flex gap-7 size-h5 margin-top-3">
|
<div class="flex gap-7 size-h5 margin-top-3">
|
||||||
<ul class="list list-gap-2">
|
<ul class="list list-gap-2">
|
||||||
{{ range .RepositoryDetails.Issues }}
|
{{ range .RepositoryDetails.Issues }}
|
||||||
<li title="{{ .CreatedAt | formatTime }}" {{ dynamicRelativeTimeAttrs .CreatedAt }}>{{ .CreatedAt | relativeTime }}</li>
|
<li {{ dynamicRelativeTimeAttrs .CreatedAt }}></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="list list-gap-2 min-width-0">
|
<ul class="list list-gap-2 min-width-0">
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<div class="rss-card-2-content padding-inline-widget">
|
<div class="rss-card-2-content padding-inline-widget">
|
||||||
<a href="{{ .Link }}" title="{{ .Title }}" class="block text-truncate color-primary-if-not-visited" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
<a href="{{ .Link }}" title="{{ .Title }}" class="block text-truncate color-primary-if-not-visited" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
||||||
<ul class="list-horizontal-text flex-nowrap margin-top-5">
|
<ul class="list-horizontal-text flex-nowrap margin-top-5">
|
||||||
<li class="shrink-0" title="{{ .PublishedAt | formatTime }}" {{ dynamicRelativeTimeAttrs .PublishedAt }}>{{ .PublishedAt | relativeTime }}</li>
|
<li class="shrink-0" {{ dynamicRelativeTimeAttrs .PublishedAt }}></li>
|
||||||
<li class="shrink min-width-0 text-truncate">{{ .ChannelName }}</li>
|
<li class="shrink min-width-0 text-truncate">{{ .ChannelName }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<div class="margin-bottom-widget padding-inline-widget flex flex-column grow">
|
<div class="margin-bottom-widget padding-inline-widget flex flex-column grow">
|
||||||
<a href="{{ .Link }}" title="{{ .Title }}" class="text-truncate-3-lines color-primary-if-not-visited margin-top-10 margin-bottom-auto" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
<a href="{{ .Link }}" title="{{ .Title }}" class="text-truncate-3-lines color-primary-if-not-visited margin-top-10 margin-bottom-auto" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
||||||
<ul class="list-horizontal-text flex-nowrap margin-top-7">
|
<ul class="list-horizontal-text flex-nowrap margin-top-7">
|
||||||
<li class="shrink-0" title="{{ .PublishedAt | formatTime }}" {{ dynamicRelativeTimeAttrs .PublishedAt }}>{{ .PublishedAt | relativeTime }}</li>
|
<li class="shrink-0" {{ dynamicRelativeTimeAttrs .PublishedAt }}></li>
|
||||||
<li class="shrink min-width-0 text-truncate">{{ .ChannelName }}</li>
|
<li class="shrink min-width-0 text-truncate">{{ .ChannelName }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
{{ template "widget-base.html" . }}
|
{{ template "widget-base.html" . }}
|
||||||
|
|
||||||
{{ define "widget-content" }}
|
{{ define "widget-content" }}
|
||||||
<ul class="list list-gap-14 list-collapsible">
|
<ul class="list list-gap-14 list-collapsible" data-collapse-after="{{ .CollapseAfter }}">
|
||||||
{{ range $i, $item := .Items }}
|
{{ range .Items }}
|
||||||
<li {{ if shouldCollapse $i $.CollapseAfter }}class="list-collapsible-item" style="--animation-delay: {{ itemAnimationDelay $i $.CollapseAfter }};"{{ end }}>
|
<li>
|
||||||
<a class="size-title-dynamic color-primary-if-not-visited" href="{{ .Link }}" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
<a class="size-title-dynamic color-primary-if-not-visited" href="{{ .Link }}" target="_blank" rel="noreferrer">{{ .Title }}</a>
|
||||||
<ul class="list-horizontal-text">
|
<ul class="list-horizontal-text">
|
||||||
<li title="{{ $item.PublishedAt | formatTime }}" {{ dynamicRelativeTimeAttrs $item.PublishedAt }}>{{ .PublishedAt | relativeTime }}</li>
|
<li {{ dynamicRelativeTimeAttrs .PublishedAt }}></li>
|
||||||
{{ if gt (len $.FeedRequests) 1 }}
|
{{ if gt (len $.FeedRequests) 1 }}
|
||||||
<li><a href="{{ .ChannelURL }}" target="_blank" rel="noreferrer">{{ .ChannelName }}</a></li>
|
<li><a href="{{ .ChannelURL }}" target="_blank" rel="noreferrer">{{ .ChannelName }}</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@ -14,7 +14,4 @@
|
|||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
{{ if gt (len .Items) $.CollapseAfter }}
|
|
||||||
<label class="list-collapsible-label"><input type="checkbox" autocomplete="off" class="list-collapsible-input"></label>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
{{ template "widget-base.html" . }}
|
{{ template "widget-base.html" . }}
|
||||||
|
|
||||||
{{ define "widget-content" }}
|
{{ define "widget-content" }}
|
||||||
<ul class="list list-gap-14 list-collapsible">
|
<ul class="list list-gap-14 list-collapsible" data-collapse-after="{{ .CollapseAfter }}">
|
||||||
{{ range $i, $channel := .Channels }}
|
{{ range .Channels }}
|
||||||
<li {{ if shouldCollapse $i $.CollapseAfter }}class="list-collapsible-item" style="--animation-delay: {{ itemAnimationDelay $i $.CollapseAfter }};"{{ end }}>
|
<li>
|
||||||
<div class="{{ if $channel.IsLive }}twitch-channel-live {{ end }}flex gap-10 items-start thumbnail-container">
|
<div class="{{ if .IsLive }}twitch-channel-live {{ end }}flex gap-10 items-start thumbnail-container">
|
||||||
<div class="twitch-channel-avatar-container">
|
<div class="twitch-channel-avatar-container">
|
||||||
{{ if $channel.Exists }}
|
{{ if .Exists }}
|
||||||
<img class="twitch-channel-avatar thumbnail" src="{{ $channel.AvatarUrl }}" alt="" loading="lazy">
|
<img class="twitch-channel-avatar thumbnail" src="{{ .AvatarUrl }}" alt="" loading="lazy">
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<svg class="twitch-channel-avatar thumbnail" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
<svg class="twitch-channel-avatar thumbnail" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
|
||||||
@ -15,13 +15,13 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
<div class="shrink min-width-0">
|
<div class="shrink min-width-0">
|
||||||
<a href="https://twitch.tv/{{ $channel.Login }}" class="size-h3{{ if $channel.IsLive }} color-highlight{{ end }} block text-truncate" target="_blank" rel="noreferrer">{{ $channel.Name }}</a>
|
<a href="https://twitch.tv/{{ .Login }}" class="size-h3{{ if .IsLive }} color-highlight{{ end }} block text-truncate" target="_blank" rel="noreferrer">{{ .Name }}</a>
|
||||||
{{ if $channel.Exists }}
|
{{ if .Exists }}
|
||||||
{{ if $channel.IsLive }}
|
{{ if .IsLive }}
|
||||||
<a class="text-truncate block" href="https://www.twitch.tv/directory/category/{{ $channel.CategorySlug }}" target="_blank" rel="noreferrer">{{ $channel.Category }}</a>
|
<a class="text-truncate block" href="https://www.twitch.tv/directory/category/{{ .CategorySlug }}" target="_blank" rel="noreferrer">{{ .Category }}</a>
|
||||||
<ul class="list-horizontal-text">
|
<ul class="list-horizontal-text">
|
||||||
<li title="{{ $channel.LiveSince | formatTime }}" {{ dynamicRelativeTimeAttrs $channel.LiveSince }}>{{ $channel.LiveSince | relativeTime }}</li>
|
<li {{ dynamicRelativeTimeAttrs .LiveSince }}></li>
|
||||||
<li>{{ $channel.ViewersCount | formatViewerCount }} viewers</li>
|
<li>{{ .ViewersCount | formatViewerCount }} viewers</li>
|
||||||
</ul>
|
</ul>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<div>Offline</div>
|
<div>Offline</div>
|
||||||
@ -34,7 +34,4 @@
|
|||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
{{ if gt (len .Channels) $.CollapseAfter }}
|
|
||||||
<label class="list-collapsible-label"><input type="checkbox" autocomplete="off" class="list-collapsible-input"></label>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
{{ template "widget-base.html" . }}
|
{{ template "widget-base.html" . }}
|
||||||
|
|
||||||
{{ define "widget-content" }}
|
{{ define "widget-content" }}
|
||||||
<ul class="list list-gap-14 list-collapsible">
|
<ul class="list list-gap-14 list-collapsible" data-collapse-after="{{ .CollapseAfter }}">
|
||||||
{{ range $i, $category := .Categories }}
|
{{ range .Categories }}
|
||||||
{{ $shouldCollapseItem := shouldCollapse $i $.CollapseAfter }}
|
<li class="twitch-category thumbnail-container">
|
||||||
<li class="twitch-category thumbnail-container{{ if $shouldCollapseItem }} list-collapsible-item{{ end }}" {{ if $shouldCollapseItem }}style="--animation-delay: {{ itemAnimationDelay $i $.CollapseAfter }};"{{ end }}>
|
|
||||||
<div class="flex gap-10 items-center">
|
<div class="flex gap-10 items-center">
|
||||||
<img class="twitch-category-thumbnail thumbnail" loading="lazy" src="{{ $category.AvatarUrl }}" alt="">
|
<img class="twitch-category-thumbnail thumbnail" loading="lazy" src="{{ .AvatarUrl }}" alt="">
|
||||||
<div class="shrink min-width-0">
|
<div class="shrink min-width-0">
|
||||||
<a class="size-h3 color-highlight text-truncate block" href="https://www.twitch.tv/directory/category/{{ $category.Slug }}" target="_blank" rel="noreferrer">{{ $category.Name }}</a>
|
<a class="size-h3 color-highlight text-truncate block" href="https://www.twitch.tv/directory/category/{{ .Slug }}" target="_blank" rel="noreferrer">{{ .Name }}</a>
|
||||||
<ul class="list-horizontal-text">
|
<ul class="list-horizontal-text">
|
||||||
<li>{{ $category.ViewersCount | formatViewerCount }} viewers</li>
|
<li>{{ .ViewersCount | formatViewerCount }} viewers</li>
|
||||||
{{ if $category.IsNew }}
|
{{ if .IsNew }}
|
||||||
<li class="color-primary">NEW</li>
|
<li class="color-primary">NEW</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="list-horizontal-text flex-nowrap">
|
<ul class="list-horizontal-text flex-nowrap">
|
||||||
{{ range $i, $tag := $category.Tags }}
|
{{ range $i, $tag := .Tags }}
|
||||||
{{ if eq $i 0 }}
|
{{ if eq $i 0 }}
|
||||||
<li class="shrink-0">{{ $tag.Name }}</li>
|
<li class="shrink-0">{{ $tag.Name }}</li>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
@ -29,7 +28,4 @@
|
|||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
{{ if gt (len .Categories) $.CollapseAfter }}
|
|
||||||
<label class="list-collapsible-label"><input type="checkbox" autocomplete="off" class="list-collapsible-input"></label>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="margin-top-10 margin-bottom-widget flex flex-column grow padding-inline-widget">
|
<div class="margin-top-10 margin-bottom-widget flex flex-column grow padding-inline-widget">
|
||||||
<a class="video-title color-primary-if-not-visited" href="{{ .Url }}" target="_blank" rel="noreferrer" title="{{ .Title }}">{{ .Title }}</a>
|
<a class="video-title color-primary-if-not-visited" href="{{ .Url }}" target="_blank" rel="noreferrer" title="{{ .Title }}">{{ .Title }}</a>
|
||||||
<ul class="list-horizontal-text flex-nowrap margin-top-7">
|
<ul class="list-horizontal-text flex-nowrap margin-top-7">
|
||||||
<li class="shrink-0" title="{{ .TimePosted | formatTime }}" {{ dynamicRelativeTimeAttrs .TimePosted }}>{{ .TimePosted | relativeTime }}</li>
|
<li class="shrink-0" {{ dynamicRelativeTimeAttrs .TimePosted }}></li>
|
||||||
<li class="shrink min-width-0">
|
<li class="shrink min-width-0">
|
||||||
<a class="block text-truncate" href="{{ .AuthorUrl }}" target="_blank" rel="noreferrer">{{ .Author }}</a>
|
<a class="block text-truncate" href="{{ .AuthorUrl }}" target="_blank" rel="noreferrer">{{ .Author }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user