oleg.vodyanov91@gmail.com d7b674d79a
All checks were successful
continuous-integration/drone/push Build is passing
fix
2025-04-23 14:17:01 +04:00

215 lines
6.0 KiB
JavaScript
Raw Permalink 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 extractReelId(url) {
// Example: https://www.instagram.com/reel/ABC123xyz/
const match = url.match(/instagram\.com\/reel\/([a-zA-Z0-9_-]+)/);
return match ? match[1] : null;
}
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 column = document.createElement('div');
column.className = 'col-12 col-sm-6 col-md-4 col-lg-3';
column.appendChild(createLinkElement(link, isWatched));
row.appendChild(column);
});
container.appendChild(row);
// Pagination controls
if (totalPages > 1) {
const navigation = document.createElement('div');
navigation.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();
};
navigation.appendChild(prevBtn);
navigation.appendChild(nextBtn);
container.appendChild(navigation);
}
reinitializeInstagramEmbeds();
}
// Create a DOM element for a single link
function createLinkElement(link, isWatched) {
const wrapper = document.createElement('div');
wrapper.className = 'mb-auto';
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 = '600px';
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 = '700'; // taller to allow for cropping
iframe.style.border = 'none';
iframe.allow="fullscreen";
iframe.loading = 'lazy';
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();