hmn/src/templates/src/project_homepage.html

218 lines
7.5 KiB
HTML

{{ template "base-2024.html" . }}
{{ define "extrahead" }}
{{ range .Screenshots }}
<link rel="preload" href="{{ . }}" as="image">
{{ end }}
<script src="{{ static "js/templates.js" }}"></script>
{{ end }}
{{ define "content" }}
<div class="flex flex-row justify-center">
<div class="flex-grow-1 flex flex-column items-center mw-site pt4">
<div class="w-100 h5 bg-white-50 bg-center cover" style="background-image: url('{{ .Project.HeaderImage }}')">
<div class="flex justify-end pa3 g3 link--normal b">
{{ with .NamedLinks }}
<div class="bg--card-transparent flex">
{{ range . }}
<a class="flex ph3 pv2 flex items-center" href="{{ .Url }}">{{ .Name }}<span class="svgicon f6 ml2">{{ svg "arrow-right-up" }}</span></a>
{{ end }}
</div>
{{ end }}
{{ with .UnnamedLinks }}
<div class="bg--card-transparent flex items-center ph1">
{{ range . }}
<a class="flex ph2" href="{{ .Url }}" title="{{ .ServiceName }}{{ with .Username }} ({{ . }}){{ end }}">{{ svg (strjoin "logos/" .Icon) }}</a>
{{ end }}
</div>
{{ end }}
</div>
</div>
<div class="w-100 mw-site-narrow flex justify-center" style="margin-top: -3rem">
{{ template "project_card.html" projectcarddata .Project "flex-grow-1 project-card-black" }}
</div>
{{ if .Project.ParsedDescription }}
<div id="longdesc" class="description w-100 mw-site-narrow pt4">
<h3 class="f4">About {{ .Project.Name }}</h3>
<hr class="mv3">
<div class="longdesc-content post-content mh-5 overflow-hidden">
{{ .Project.ParsedDescription }}
</div>
<a class="longdesc-link pt3 db" href="#">
<span class="longdesc-text">Read more</span> <span class="svgicon f7 dib">{{ svg "chevron-down-thick" }}</span>
</a>
</div>
{{ end }}
{{ if .FollowUrl }}
<a id="follow_link" class="db" href="javascript:;">{{ if .Following }}Unfollow{{ else }}Follow{{ end }}</a>
<script>
const followLink = document.getElementById("follow_link");
let following = {{ .Following }};
followLink.addEventListener("click", async function() {
followLink.disabled = true;
let formData = new FormData();
formData.set("csrf_token", "{{ .Session.CSRFToken }}");
formData.set("project_id", "{{ .Project.ID }}");
if (following) {
formData.set("unfollow", "true");
}
let result = await fetch("{{ .FollowUrl }}", {
method: "POST",
body: formData,
redirect: "error",
});
if (result.ok) {
following = !following;
followLink.textContent = (following ? "Unfollow" : "Follow");
}
followLink.disabled = false;
});
</script>
{{ end }}
{{ if or .Header.Project.CanEdit (gt (len .RecentActivity) 0) }}
<hr class="w-100 mv4">
<div class="w-100 flex g3">
<div class="flex flex-column g3">
<div class="bg--card pa3 w5">
Filters
</div>
</div>
<div class="flex-grow-1">
<div class="flex flex-row items-center mb2">
<h2 id="recent">Recent Activity</h2>
<div class="flex-grow-1"></div>
{{ if .Header.Project.CanEdit }}
<a href="javascript:;" class="create_snippet_link button">Add Snippet</a>
{{ end }}
</div>
<div class="timeline flex flex-column g3">
{{ range .RecentActivity }}
{{ template "timeline_item.html" . }}
{{ end }}
TODO: READ MORE LINK
</div>
</div>
</div>
{{ end }}
</div>
</div>
<div class="flex flex-column flex-row-l">
<div class="flex-grow-1 overflow-hidden">
{{ with .Screenshots }}
<div class="carousel-container mw-100 mb3">
<div class="carousel aspect-ratio aspect-ratio--16x9 overflow-hidden bg3 br2-ns">
<div class="dn db-l">
{{ range $index, $screenshot := . }}
<div class="carousel-item aspect-ratio--object bg3 {{ if eq $index 0 }}active{{ end }}">
<div class="w-100 h-100" style="background:url('{{ $screenshot }}') no-repeat center / contain"></div>
</div>
{{ end }}
</div>
<div class="db dn-l">
{{ range $index, $screenshot := . }}
<div class="carousel-item-small aspect-ratio--object {{ if eq $index 0 }}active{{ end }}">
<div class="w-100 h-100" style="background:url('{{ $screenshot }}') no-repeat center / contain"></div>
</div>
{{ end }}
</div>
</div>
<div class="flex justify-center pv2">
{{ range $index, $screenshot := . }}
<div class="carousel-button br-pill w1 h1 mh2 {{ if eq $index 0 }}active{{ end }}" onclick="carouselButtonClick({{ $index }})"></div>
{{ end }}
</div>
</div>
{{ end }}
</div>
</div>
{{ if .User }}
{{ template "snippet_edit.html" . }}
{{ if .Header.Project.CanEdit }}
<script>
const userName = "{{ .User.Name }}";
const userAvatar = "{{ .User.AvatarUrl }}";
const userUrl = "{{ .User.ProfileUrl }}";
const currentProjectId = {{ .Project.ID }};
document.querySelector(".create_snippet_link")?.addEventListener("click", function() {
let snippetEdit = makeSnippetEdit(userName, userAvatar, userUrl, new Date(), "", null, [currentProjectId], currentProjectId, null, null);
document.querySelector(".timeline").insertBefore(snippetEdit.root, document.querySelector(".timeline").children[0]);
document.querySelector(".create_snippet_link")?.remove();
});
document.querySelector(".timeline").addEventListener("click", function(ev) {
if (ev.target.classList.contains("edit")) {
let parent = ev.target.parentElement;
while (parent && !parent.classList.contains("timeline-item")) {
parent = parent.parentElement;
}
if (parent && parent.classList.contains("timeline-item")) {
editTimelineSnippet(parent, currentProjectId);
}
}
});
</script>
{{ end }}
{{ end }}
<script>
const numCarouselItems = {{ len .Screenshots }};
function activateCarouselItem(i) {
const items = document.querySelectorAll('.carousel-item');
items.forEach(item => item.classList.remove('active'));
items[i].classList.add('active');
const smallItems = document.querySelectorAll('.carousel-item-small');
smallItems.forEach(item => item.classList.remove('active'));
smallItems[i].classList.add('active');
const buttons = document.querySelectorAll('.carousel-button');
buttons.forEach(button => button.classList.remove('active'));
buttons[i].classList.add('active');
}
let carouselTimerCurrent = 0;
const carouselTimer = setInterval(() => {
if (numCarouselItems === 0) {
return;
}
const next = (carouselTimerCurrent + 1) % numCarouselItems;
activateCarouselItem(next);
carouselTimerCurrent = next;
}, 10000);
function carouselButtonClick(i) {
activateCarouselItem(i);
clearInterval(carouselTimer);
}
const longdesc = document.querySelector("#longdesc");
const longdescLink = longdesc.querySelector(".longdesc-link");
const longdescContent = longdesc.querySelector(".longdesc-content");
longdescLink.addEventListener("click", e => {
e.preventDefault();
const expandText = longdesc.querySelector(".longdesc-text");
const chevron = longdesc.querySelector(".svgicon");
const expanding = longdescContent.classList.contains("mh-5");
longdescContent.classList.toggle("mh-5", !expanding);
expandText.innerText = expanding ? "Read less" : "Read more";
chevron.classList.toggle("rot-180", expanding);
});
function showOrHideLongdescLink() {
// The content has mh-5, which is max-height: 16rem
const hide = longdescContent.clientHeight < rem2px(16);
longdescLink.classList.toggle("dn", hide);
longdescLink.classList.toggle("db", !hide);
}
showOrHideLongdescLink();
window.addEventListener("resize", showOrHideLongdescLink);
</script>
{{ end }}