Support ! and ? mode in WebUI

- Long press to open mode menu
- Show toast on error log

#7
This commit is contained in:
KOWX712
2024-12-29 02:38:47 +08:00
parent 940d809ac5
commit 77a6e06631
7 changed files with 289 additions and 12 deletions

View File

@@ -90,10 +90,37 @@
<p id="update-available" data-i18n="update_banner.update_available"></p> <p id="update-available" data-i18n="update_banner.update_available"></p>
<p id="redirect-to-release" data-i18n="update_banner.redirect_to_release"></p> <p id="redirect-to-release" data-i18n="update_banner.redirect_to_release"></p>
</div> </div>
<div class="mode-overlay"></div>
<template id="app-template"> <template id="app-template">
<div class="card-box"> <div class="card-box">
<div class="card" id="card"> <div class="card" id="card">
<div class="content" data-package=""> <div class="content" data-package="">
<div class="mode">
<label class="mode-switch" id="normal">
<input type="radio" class="mode-input" id="normal-mode">
<i class="mode-icon">
<div class="status-indicator" id="normal-indicator">
<svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#ffffff"><path d="M480-480Zm0 280q-116 0-198-82t-82-198q0-116 82-198t198-82q116 0 198 82t82 198q0 116-82 198t-198 82Zm0-80q83 0 141.5-58.5T680-480q0-83-58.5-141.5T480-680q-83 0-141.5 58.5T280-480q0 83 58.5 141.5T480-280Z"/></svg>
</div>
</i>
</label>
<label class="mode-switch" id="hack">
<input type="radio" class="mode-input" id="hack-mode">
<i class="mode-icon">
<div class="status-indicator" id="hack-indicator">
<svg xmlns="http://www.w3.org/2000/svg" height="19px" viewBox="0 -960 960 960" width="19px" fill="#ffffff"><path d="M424-320q0-81 14.5-116.5T500-514q41-36 62.5-62.5T584-637q0-41-27.5-68T480-732q-51 0-77.5 31T365-638l-103-44q21-64 77-111t141-47q105 0 161.5 58.5T698-641q0 50-21.5 85.5T609-475q-49 47-59.5 71.5T539-320H424Zm56 240q-33 0-56.5-23.5T400-160q0-33 23.5-56.5T480-240q33 0 56.5 23.5T560-160q0 33-23.5 56.5T480-80Z"/></svg>
</div>
</i>
</label>
<label class="mode-switch" id="generate">
<input type="radio" class="mode-input" id="generate-mode">
<i class="mode-icon">
<div class="status-indicator" id="generate-indicator">
<svg xmlns="http://www.w3.org/2000/svg" height="18px" viewBox="0 -960 960 960" width="18px" fill="#ffffff"><path d="M480-120q-33 0-56.5-23.5T400-200q0-33 23.5-56.5T480-280q33 0 56.5 23.5T560-200q0 33-23.5 56.5T480-120Zm-80-240v-480h160v480H400Z"/></svg>
</div>
</i>
</label>
</div>
<p class="name"></p> <p class="name"></p>
<div class="checkbox-wrapper"> <div class="checkbox-wrapper">
<input type="checkbox" class="checkbox" id="checkbox1" disabled /> <input type="checkbox" class="checkbox" id="checkbox1" disabled />

View File

@@ -1,6 +1,7 @@
import { basePath, execCommand, floatingBtn, appsWithExclamation, appsWithQuestion } from './main.js'; import { basePath, execCommand, floatingBtn, appsWithExclamation, appsWithQuestion, toast } from './main.js';
const appTemplate = document.getElementById('app-template').content; const appTemplate = document.getElementById('app-template').content;
const modeOverlay = document.querySelector('.mode-overlay');
export const appListContainer = document.getElementById('apps-list'); export const appListContainer = document.getElementById('apps-list');
export const updateCard = document.getElementById('update-card'); export const updateCard = document.getElementById('update-card');
@@ -13,6 +14,7 @@ export async function fetchAppList() {
targetList = processTargetList(targetFileContent); targetList = processTargetList(targetFileContent);
console.log("Current target list:", targetList); console.log("Current target list:", targetList);
} catch (error) { } catch (error) {
toast("Failed to read target.txt!");
console.error("Failed to read target.txt file:", error); console.error("Failed to read target.txt file:", error);
} }
@@ -35,7 +37,7 @@ export async function fetchAppList() {
console.warn("Applist file not found or could not be loaded. Skipping applist lookup."); console.warn("Applist file not found or could not be loaded. Skipping applist lookup.");
} }
const result = await execCommand("pm list packages -3"); const result = await execCommand('pm list packages -3; pm path com.google.android.gms >/dev/null 2>&1 && echo "package:com.google.android.gms"');
const appEntries = result const appEntries = result
.split("\n") .split("\n")
.map(line => { .map(line => {
@@ -76,6 +78,26 @@ export async function fetchAppList() {
const appElement = document.importNode(appTemplate, true); const appElement = document.importNode(appTemplate, true);
const contentElement = appElement.querySelector(".content"); const contentElement = appElement.querySelector(".content");
contentElement.setAttribute("data-package", packageName); contentElement.setAttribute("data-package", packageName);
// Set unique names for radio button groups
const radioButtons = appElement.querySelectorAll('input[type="radio"]');
radioButtons.forEach((radio) => {
radio.name = `mode-radio-${packageName}`;
});
// Preselect the radio button based on the package name
const generateRadio = appElement.querySelector('#generate-mode');
const hackRadio = appElement.querySelector('#hack-mode');
const normalRadio = appElement.querySelector('#normal-mode');
if (appsWithExclamation.includes(packageName)) {
generateRadio.checked = true;
} else if (appsWithQuestion.includes(packageName)) {
hackRadio.checked = true;
} else {
normalRadio.checked = true;
}
const nameElement = appElement.querySelector(".name"); const nameElement = appElement.querySelector(".name");
nameElement.innerHTML = `<strong>${appName || "Unknown App"}</strong><br>${packageName}`; nameElement.innerHTML = `<strong>${appName || "Unknown App"}</strong><br>${packageName}`;
const checkbox = appElement.querySelector(".checkbox"); const checkbox = appElement.querySelector(".checkbox");
@@ -84,6 +106,7 @@ export async function fetchAppList() {
}); });
console.log("App list with names and packages rendered successfully."); console.log("App list with names and packages rendered successfully.");
} catch (error) { } catch (error) {
toast("Failed to fetch app list!");
console.error("Failed to fetch or render app list with names:", error); console.error("Failed to fetch or render app list with names:", error);
} }
floatingBtn.style.transform = 'translateY(0)'; floatingBtn.style.transform = 'translateY(0)';
@@ -91,6 +114,10 @@ export async function fetchAppList() {
if (appListContainer.firstChild !== updateCard) { if (appListContainer.firstChild !== updateCard) {
appListContainer.insertBefore(updateCard, appListContainer.firstChild); appListContainer.insertBefore(updateCard, appListContainer.firstChild);
} }
const checkboxes = appListContainer.querySelectorAll(".checkbox");
setupRadioButtonListeners();
setupCardHoldListener();
updateCheckboxColor();
} }
// Function to save app with ! and ? then process target list // Function to save app with ! and ? then process target list
@@ -122,4 +149,112 @@ function toggleableCheckbox() {
checkbox.checked = !checkbox.checked; checkbox.checked = !checkbox.checked;
}); });
}); });
}
// Add eventlistener to mode button
function setupRadioButtonListeners() {
const radioButtons = appListContainer.querySelectorAll('input[type="radio"]');
radioButtons.forEach((radioButton) => {
radioButton.addEventListener('change', (event) => {
const card = radioButton.closest(".card");
const packageName = card.querySelector(".content").getAttribute("data-package");
if (radioButton.id === 'generate-mode') {
if (!appsWithExclamation.includes(packageName)) {
appsWithExclamation.push(packageName);
}
const indexInQuestion = appsWithQuestion.indexOf(packageName);
if (indexInQuestion > -1) {
appsWithQuestion.splice(indexInQuestion, 1);
}
} else if (radioButton.id === 'hack-mode') {
if (!appsWithQuestion.includes(packageName)) {
appsWithQuestion.push(packageName);
}
const indexInExclamation = appsWithExclamation.indexOf(packageName);
if (indexInExclamation > -1) {
appsWithExclamation.splice(indexInExclamation, 1);
}
} else if (radioButton.id === 'normal-mode') {
const indexInExclamation = appsWithExclamation.indexOf(packageName);
if (indexInExclamation > -1) {
appsWithExclamation.splice(indexInExclamation, 1);
}
const indexInQuestion = appsWithQuestion.indexOf(packageName);
if (indexInQuestion > -1) {
appsWithQuestion.splice(indexInQuestion, 1);
}
}
updateCheckboxColor();
console.log("Updated appsWithExclamation:", appsWithExclamation);
console.log("Updated appsWithQuestion:", appsWithQuestion);
});
});
}
// Hold to open menu
function setupCardHoldListener() {
let holdTimeout;
function showMode(card) {
const modeElement = card.querySelector(".mode");
if (modeElement) {
modeElement.style.display = "flex";
modeOverlay.style.display = "flex";
setTimeout(() => {
modeElement.classList.add('show');
}, 10);
}
}
function hideAllModes() {
const allModeElements = appListContainer.querySelectorAll(".mode");
allModeElements.forEach((modeElement) => {
modeElement.classList.remove('show');
modeOverlay.style.display = "none";
setTimeout(() => {
modeElement.style.display = "none";
}, 200);
});
}
const cards = appListContainer.querySelectorAll(".card");
cards.forEach((card) => {
card.addEventListener("pointerdown", () => {
const checkbox = card.querySelector(".checkbox");
if (checkbox && checkbox.checked) {
holdTimeout = setTimeout(() => {
showMode(card);
}, 500);
}
});
card.addEventListener("pointerup", () => clearTimeout(holdTimeout));
card.addEventListener("pointercancel", () => clearTimeout(holdTimeout));
});
// Close on click/scroll
document.addEventListener("click", (event) => {
if (!event.target.closest(".mode") || modeOverlay.contains(event.target)) {
hideAllModes();
} else if (event.target.closest(".status-indicator")) {
setTimeout(() => {
hideAllModes();
}, 200);
}
});
window.addEventListener("scroll", hideAllModes);
}
// Function to update card borders color
function updateCheckboxColor() {
const cards = appListContainer.querySelectorAll(".card");
cards.forEach((card) => {
const packageName = card.querySelector(".content").getAttribute("data-package");
const checkbox = card.querySelector(".checkbox");
checkbox.classList.remove("checkbox-checked-generate", "checkbox-checked-hack");
if (appsWithExclamation.includes(packageName)) {
checkbox.classList.add("checkbox-checked-generate");
} else if (appsWithQuestion.includes(packageName)) {
checkbox.classList.add("checkbox-checked-hack");
} else if (checkbox.checked) {
checkbox.classList.remove("checkbox-checked-generate", "checkbox-checked-hack");
}
});
} }

View File

@@ -1,4 +1,4 @@
import { basePath, execCommand } from './main.js'; import { basePath, execCommand, toast } from './main.js';
const languageButton = document.querySelector('.language-button'); const languageButton = document.querySelector('.language-button');
const languageMenu = document.querySelector('.language-menu'); const languageMenu = document.querySelector('.language-menu');
@@ -16,6 +16,7 @@ export async function initializeAvailableLanguages() {
availableLanguages = multiLang.trim().split('\n'); availableLanguages = multiLang.trim().split('\n');
generateLanguageMenu(); generateLanguageMenu();
} catch (error) { } catch (error) {
toast("Failed to get available langauge!");
console.error('Failed to fetch available languages:', error); console.error('Failed to fetch available languages:', error);
availableLanguages = ['en-US']; availableLanguages = ['en-US'];
} }
@@ -41,6 +42,7 @@ export async function loadTranslations(lang) {
translations = await response.json(); translations = await response.json();
applyTranslations(); applyTranslations();
} catch (error) { } catch (error) {
toast(`Failed to load translation for ${lang}!`);
console.error(`Error loading translations for ${lang}:`, error); console.error(`Error loading translations for ${lang}:`, error);
if (lang !== 'en-US') { if (lang !== 'en-US') {
console.log("Falling back to English."); console.log("Falling back to English.");

View File

@@ -33,7 +33,7 @@ async function getModuleVersion() {
moduleVersion.textContent = version; moduleVersion.textContent = version;
} catch (error) { } catch (error) {
console.error("Failed to read version from module.prop:", error); console.error("Failed to read version from module.prop:", error);
updateVersion("Error reading version from module.prop"); updateVersion("Error");
} }
} }
@@ -55,6 +55,7 @@ async function refreshAppList() {
updateCheck(); updateCheck();
await execCommand(`[ -f ${basePath}common/tmp/exclude-list ] && rm -f "${basePath}common/tmp/exclude-list"`); await execCommand(`[ -f ${basePath}common/tmp/exclude-list ] && rm -f "${basePath}common/tmp/exclude-list"`);
} catch (error) { } catch (error) {
toast("Failed!");
console.error("Error occurred:", error); console.error("Error occurred:", error);
} }
} }
@@ -77,6 +78,7 @@ async function checkMagisk() {
console.log("not running on Magisk, leaving denylist element hidden."); console.log("not running on Magisk, leaving denylist element hidden.");
} }
} catch (error) { } catch (error) {
toast("Failed to check Magisk!");
console.error("Error while checking denylist conditions:", error); console.error("Error while checking denylist conditions:", error);
} }
} }
@@ -106,7 +108,7 @@ export async function linkRedirect(link) {
try { try {
await execCommand(`am start -a android.intent.action.VIEW -d ${link}`); await execCommand(`am start -a android.intent.action.VIEW -d ${link}`);
} catch (error) { } catch (error) {
toast('error!'); toast("Failed!");
console.error('Error redirect link:', error); console.error('Error redirect link:', error);
} }
} }
@@ -199,7 +201,7 @@ function applyRippleEffect() {
const y = event.clientY - rect.top - size / 2; const y = event.clientY - rect.top - size / 2;
// Determine animation duration // Determine animation duration
let duration = 0.2 + (width / 800) * 0.5; let duration = 0.2 + (width / 800) * 0.4;
duration = Math.min(0.8, Math.max(0.2, duration)); duration = Math.min(0.8, Math.max(0.2, duration));
// Set ripple styles // Set ripple styles
@@ -289,6 +291,7 @@ updateCard.addEventListener('click', async () => {
try { try {
await execCommand('am start -a android.intent.action.VIEW -d https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases/latest'); await execCommand('am start -a android.intent.action.VIEW -d https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases/latest');
} catch (error) { } catch (error) {
toast("Failed!");
console.error('Error opening GitHub Release link:', error); console.error('Error opening GitHub Release link:', error);
} }
}); });

View File

@@ -1,4 +1,4 @@
import { basePath, execCommand, showPrompt } from './main.js'; import { basePath, execCommand, showPrompt, toast } from './main.js';
// Function to check or uncheck all app // Function to check or uncheck all app
function toggleCheckboxes(shouldCheck) { function toggleCheckboxes(shouldCheck) {
@@ -32,6 +32,7 @@ document.getElementById("select-denylist").addEventListener("click", async () =>
}); });
console.log("Denylist apps selected successfully."); console.log("Denylist apps selected successfully.");
} catch (error) { } catch (error) {
toast("Failed to read DenyList!");
console.error("Failed to select Denylist apps:", error); console.error("Failed to select Denylist apps:", error);
} }
}); });
@@ -65,6 +66,7 @@ document.getElementById("deselect-unnecessary").addEventListener("click", async
}); });
console.log("Unnecessary apps deselected successfully."); console.log("Unnecessary apps deselected successfully.");
} catch (error) { } catch (error) {
toast("Failed!");
console.error("Failed to deselect unnecessary apps:", error); console.error("Failed to deselect unnecessary apps:", error);
} }
}); });

View File

@@ -46,7 +46,7 @@
margin: 0 auto; margin: 0 auto;
margin-bottom: 10px; margin-bottom: 10px;
outline: none; outline: none;
padding: 12px; padding: 10px;
width: calc(100% - 30px); width: calc(100% - 30px);
max-width: 900px; max-width: 900px;
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
@@ -58,6 +58,83 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
position: relative;
}
.mode {
display: none;
flex-direction: row;
gap: 10px;
padding: 10px;
position: absolute;
left: 0;
right: 0;
margin: auto;
width: fit-content;
background-color: #B1B1B1;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
border: 1px solid #ccc;
border-radius: 12px;
opacity: 0;
transform: scale(0);
transition: all 0.4s cubic-bezier(0.22, 1, 0.36, 1);
z-index: 1200;
}
.mode.show {
opacity: 1;
transform: scale(1);
}
.mode-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: none;
z-index: 1100;
}
.mode-switch {
position: relative;
display: inline-block;
}
.mode-input {
opacity: 0;
position: absolute;
width: 0;
height: 0;
}
.status-indicator {
display: flex;
justify-content: center;
align-items: center;
width: 30px;
height: 30px;
border-radius: 7px;
box-sizing: border-box;
transition: border-color 0.2s ease;
border: 3px solid transparent;
}
#normal-indicator {
background-color: #007bff;
}
#hack-indicator {
background-color: #FF8400;
}
#generate-indicator {
background-color: #6C00FF;
}
.mode-input[type="radio"]:checked ~ .mode-icon .status-indicator {
border-color: #fff;
} }
.name { .name {
@@ -92,8 +169,8 @@
height: 100%; height: 100%;
border: 2px solid #ccc; border: 2px solid #ccc;
border-radius: 4px; 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; box-sizing: border-box;
transition: border-color 1s ease, transform 0.3s ease, background-color 0.4s cubic-bezier(0.22, 1, 0.36, 1);
} }
.tick-symbol { .tick-symbol {
@@ -109,7 +186,21 @@
border-color: #007bff; border-color: #007bff;
background-color: #007bff; background-color: #007bff;
transition: border-color 0.1s ease; transition: border-color 0.1s ease;
animation: border-bounce 0.3s ease-out; animation: checked-bounce 0.3s ease-out;
}
.checkbox-checked-generate:checked + .custom-checkbox {
border-color: #6C00FF;
background-color: #6C00FF;
}
.checkbox-checked-hack:checked + .custom-checkbox {
border-color: #FF8400;
background-color: #FF8400;
}
.checkbox:not(:checked) + .custom-checkbox {
animation: unchecked-bounce 0.3s ease-out;
} }
.checkbox:checked + .custom-checkbox .tick-symbol { .checkbox:checked + .custom-checkbox .tick-symbol {
@@ -117,7 +208,19 @@
opacity: 1; opacity: 1;
} }
@keyframes border-bounce { @keyframes checked-bounce {
0% {
transform: scale(1);
}
50% {
transform: scale(0.8);
}
100% {
transform: scale(1);
}
}
@keyframes unchecked-bounce {
0% { 0% {
transform: scale(1); transform: scale(1);
} }
@@ -137,4 +240,9 @@
.update-card { .update-card {
background-color: #4D4D4D; background-color: #4D4D4D;
} }
.mode {
background-color: #343434;
border: 1px solid #6E6E6E;
}
} }

View File

@@ -15,7 +15,7 @@ body {
bottom: 50px; bottom: 50px;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
z-index: 3; z-index: 10;
} }
.floating-btn { .floating-btn {