oleg.vodyanov91@gmail.com dd073f0f34
All checks were successful
continuous-integration/drone/push Build is passing
use_iframe
2025-04-23 13:31:09 +04:00

210 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const newLinkInput = document.getElementById('newLinkInput');
const addLinkBtn = document.getElementById('addLinkBtn');
const newLinksContainer = document.getElementById('newLinksContainer');
const watchedLinksContainer = document.getElementById('watchedLinksContainer');
const PER_PAGE = 8;
let currentPage = 1;
let linksData = [];
function loadLinks() {
const cacheKey = `links_cache_page_${currentPage}_per_${PER_PAGE}`;
const cached = localStorage.getItem(cacheKey);
if (cached) {
const data = JSON.parse(cached);
linksData = data.results;
renderPaginatedList(
linksData.filter(l => !l.watched),newLinksContainer,false,data.page,data.pages
);
renderPaginatedList(
linksData.filter(l => l.watched),watchedLinksContainer,true,data.page,data.pages
);
}
// Always fetch fresh data in the background
fetch(`/api/links/?page=${currentPage}&per_page=${PER_PAGE}`)
.then(res => res.json())
.then(data => {
localStorage.setItem(cacheKey, JSON.stringify(data));
if (!cached) {
linksData = data.results;
renderPaginatedList(
linksData.filter(l => !l.watched),newLinksContainer,false,data.page,data.pages
);
renderPaginatedList(
linksData.filter(l => l.watched),watchedLinksContainer,true,data.page,data.pages
);
}
})
.catch(err => console.error(err));
}
function renderPaginatedList(linkList, container, isWatched, page, totalPages) {
container.innerHTML = '';
const row = document.createElement('div');
row.className = 'row gy-4';
linkList
.filter(link => link.watched === isWatched)
.forEach(link => {
const col = document.createElement('div');
col.className = 'col-12 col-sm-6 col-md-4 col-lg-3';
col.appendChild(createLinkElement(link, isWatched));
row.appendChild(col);
});
container.appendChild(row);
// Pagination controls
if (totalPages > 1) {
const nav = document.createElement('div');
nav.className = 'd-flex justify-content-center mt-4 gap-2';
const prevBtn = document.createElement('button');
prevBtn.innerText = '← Previous';
prevBtn.className = 'btn btn-outline-primary';
prevBtn.disabled = page <= 1;
prevBtn.onclick = () => {
currentPage--;
loadLinks();
};
const nextBtn = document.createElement('button');
nextBtn.innerText = 'Next →';
nextBtn.className = 'btn btn-outline-primary';
nextBtn.disabled = page >= totalPages;
nextBtn.onclick = () => {
currentPage++;
loadLinks();
};
nav.appendChild(prevBtn);
nav.appendChild(nextBtn);
container.appendChild(nav);
}
reinitializeInstagramEmbeds();
}
// Create a DOM element for a single link
function createLinkElement(link, isWatched) {
const wrapper = document.createElement('div');
wrapper.className = 'mb-3';
const reelId = extractReelId(link.url);
if (!reelId) {
wrapper.innerText = 'Invalid Instagram Reel URL';
return wrapper;
}
// Clean iframe embed
const iframeWrapper = document.createElement('div');
iframeWrapper.style.width = '100%';
iframeWrapper.style.height = '400px';
iframeWrapper.style.overflow = 'hidden';
iframeWrapper.style.borderRadius = '10px';
const iframe = document.createElement('iframe');
iframe.src = `https://www.instagram.com/reel/${reelId}/embed`;
iframe.width = '100%';
iframe.height = '600'; // taller to allow for cropping
iframe.style.border = 'none';
iframe.allowFullscreen = true;
iframe.loading = 'lazy';
iframe.style.marginTop = '-100px'; // shift to crop comments/likes
iframeWrapper.appendChild(iframe);
wrapper.appendChild(iframeWrapper);
const btnGroup = document.createElement('div');
btnGroup.className = 'mt-2 d-flex gap-2';
// Button to mark watched if it's not watched
if (!isWatched) {
const watchButton = document.createElement('button');
watchButton.className = 'btn btn-sm btn-secondary';
watchButton.innerText = 'Mark Watched';
watchButton.onclick = () => markLinkWatched(link.id);
btnGroup.appendChild(watchButton);
}
// Delete button
const deleteButton = document.createElement('button');
deleteButton.innerText = 'Delete';
deleteButton.className = 'btn btn-sm btn-danger';
deleteButton.onclick = () => deleteLink(link.id);
btnGroup.appendChild(deleteButton);
wrapper.appendChild(btnGroup);
return wrapper;
}
function deleteLink(linkId) {
fetch(`/api/links/${linkId}/delete/`, {
method: 'DELETE'
})
.then(res => {
if (res.ok) {
linksData = linksData.filter(link => link.id !== linkId);
currentPage = 1;
loadLinks();
reinitializeInstagramEmbeds();
}
})
.catch(err => console.error(err));
}
// Re-run the Instagram embed script after weve injected new blockquotes
function reinitializeInstagramEmbeds() {
if (window.instgrm) {
window.instgrm.Embeds.process();
}
}
// Mark a link as watched
function markLinkWatched(linkId) {
fetch(`/api/links/${linkId}/watched/`, {
method: 'PATCH'
})
.then(res => res.json())
.then(updatedLink => {
// Update the local array
const idx = linksData.findIndex(l => l.id === updatedLink.id);
if (idx !== -1) {
linksData[idx] = updatedLink;
}
loadLinks();
reinitializeInstagramEmbeds();
})
.catch(err => console.error(err));
}
// Add new link
addLinkBtn.addEventListener('click', () => {
const newUrl = newLinkInput.value.trim();
if (!newUrl) return;
fetch('/api/links/add/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: newUrl })
})
.then(res => res.json())
.then(addedLink => {
// update local data
linksData.push(addedLink);
newLinkInput.value = '';
currentPage = 1;
loadLinks();
reinitializeInstagramEmbeds();
})
.catch(err => console.error(err));
});
// On page load
loadLinks();