diff --git a/index.html b/index.html index cdde661..c465a8c 100644 --- a/index.html +++ b/index.html @@ -50,6 +50,91 @@ justify-content: center; margin: 0; } + + .posts.mode-tinder, + .posts.mode-tiktok { + height: calc(100vh - 48px - var(--safe-bottom)); + overflow: hidden; + position: relative; + max-width: 600px; + margin-bottom: 0 !important; + } + + .posts.mode-tiktok { + overflow-y: scroll; + scroll-snap-type: y mandatory; + -ms-overflow-style: none; + /* IE and Edge */ + scrollbar-width: none; + /* Firefox */ + height: calc(100vh - 48px - var(--safe-bottom)); + overflow-y: scroll; + scroll-snap-type: y mandatory; + } + + .posts.mode-tiktok .post { + height: calc(100vh - 48px - var(--safe-bottom)); + scroll-snap-align: start; + box-sizing: border-box; + justify-content: center; + } + + .posts.mode-tiktok::-webkit-scrollbar { + display: none; + } + + .posts.mode-tinder .post { + position: absolute; + box-sizing: border-box; + justify-content: center; + top: 0; + left: 0; + right: 0; + height: 100%; + width: 100%; + background: var(--bg); + z-index: 2; + transition: transform 0.3s ease, opacity 0.3s ease; + touch-action: none; + } + + .swipe-right { + transform: translateX(200%) rotate(30deg); + opacity: 0; + } + + .swipe-left { + transform: translateX(-200%) rotate(-30deg); + opacity: 0; + } + + .swipe-indicator { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0); + font-size: 80px; + z-index: 10; + pointer-events: none; + opacity: 0; + transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.2s; + } + + .swipe-indicator.visible { + transform: translate(-50%, -50%) scale(1.2); + opacity: 1; + } + + .indicator-heart { + color: #ff4b4b; + text-shadow: 0 0 20px rgba(255, 75, 75, 0.4); + } + + .indicator-cross { + color: #8899a6; + text-shadow: 0 0 20px rgba(136, 153, 166, 0.4); + } + .posts { max-width: 600px; width: 100vw; @@ -235,6 +320,7 @@ color: var(--theme); } .bottomNav { + z-index: 100; display: flex; justify-content: space-around; position: fixed; @@ -605,6 +691,13 @@ + Display mode + + + + + +
@@ -687,7 +780,7 @@

A const defaultCategories = ["nature", "science", "animals", "anthropology", "places", "sociology", "art", "mathematics", "games", "technology", "music", "human sexuality"]; let postsWithoutLike = 0; - const HTML_VERSION = "1.1.4"; + const HTML_VERSION = "1.1.5"; function loadSettings() { const baseSettings = { @@ -697,6 +790,7 @@

A profile: "default", profiles: ["default"], theme: "theme-auto", + viewMode: "mode-classic" }; const loadedSettings = JSON.parse(localStorage.getItem("xikipedia-settings") ?? '{}'); const computedSettings = Object.assign(baseSettings, loadedSettings); @@ -710,7 +804,14 @@

A settings.theme = document.querySelector('[name=theme]:checked')?.id ?? "theme-auto"; settings.storeData = document.getElementById("setting-storeData").checked; settings.openMainWiki = document.getElementById("setting-openMainWiki").checked; + settings.viewMode = document.querySelector('[name=viewMode]:checked')?.id ?? "mode-classic"; localStorage.setItem("xikipedia-settings", JSON.stringify(settings)); + applyViewMode(); + } + + function applyViewMode() { + const container = document.querySelector(".posts"); + container.className = "posts " + settings.viewMode; } function resetAlgorithm() { @@ -1029,6 +1130,7 @@

A const postTitle = document.createElement("h1"); const postP = document.createElement("p"); const postYes = document.createElement("button"); + postDiv.dataset.id = nextPost.id; postDiv.classList.add("post"); postDiv.onclick = () => { window.open(getArticleLink(nextPost.title)); @@ -1069,6 +1171,59 @@

A //nextPost.allCategories.forEach(e => categoryScores[e] -= 1); engagePost(nextPost, -5); postsWithoutLike++; + let startX = 0; + let isDragging = false; + + const start = (x) => { + if (settings.viewMode !== "mode-tinder") return; + startX = x; + isDragging = true; + postDiv.style.transition = 'none'; + }; + + const move = (x) => { + if (!isDragging || settings.viewMode !== "mode-tinder") return; + let moveX = x - startX; + postDiv.style.transform = `translateX(${moveX}px) rotate(${moveX / 10}deg)`; + }; + + const end = (x) => { + if (!isDragging || settings.viewMode !== "mode-tinder") return; + isDragging = false; + postDiv.style.transition = 'transform 0.3s ease, opacity 0.3s ease'; + + let diffX = x - startX; + if (Math.abs(diffX) > 100) { + postDiv.style.pointerEvents = "none"; + const parent = document.querySelector(".posts.mode-tinder"); + if (diffX > 0) { + showSwipeIndicator('like', parent); + postDiv.classList.add('swipe-right'); + likedPosts.push(nextPost.id); + engagePost(nextPost, 50); + } else { + showSwipeIndicator('dislike', parent); + postDiv.classList.add('swipe-left'); + engagePost(nextPost, -25); + } + setTimeout(() => postDiv.remove(), 300); + } else { + postDiv.style.transform = ""; + } + }; + + postDiv.addEventListener('touchstart', e => start(e.touches[0].clientX)); + postDiv.addEventListener('touchmove', e => move(e.touches[0].clientX)); + postDiv.addEventListener('touchend', e => end(e.changedTouches[0].clientX)); + + postDiv.addEventListener('mousedown', e => { + if(e.button !== 0) return; + start(e.clientX); + }); + window.addEventListener('mousemove', e => move(e.clientX)); + window.addEventListener('mouseup', e => end(e.clientX)); + + postDiv.querySelectorAll('img').forEach(img => img.draggable = false); document.querySelector(".posts").appendChild(postDiv); } @@ -1112,8 +1267,23 @@

A } function render() { - if (document.documentElement.scrollHeight < scrollY+innerHeight + 1500) - createNextPost(); + const container = document.querySelector(".posts"); + if (!container) return; + + if (settings.viewMode === "mode-classic") { + if (document.documentElement.scrollHeight < scrollY + innerHeight + 1500) { + createNextPost(); + } + } else if (settings.viewMode === "mode-tinder") { + const activePosts = container.querySelectorAll('.post:not(.swipe-right):not(.swipe-left)'); + if (activePosts.length < 2) { + createNextPost(); + } + } else if (settings.viewMode === "mode-tiktok") { + if (container.scrollHeight - container.scrollTop <= container.clientHeight + 500) { + createNextPost(); + } + } requestAnimationFrame(render); } @@ -1201,6 +1371,50 @@

A loading.innerText = `Loading...\n(${text})`; } + function showSwipeIndicator(type, parent) { + const indicator = document.createElement("div"); + indicator.className = `swipe-indicator ${type === 'like' ? 'indicator-heart' : 'indicator-cross'}`; + indicator.innerHTML = type === 'like' ? '❤️' : '✖️'; + parent.appendChild(indicator); + + requestAnimationFrame(() => { + indicator.classList.add("visible"); + }); + + setTimeout(() => { + indicator.classList.remove("visible"); + setTimeout(() => indicator.remove(), 200); + }, 500); + } + + window.addEventListener('keydown', (e) => { + if (settings.viewMode === "mode-tinder") { + const topPost = document.querySelector(".posts.mode-tinder .post:last-child"); + if (!topPost) return; + const parent = document.querySelector(".posts.mode-tinder"); + if (e.key === "ArrowRight") { + showSwipeIndicator('like', parent); + topPost.classList.add('swipe-right'); + likedPosts.push(topPost.dataset.id); + topPost.querySelector('button').click(); + setTimeout(() => topPost.remove(), 300); + } else if (e.key === "ArrowLeft") { + showSwipeIndicator('dislike', parent); + topPost.classList.add('swipe-left'); + setTimeout(() => topPost.remove(), 300); + } + } else if (settings.viewMode === "mode-tiktok") { + const container = document.querySelector(".posts.mode-tiktok"); + if (e.key === "ArrowDown") { + e.preventDefault(); // Prevent page jitter + container.scrollBy({ top: container.clientHeight, behavior: 'smooth' }); + } else if (e.key === "ArrowUp") { + e.preventDefault(); + container.scrollBy({ top: -container.clientHeight, behavior: 'smooth' }); + } + } + }); + let downloadFinished = false; async function main() { if (/iPad|iPhone|iPod/.test(navigator.userAgent)) @@ -1283,4 +1497,4 @@

A window.onload = main; - \ No newline at end of file + diff --git a/sw.js b/sw.js index 7f7fa49..994d4df 100644 --- a/sw.js +++ b/sw.js @@ -1,4 +1,4 @@ -const SW_VERSION = '1.1.3'; +const SW_VERSION = '1.1.4'; self.addEventListener('install', () => { self.skipWaiting(); diff --git a/version.json b/version.json index 21ad5a9..bd6f999 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "html": "1.1.4", - "sw": "1.1.3", + "html": "1.1.5", + "sw": "1.1.4", "simple": 228005406 } \ No newline at end of file