diff --git a/module/webui/index.html b/module/webui/index.html index 307a6ac..2ae3529 100644 --- a/module/webui/index.html +++ b/module/webui/index.html @@ -32,7 +32,7 @@
@@ -95,7 +95,14 @@

- +
+ + +
@@ -165,7 +172,9 @@
- +
+ +
diff --git a/module/webui/scripts/about.js b/module/webui/scripts/about.js index 2aee469..3bc013e 100644 --- a/module/webui/scripts/about.js +++ b/module/webui/scripts/about.js @@ -1,4 +1,4 @@ -import { execCommand } from './main.js'; +import { execCommand, linkRedirect } from './main.js'; const telegramLink = document.getElementById('telegram'); const githubLink = document.getElementById('github'); @@ -38,17 +38,9 @@ document.getElementById("about").addEventListener("click", () => { }); // Event listener for link redirect -telegramLink.addEventListener('click', async () => { - try { - await execCommand('am start -a android.intent.action.VIEW -d https://t.me/kowchannel'); - } catch (error) { - console.error('Error opening Telegram link:', error); - } +telegramLink.addEventListener('click', function() { + linkRedirect('https://t.me/kowchannel'); }); -githubLink.addEventListener('click', async () => { - try { - await execCommand('am start -a android.intent.action.VIEW -d https://github.com/KOWX712/Tricky-Addon-Update-Target-List'); - } catch (error) { - console.error('Error opening GitHub link:', error); - } +githubLink.addEventListener('click', function() { + linkRedirect('https://github.com/KOWX712/Tricky-Addon-Update-Target-List'); }); \ No newline at end of file diff --git a/module/webui/scripts/applist.js b/module/webui/scripts/applist.js index 5bd76c7..141ba55 100644 --- a/module/webui/scripts/applist.js +++ b/module/webui/scripts/applist.js @@ -86,7 +86,7 @@ export async function fetchAppList() { } catch (error) { console.error("Failed to fetch or render app list with names:", error); } - floatingBtn.style.transform = "translateY(-120px)"; + floatingBtn.style.transform = 'translateY(0)'; toggleableCheckbox(); if (appListContainer.firstChild !== updateCard) { appListContainer.insertBefore(updateCard, appListContainer.firstChild); @@ -119,9 +119,7 @@ function toggleableCheckbox() { const content = card.querySelector(".content"); const checkbox = content.querySelector(".checkbox"); content.addEventListener("click", (event) => { - if (event.target !== checkbox) { - checkbox.checked = !checkbox.checked; - } + checkbox.checked = !checkbox.checked; }); }); } \ No newline at end of file diff --git a/module/webui/scripts/main.js b/module/webui/scripts/main.js index d93a91b..7ec252c 100644 --- a/module/webui/scripts/main.js +++ b/module/webui/scripts/main.js @@ -12,13 +12,14 @@ export const noConnection = document.querySelector('.no-connection'); // Loading, Save and Prompt Elements const loadingIndicator = document.querySelector('.loading'); const prompt = document.getElementById('prompt'); +const floatingCard = document.querySelector('.floating-card'); export const floatingBtn = document.querySelector('.floating-btn'); export const basePath = "set-path"; export const appsWithExclamation = []; export const appsWithQuestion = []; const ADDITIONAL_APPS = [ "com.google.android.gms", "io.github.vvb2060.keyattestation", "io.github.vvb2060.mahoshojo", "icu.nullptr.nativetest" ]; -const rippleClasses = ['.language-option', '.menu-button', '.menu-options li', '.search-card', '.card', '.update-card', '.link-icon', '.floating-btn', '.uninstall-container']; +const rippleClasses = ['.language-option', '.menu-button', '.menu-options li', '.search-card', '.card', '.update-card', '.link-icon', '.floating-btn', '.uninstall-container', '.boot-hash-save-button', '.boot-hash-value']; // Variables let e = 0; @@ -41,7 +42,7 @@ async function refreshAppList() { isRefreshing = true; title.style.transform = 'translateY(0)'; searchMenuContainer.style.transform = 'translateY(0)'; - floatingBtn.style.transform = 'translateY(0)'; + hideFloatingBtn(); searchInput.value = ''; clearBtn.style.display = "none"; appListContainer.innerHTML = ''; @@ -89,15 +90,27 @@ export function showPrompt(key, isSuccess = true) { clearTimeout(window.promptTimeout); } setTimeout(() => { - prompt.classList.add('visible'); - prompt.classList.remove('hidden'); + if (typeof ksu !== 'undefined' && ksu.mmrl) { + prompt.style.transform = 'translateY(calc((var(--window-inset-bottom) + 60%) * -1))'; + } else { + prompt.style.transform = 'translateY(-60%)'; + } window.promptTimeout = setTimeout(() => { - prompt.classList.remove('visible'); - prompt.classList.add('hidden'); + prompt.style.transform = 'translateY(100%)'; }, 3000); }, 200); } +// Function to redirect link on external browser +export async function linkRedirect(link) { + try { + await execCommand(`am start -a android.intent.action.VIEW -d ${link}`); + } catch (error) { + toast('error!'); + console.error('Error redirect link:', error); + } +} + // Save configure and preserve ! and ? in target.txt document.getElementById("save").addEventListener("click", async () => { const selectedApps = Array.from(appListContainer.querySelectorAll(".checkbox:checked")) @@ -155,6 +168,16 @@ function adjustHeaderForMMRL() { const insetTopValue = parseInt(insetTop, 10); searchMenuContainer.style.top = `${insetTopValue + 40}px`; headerBlock.style.display = 'block'; + floatingCard.style.bottom = 'calc(var(--window-inset-bottom) + 50px)'; + } +} + +// Funtion to adapt floating button hide in MMRL +function hideFloatingBtn() { + if (typeof ksu !== 'undefined' && ksu.mmrl) { + floatingBtn.style.transform = 'translateY(calc(var(--window-inset-bottom) + 120px))'; + } else { + floatingBtn.style.transform = 'translateY(120px)'; } } @@ -162,26 +185,55 @@ function adjustHeaderForMMRL() { function applyRippleEffect() { rippleClasses.forEach(selector => { document.querySelectorAll(selector).forEach(element => { - element.addEventListener("click", function(event) { + element.addEventListener("pointerdown", function (event) { + if (isScrolling) return; + const ripple = document.createElement("span"); ripple.classList.add("ripple"); + // Calculate ripple size and position const rect = element.getBoundingClientRect(); const width = rect.width; const size = Math.max(rect.width, rect.height); const x = event.clientX - rect.left - size / 2; const y = event.clientY - rect.top - size / 2; - let duration = 0.3 + (width / 800) * 0.5; + // Determine animation duration + let duration = 0.2 + (width / 800) * 0.5; duration = Math.min(0.8, Math.max(0.2, duration)); + + // Set ripple styles ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${x}px`; ripple.style.top = `${y}px`; ripple.style.animationDuration = `${duration}s`; + ripple.style.transition = `opacity ${duration}s ease`; + + // Adaptive color + const computedStyle = window.getComputedStyle(element); + const bgColor = computedStyle.backgroundColor || "rgba(0, 0, 0, 0)"; + const textColor = computedStyle.color; + const isDarkColor = (color) => { + const rgb = color.match(/\d+/g); + if (!rgb) return false; + const [r, g, b] = rgb.map(Number); + return (r * 0.299 + g * 0.587 + b * 0.114) < 128; // Luma formula + }; + ripple.style.backgroundColor = isDarkColor(bgColor) ? "rgba(255, 255, 255, 0.2)" : ""; + + // Append ripple and handle cleanup element.appendChild(ripple); - ripple.addEventListener("animationend", () => { - ripple.remove(); - }); + const handlePointerUp = () => { + ripple.classList.add("end"); + setTimeout(() => { + ripple.classList.remove("end"); + ripple.remove(); + }, duration * 1000); + element.removeEventListener("pointerup", handlePointerUp); + element.removeEventListener("pointercancel", handlePointerUp); + }; + element.addEventListener("pointerup", handlePointerUp); + element.addEventListener("pointercancel", handlePointerUp); }); }); }); @@ -189,19 +241,26 @@ function applyRippleEffect() { // Scroll event let lastScrollY = window.scrollY; +let isScrolling = false; +let scrollTimeout; const scrollThreshold = 40; window.addEventListener('scroll', () => { + isScrolling = true; + clearTimeout(scrollTimeout); + scrollTimeout = setTimeout(() => { + isScrolling = false; + }, 200); if (isRefreshing) return; if (window.scrollY > lastScrollY && window.scrollY > scrollThreshold) { title.style.transform = 'translateY(-80px)'; headerBlock.style.transform = 'translateY(-80px)'; searchMenuContainer.style.transform = 'translateY(-40px)'; - floatingBtn.style.transform = 'translateY(0)'; + hideFloatingBtn(); } else if (window.scrollY < lastScrollY) { headerBlock.style.transform = 'translateY(0)'; title.style.transform = 'translateY(0)'; searchMenuContainer.style.transform = 'translateY(0)'; - floatingBtn.style.transform = 'translateY(-120px)'; + floatingBtn.style.transform = 'translateY(0)'; } lastScrollY = window.scrollY; }); @@ -254,4 +313,9 @@ export async function execCommand(command) { reject(error); } }); +} + +// Function to toast message +export function toast(message) { + ksu.toast(message); } \ No newline at end of file diff --git a/module/webui/styles/about.css b/module/webui/styles/about.css index 676f3fa..a6b95df 100644 --- a/module/webui/styles/about.css +++ b/module/webui/styles/about.css @@ -53,14 +53,17 @@ #module_name_line1 { font-size: 26px; + user-select: none; } #module_name_line2 { font-size: 22px; + user-select: none; } #authored { font-size: 14px; + user-select: none; } #disclaimer { @@ -70,6 +73,7 @@ #acknowledgment { font-weight: bold; font-size: 18px; + user-select: none; } .link-icon { @@ -95,6 +99,7 @@ background-color: #38A7ED; color: #fff; fill: #fff; + user-select: none; } #github { @@ -103,6 +108,7 @@ background-color: #606060; color: #fff; fill: #fff; + user-select: none; } #link-text { diff --git a/module/webui/styles/applist.css b/module/webui/styles/applist.css index f6eb4cc..65c742c 100644 --- a/module/webui/styles/applist.css +++ b/module/webui/styles/applist.css @@ -67,11 +67,66 @@ max-width: calc(100% - 30px); overflow-wrap: break-word; word-break: break-word; + user-select: none; +} + +.checkbox-wrapper { + position: relative; + display: inline-block; + width: 20px; + height: 20px; + margin-left: auto; } .checkbox { - margin-left: auto; - transform: scale(1.15); + opacity: 0; + position: absolute; + width: 0; + height: 0; +} + +.custom-checkbox { + position: relative; + display: inline-block; + width: 100%; + height: 100%; + border: 2px solid #ccc; + border-radius: 4px; + transition: border-color 1s ease, transform 0.3s ease, background-color 0.4s cubic-bezier(0.22, 1, 0.36, 1); + box-sizing: border-box; +} + +.tick-symbol { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0); + opacity: 0; + transition: transform 0.2s ease-out, opacity 0.3s ease; +} + +.checkbox:checked + .custom-checkbox { + border-color: #007bff; + background-color: #007bff; + transition: border-color 0.1s ease; + animation: border-bounce 0.3s ease-out; +} + +.checkbox:checked + .custom-checkbox .tick-symbol { + transform: translate(-50%, -50%) scale(1); + opacity: 1; +} + +@keyframes border-bounce { + 0% { + transform: scale(1); + } + 50% { + transform: scale(0.8); + } + 100% { + transform: scale(1); + } } @media (prefers-color-scheme: dark) { diff --git a/module/webui/styles/boot-hash.css b/module/webui/styles/boot-hash.css index 3cbaac9..d8bb635 100644 --- a/module/webui/styles/boot-hash.css +++ b/module/webui/styles/boot-hash.css @@ -42,15 +42,30 @@ opacity: 1; } -.input-box { - width: calc(100% - 20px); +.boot-hash-value { + width: 100%; height: 100px; - resize: none; - padding: 9px; font-size: 16px; background-color: #FFF; border: 1px solid #ccc; border-radius: 10px; + box-sizing: border-box; + position: relative; + overflow: hidden; +} + +.input-box { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + padding: 10px; + font-size: 16px; + border-radius: 9px; + box-sizing: border-box; + background-color: transparent; + outline-color: #007bff; } .button-container { @@ -67,11 +82,9 @@ background-color: #007bff; color: white; margin-left: auto; - transition: background-color 0.2s ease; -} - -.boot-hash-save-button:active { - background-color: #003d80; + position: relative; + overflow: hidden; + user-select: none; } @media (prefers-color-scheme: dark) { @@ -81,6 +94,9 @@ .input-box { color: #fff; + } + + .boot-hash-value { background-color: #343434; border: 1px solid #6E6E6E; } diff --git a/module/webui/styles/global.css b/module/webui/styles/global.css index 57355f4..cb2b333 100644 --- a/module/webui/styles/global.css +++ b/module/webui/styles/global.css @@ -12,7 +12,7 @@ body { display: flex; justify-content: center; position: fixed; - bottom: -70px; + bottom: 50px; left: 50%; transform: translateX(-50%); z-index: 3; @@ -30,9 +30,10 @@ body { padding: 10px 20px; font-size: 20px; font-weight: bold; - transition: transform 0.3s ease-in-out, background-color 0.2s ease; + transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1); border-radius: 50px 50px; overflow: hidden; + user-select: none; } .prompt { @@ -49,49 +50,13 @@ body { width: auto; max-width: calc(100% - 40px); transform: translateY(100%); - transition: transform 0.5s ease; -} - -.prompt.visible { - animation: YbounceIn 0.4s forwards; -} - -.prompt.hidden { - animation: YbounceOut 0.4s forwards; + transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1); } .prompt.error { background-color: #f44336; } -@keyframes YbounceIn { - 0% { - transform: translateY(100%); - } - - 50% { - transform: translateY(-80%); - } - - 100% { - transform: translateY(-60%); - } -} - -@keyframes YbounceOut { - 0% { - transform: translateY(-60%); - } - - 50% { - transform: translateY(-80%); - } - - 100% { - transform: translateY(100%); - } -} - .loading { position: fixed; top: 0; @@ -130,6 +95,7 @@ body { background-color: #CE0000; white-space: nowrap; overflow: hidden; + user-select: none; } .uninstall-container i { @@ -150,15 +116,19 @@ body { position: absolute; border-radius: 50%; transform: scale(0); - animation: ripple-animation ease-out; + opacity: 1; + animation: ripple-animation ease-out forwards; pointer-events: none; background: rgba(0, 0, 0, 0.2); } +.ripple.end { + opacity: 0; +} + @keyframes ripple-animation { to { - transform: scale(4); - opacity: 0; + transform: scale(3); } } @@ -167,9 +137,4 @@ body { background-color: #121212; color: #fff; } - - .ripple { - background: rgba(255, 255, 255, 0.2); - } - } \ No newline at end of file diff --git a/module/webui/styles/header.css b/module/webui/styles/header.css index 97264b7..f4c86c9 100644 --- a/module/webui/styles/header.css +++ b/module/webui/styles/header.css @@ -8,12 +8,13 @@ width: calc(100% - 10px); max-width: 1100px; background-color: #F5F5F5; - transition: transform 0.3s ease; + transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1); z-index: 1100; margin-left: auto; margin-right: auto; left: 0; right: 0; + user-select: none; } .header-block { @@ -24,7 +25,7 @@ left: 0; width: 100%; z-index: 1100; - transition: transform 0.3s ease; + transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1); height: var(--window-inset-top); } @@ -74,7 +75,7 @@ max-height: calc(100vh - 50px); overflow-y: auto; transform: translateY(-10px); - transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease; + transition: all 0.4s cubic-bezier(0.22, 1, 0.36, 1); } .language-menu.show { @@ -97,6 +98,7 @@ transition: background-color 0.2s ease; position: relative; overflow: hidden; + user-select: none; } .language-option:last-child { @@ -133,7 +135,7 @@ justify-content: center; align-items: center; opacity: 0; - transition: opacity 0.2s ease; + transition: opacity 0.4s cubic-bezier(0.22, 1, 0.36, 1); } .help-overlay.show { @@ -174,6 +176,7 @@ .help-content p { font-size: 26px; + user-select: none; } .help-content ul { diff --git a/module/webui/styles/search_menu.css b/module/webui/styles/search_menu.css index 982387b..7c9fff5 100644 --- a/module/webui/styles/search_menu.css +++ b/module/webui/styles/search_menu.css @@ -6,7 +6,7 @@ width: calc(100% - 20px); max-width: 1100px; z-index: 1000; - transition: transform 0.3s ease; + transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1); margin-left: auto; margin-right: auto; left: 0; @@ -86,7 +86,7 @@ display: inline-block; fill: #000; transform: rotate(0deg); - transition: transform 0.2s ease; + transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1); } .menu-icon.menu-open { @@ -108,10 +108,10 @@ max-height: calc(100vh - 120px); overflow-y: auto; white-space: nowrap; - opacity: 0; visibility: hidden; transform: translateX(120%); - transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s ease; + transition: all 0.4s cubic-bezier(0.22, 1, 0.36, 1); + user-select: none; } #select-denylist { @@ -120,7 +120,6 @@ .menu-options.visible { display: block; - opacity: 1; visibility: visible; transform: translateX(0); }