415 lines
14 KiB
HTML
Executable File
415 lines
14 KiB
HTML
Executable File
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Reddit, but better</title>
|
|
<style>
|
|
:root {
|
|
--dark: #2c2c2c;
|
|
--darker: #171717;
|
|
--light: #bfbfbf;
|
|
--confirm: #970000;
|
|
--saved: #9f9400;
|
|
}
|
|
html {
|
|
height: 100%;
|
|
}
|
|
body {
|
|
background-color: var(--darker);
|
|
color: var(--light);
|
|
min-height: 100%;
|
|
margin: 0; /* Removes default browser margin */
|
|
}
|
|
img, video {
|
|
max-width: 100%;
|
|
max-height: 80vh;
|
|
width: auto;
|
|
height: auto;
|
|
}
|
|
div.post {
|
|
background-color: var(--dark);
|
|
border: 2px solid var(--light);
|
|
border-radius: 15px;
|
|
padding: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.sidebar {
|
|
outline: 2px solid var(--light);
|
|
position: sticky;
|
|
top: 0;
|
|
left: 0;
|
|
background-color: var(--dark);
|
|
display: flex;
|
|
flex-wrap: nowrap;
|
|
z-index: 1000;
|
|
}
|
|
.content {
|
|
display: flex;
|
|
flex-grow: 1; /* Takes up remaining space */
|
|
flex-direction: column;
|
|
}
|
|
/* desktop */
|
|
@media (min-aspect-ratio: 1) {
|
|
div.post {
|
|
width: 70vw;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
.sidebar {
|
|
width: fit-content;
|
|
height: 100vh;
|
|
flex-direction: column;
|
|
overflow-y: auto;
|
|
padding: 5px;
|
|
margin-right: 20px;
|
|
}
|
|
}
|
|
/* phone */
|
|
@media (max-aspect-ratio: 1) {
|
|
div.post {
|
|
width: calc(100vw - 50px);
|
|
margin-top: 10px;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.sidebar {
|
|
width: 100vw;
|
|
height: 50px;
|
|
flex-direction: row;
|
|
overflow-x: auto;
|
|
align-items: center;
|
|
padding-top: 5px;
|
|
padding-bottom: 5px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.content {
|
|
align-items: center;
|
|
}
|
|
}
|
|
.sidebar a {
|
|
display: block;
|
|
color: var(--light);
|
|
text-decoration: none;
|
|
white-space: nowrap;
|
|
margin: 5px;
|
|
padding: 5px;
|
|
}
|
|
.sidebar a:hover {
|
|
background-color: var(--darker);
|
|
color: var(--light);
|
|
}
|
|
a.no-style-link {
|
|
color: inherit;
|
|
text-decoration: inherit;
|
|
cursor: pointer;
|
|
}
|
|
.invert {
|
|
filter: invert(1);
|
|
transition: filter 0.3s;
|
|
}
|
|
.button-wrapper {
|
|
display: flex;
|
|
width: 100%;
|
|
gap: 10px;
|
|
margin-top: 10px;
|
|
}
|
|
.button-wrapper.gallery {
|
|
gap: 5px;
|
|
}
|
|
.button-wrapper button {
|
|
flex: 1;
|
|
padding: 10px;
|
|
cursor: pointer;
|
|
background-color: var(--darker);
|
|
color: var(--light);
|
|
border: 2px solid var(--light);
|
|
border-radius: 10px;
|
|
font-size: 1.25rem;
|
|
font-weight: bold;
|
|
}
|
|
.button-wrapper button.confirm {
|
|
background-color: var(--confirm)
|
|
}
|
|
.button-wrapper button.saved {
|
|
background-color: var(--saved)
|
|
}
|
|
.button-wrapper button.gallery {
|
|
padding: 5px;
|
|
background-color: var(--darker);
|
|
border-radius: 5px;
|
|
border: none;
|
|
cursor: none;
|
|
}
|
|
.button-wrapper button.gallery.selected {
|
|
background-color: var(--light);
|
|
}
|
|
.text-content {
|
|
overflow: hidden;
|
|
transition: max-height 0.3s ease-out; /* Smooth transition */
|
|
max-height: 20vh;
|
|
position: relative;
|
|
}
|
|
.text-content::after {
|
|
content: "";
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 30px;
|
|
background: linear-gradient(to bottom, rgba(255,255,255,0), var(--dark));
|
|
}
|
|
.text-content.expanded {
|
|
max-height: 1000vh;
|
|
}
|
|
.text-content.expanded::after {
|
|
display: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="sidebar">
|
|
{% for link in sidebar_links %}
|
|
<a href="{{ link }}">{{ link }}</a>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="content">
|
|
<h1>{{ title }}</h1>
|
|
{% for post in posts %}
|
|
<div class="post">
|
|
<h3>{{ post.title }}</h3>
|
|
<span>
|
|
{% if post.subreddit %}
|
|
<h5>
|
|
<a href="/r/{{ post.subreddit }}" class="no-style-link">/r/{{ post.subreddit }}</a>
|
|
{% if post.author %}
|
|
— {{ post.author }}
|
|
{% endif %}
|
|
{% if post.age %}
|
|
— {{ post.age }}
|
|
{% endif %}
|
|
</h5>
|
|
{% endif %}
|
|
</span>
|
|
{% if post.media_html|length > 0 %}
|
|
<div class="media-div">
|
|
{% for media in post.media_html %}
|
|
{{ media|safe }}
|
|
{% endfor %}
|
|
{% if post.media_html|length > 1 %}
|
|
<span class="button-wrapper gallery">
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
{% if post.body %}
|
|
<div class="text-content" onclick="expand(this)">
|
|
{{ post.body }}
|
|
</div>
|
|
{% endif %}
|
|
<span class="button-wrapper">
|
|
<button type="button" name="comments" onpointerdown='commentsDown(this, "{{ post.permalink }}")' onpointerup='commentsUp(this, "{{ post.permalink }}")'>Comments</button>
|
|
<button type="button" name="save" onclick='save(this, "{{ post.permalink }}")' {% if saved %}class='saved'{% endif %}>Save</button>
|
|
<button type="button" name="hide" onclick='hide(this, "{{ post.permalink }}")'>Hide</button>
|
|
</span>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<script>
|
|
// setup galleries
|
|
mediaDivs = document.querySelectorAll('.media-div');
|
|
|
|
mediaDivs.forEach(div => {
|
|
images = Array.from(div.querySelectorAll('img'));
|
|
if (images.length > 1) {
|
|
buttonSpan = div.querySelector('.button-wrapper:first-of-type');
|
|
images.forEach(image => {
|
|
image.addEventListener('click', (e) => {
|
|
if (e.offsetX > image.offsetWidth * 2 / 3) {
|
|
// scroll right
|
|
div = e.target.closest('.media-div');
|
|
images = Array.from(div.querySelectorAll('img'));
|
|
currentIndex = images.indexOf(e.target)
|
|
if (currentIndex < (images.length -1)) {
|
|
buttons = Array.from(div.querySelectorAll('button'));
|
|
images[currentIndex].style.display = "none";
|
|
images[currentIndex+1].style.display = "block";
|
|
buttons[currentIndex].classList.remove('selected');
|
|
buttons[currentIndex+1].classList.add('selected');
|
|
} else {
|
|
e.target.classList.toggle('invert');
|
|
}
|
|
} else if (e.offsetX < image.offsetWidth / 3) {
|
|
// scroll left
|
|
div = e.target.closest('.media-div');
|
|
images = Array.from(div.querySelectorAll('img'));
|
|
currentIndex = images.indexOf(e.target)
|
|
if (currentIndex > 0) {
|
|
buttons = Array.from(div.querySelectorAll('button'));
|
|
images[currentIndex].style.display = "none";
|
|
images[currentIndex-1].style.display = "block";
|
|
buttons[currentIndex].classList.remove('selected');
|
|
buttons[currentIndex-1].classList.add('selected');
|
|
} else {
|
|
e.target.classList.toggle('invert');
|
|
}
|
|
} else {
|
|
image.classList.toggle('invert');
|
|
}
|
|
});
|
|
});
|
|
firstImage = images.shift();
|
|
firstButton = document.createElement('button');
|
|
firstButton.classList.add('gallery');
|
|
firstButton.classList.add('selected');
|
|
buttonSpan.appendChild(firstButton);
|
|
images.forEach(image => {
|
|
image.style.display = "none";
|
|
button = document.createElement('button');
|
|
button.classList.add('gallery');
|
|
buttonSpan.appendChild(button);
|
|
});
|
|
} else {
|
|
images.forEach(image => {
|
|
image.addEventListener('click', () => {
|
|
image.classList.toggle('invert');
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
// main button code
|
|
|
|
function hide(button, permalink){
|
|
div = button.closest('.post');
|
|
hideButton = div.querySelector('[name="save"]');
|
|
if (hideButton.classList.contains('saved')){
|
|
return;
|
|
}
|
|
if (button.classList.contains('confirm')) {
|
|
div = button.closest('.post');
|
|
div.remove();
|
|
try {
|
|
fetch('/hide' + permalink);
|
|
} catch (error) {
|
|
console.error('Could not hide', error);
|
|
}
|
|
} else {
|
|
button.classList.add('confirm');
|
|
}
|
|
}
|
|
|
|
commentTimer = null;
|
|
|
|
function commentsDown(button, permalink) {
|
|
commentTimer = setTimeout(() => {
|
|
navigator.clipboard.writeText("https://reddit.com" + permalink);
|
|
commentTimer = null;
|
|
button.textContent = "Copied!"
|
|
}, 500);
|
|
}
|
|
|
|
function commentsUp(button, permalink) {
|
|
if (commentTimer != null) {
|
|
clearTimeout(commentTimer);
|
|
commentTimer = null;
|
|
pauseVideo(button);
|
|
window.open("https://reddit.com" + permalink, '_blank');
|
|
} else {
|
|
setTimeout(() => {
|
|
button.textContent = "Comments";
|
|
}, 500);
|
|
}
|
|
}
|
|
|
|
function save(button, permalink) {
|
|
if (button.classList.contains("saved")) {
|
|
button.classList.remove("saved");
|
|
try {
|
|
fetch('/save' + permalink + '?action=unsave');
|
|
} catch (error) {
|
|
console.error('Could not unsave', error);
|
|
}
|
|
} else {
|
|
button.classList.add("saved");
|
|
div = button.closest('.post');
|
|
hideButton = div.querySelector('[name="hide"]');
|
|
hideButton.classList.remove("confirm");
|
|
try {
|
|
fetch('/save' + permalink + '?action=save');
|
|
} catch (error) {
|
|
console.error('Could not save', error);
|
|
}
|
|
}
|
|
}
|
|
// text expand code
|
|
|
|
function checkHeight(){
|
|
const divs = document.querySelectorAll('.text-content');
|
|
divs.forEach(div => {
|
|
height = div.offsetHeight;
|
|
style = window.getComputedStyle(div);
|
|
maxHeight = parseInt(style.maxHeight);
|
|
if (height < maxHeight) {
|
|
div.classList.add('expanded');
|
|
}
|
|
});
|
|
}
|
|
|
|
function expand(div) {
|
|
div.classList.add('expanded');
|
|
}
|
|
|
|
window.addEventListener('load', (event) => {
|
|
checkHeight()
|
|
});
|
|
|
|
window.addEventListener('resize', (event) => {
|
|
checkHeight()
|
|
});
|
|
|
|
// audio/video sync code
|
|
function pauseVideo(element){
|
|
div = element.closest('.post');
|
|
video = div.querySelector('video:first-of-type');
|
|
if (video) {
|
|
video.pause();
|
|
pauseAudio(video);
|
|
}
|
|
}
|
|
|
|
function findAudio(video){
|
|
div = video.closest('.post');
|
|
return div.querySelector('audio:first-of-type');
|
|
}
|
|
|
|
function playAudio(video){
|
|
audio = findAudio(video);
|
|
if (audio) {
|
|
audio.play();
|
|
audio.currentTime = video.currentTime;
|
|
}
|
|
}
|
|
function pauseAudio(video){
|
|
audio = findAudio(video);
|
|
if (audio) {
|
|
audio.pause();
|
|
audio.currentTime = video.currentTime;
|
|
}
|
|
}
|
|
function seekAudio(video){
|
|
audio = findAudio(video);
|
|
if (audio) {
|
|
audio.currentTime = video.currentTime;
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|