design improvements, stabilize everything, update readme
This commit is contained in:
parent
48736645c5
commit
0ceb0b61b4
8 changed files with 979 additions and 511 deletions
|
@ -5,7 +5,7 @@
|
|||
A KernelSU module which simplifies the installation of a keybox.xml file by fetching it from a server.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **This project is not hosted on GitHub. If you are looking to contribute or open an issue, please see my repo on [LibreCloud Git](https://git.pontusmail.org/aidan/beesrv).**
|
||||
> **This project is not hosted on GitHub. If you are looking to contribute or open an issue, please see my repo on [p0ntus git](https://git.p0ntus.com/aidan/BeeSrv).**
|
||||
|
||||
## Module
|
||||
|
||||
|
@ -51,7 +51,7 @@ A `beebox.xml` file should be placed the `server/serve/` directory. You will hav
|
|||
|
||||
Thank you to all of the people and projects I have come across while building this! Without you, this project wouldn't be a reality.
|
||||
|
||||
* [Re-Malwack by ZG089](https://github.com/ZG089/Re-Malwack) -
|
||||
* [Re-Malwack by ZG089](https://github.com/ZG089/Re-Malwack) -
|
||||
This helped me so much while writing the module
|
||||
|
||||
* [KernelSU Documentation](https://kernelsu.org/guide/module.html) - Very helpful resource for building a complete module
|
||||
|
|
|
@ -17,15 +17,15 @@ if ! command -v bun &> /dev/null; then
|
|||
fi
|
||||
|
||||
# Check if filename to be created already exists
|
||||
if [ -f "BeeSrv-$version.zip" ]; then
|
||||
echo "[i] BeeSrv-$version.zip already exists, would you like to overwrite it? (y/n)"
|
||||
if [ -f "BeeSrv-$version.zip" ] || [ -f "BeeSrv-$version-debug.zip" ]; then
|
||||
echo "[i] BeeSrv zip files already exist, would you like to overwrite them? (y/n)"
|
||||
read overwrite
|
||||
if [ "$overwrite" != "y" ]; then
|
||||
echo "[!] Aborting..."
|
||||
exit 1
|
||||
else
|
||||
rm -rf BeeSrv-$version.zip
|
||||
echo "[✔] Overwriting BeeSrv-$version.zip..."
|
||||
rm -rf BeeSrv-$version.zip BeeSrv-$version-debug.zip
|
||||
echo "[✔] Overwriting existing zip files..."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -46,9 +46,9 @@ cp -r module tmp
|
|||
echo "[✔] Created working directory"
|
||||
|
||||
# Clean any unnecessary files
|
||||
rm -rf tmp/module/webroot/dist
|
||||
rm -rf tmp/module/webroot/.gitignore
|
||||
rm -rf tmp/module/webroot/package-lock.json
|
||||
rm -rf tmp/webroot/dist
|
||||
rm -rf tmp/webroot/.gitignore
|
||||
rm -rf tmp/webroot/package-lock.json
|
||||
echo "[✔] Completed cleanup"
|
||||
|
||||
# Build webroot
|
||||
|
@ -83,15 +83,23 @@ echo "[✔] Moved built files to webroot"
|
|||
rm -rf webroot/dist
|
||||
echo "[✔] Completed cleanup"
|
||||
|
||||
# Create zip
|
||||
echo "[i] Creating zip..."
|
||||
echo "[i] Creating debug zip..."
|
||||
zip -r ../BeeSrv-$version-debug.zip *
|
||||
echo "[✔] Created debug zip"
|
||||
|
||||
echo "[i] Removing eruda scripts for production version..."
|
||||
sed -i '/<script src="https:\/\/cdn\.jsdelivr\.net\/npm\/eruda"><\/script>/d' webroot/index.html
|
||||
sed -i '/<script>eruda\.init();<\/script>/d' webroot/index.html
|
||||
echo "[✔] Removed eruda scripts"
|
||||
|
||||
echo "[i] Creating production zip..."
|
||||
zip -r ../BeeSrv-$version.zip *
|
||||
cd ..
|
||||
echo "[✔] Created zip"
|
||||
echo "[✔] Created production zip"
|
||||
|
||||
# Clean up
|
||||
rm -rf tmp
|
||||
echo "[✔] Completed cleanup"
|
||||
|
||||
echo ""
|
||||
echo "BeeSrv-$version.zip created successfully!"
|
||||
echo "BeeSrv-$version.zip and BeeSrv-$version-debug.zip created successfully!"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
ui_print ""
|
||||
ui_print "=== BEESRV ==="
|
||||
ui_print "Version: $(grep_prop version $MODPATH/module.prop) ($(grep_prop versionCode $MODPATH/module.prop))"
|
||||
ui_print "Made with ❤️ by ihatenodejs"
|
||||
ui_print "==========================="
|
||||
ui_print "┌───────── BEESRV ─────────┐"
|
||||
ui_print "│ Version: $(grep_prop version $MODPATH/module.prop) ($(grep_prop versionCode $MODPATH/module.prop))"
|
||||
ui_print "│ Made with ❤️ by ihatenodejs"
|
||||
ui_print "└─────────────────────────┘"
|
||||
ui_print ""
|
||||
sleep 0.4
|
||||
|
||||
|
@ -84,5 +84,7 @@ ui_print ""
|
|||
ui_print "== INSTALLATION COMPLETE! =="
|
||||
ui_print ""
|
||||
ui_print "Join our Telegram channel: t.me/pontushub"
|
||||
am start -a android.intent.action.VIEW -d "https://t.me/pontushub"
|
||||
ui_print
|
||||
ui_print
|
||||
ui_print # restart button typically covers content
|
||||
sleep 0.4
|
|
@ -1,8 +1,10 @@
|
|||
function show_usage() {
|
||||
echo "Usage: $0 [-e <email>] [-s <server>]"
|
||||
echo "Usage: $0 [-e <email>] [-s <server>] [-v] [-d]"
|
||||
echo "Options:"
|
||||
echo " -e Email address"
|
||||
echo " -s Server URL"
|
||||
echo " -e <email> Set email address"
|
||||
echo " -s <server> Set server URL"
|
||||
echo " -v Get module version"
|
||||
echo " -d Get debug mode status"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
@ -12,11 +14,14 @@ function set_email() {
|
|||
show_usage
|
||||
fi
|
||||
|
||||
if ! grep -q "EMAIL=" /data/adb/beesrv/config.txt; then
|
||||
echo "EMAIL=$1" >> /data/adb/beesrv/config.txt
|
||||
else
|
||||
sed -i "s/EMAIL=.*/EMAIL=$1/" /data/adb/beesrv/config.txt
|
||||
fi
|
||||
local email="$1"
|
||||
local config_file="/data/adb/beesrv/config.txt"
|
||||
|
||||
touch "$config_file"
|
||||
|
||||
grep -v "^EMAIL=" "$config_file" > "$config_file.tmp" 2>/dev/null || true
|
||||
echo "EMAIL=$email" >> "$config_file.tmp"
|
||||
mv "$config_file.tmp" "$config_file"
|
||||
|
||||
echo "Success"
|
||||
}
|
||||
|
@ -27,16 +32,39 @@ function set_server() {
|
|||
show_usage
|
||||
fi
|
||||
|
||||
if ! grep -q "SERVER=" /data/adb/beesrv/config.txt; then
|
||||
echo "SERVER=$1" >> /data/adb/beesrv/config.txt
|
||||
else
|
||||
sed -i "s/SERVER=.*/SERVER=$1/" /data/adb/beesrv/config.txt
|
||||
fi
|
||||
local server_url="$1"
|
||||
local config_file="/data/adb/beesrv/config.txt"
|
||||
|
||||
touch "$config_file"
|
||||
|
||||
grep -v "^SERVER=" "$config_file" > "$config_file.tmp" 2>/dev/null || true
|
||||
echo "SERVER=$server_url" >> "$config_file.tmp"
|
||||
mv "$config_file.tmp" "$config_file"
|
||||
|
||||
echo "Success"
|
||||
}
|
||||
|
||||
while getopts "e:s:" opt; do
|
||||
function get_version() {
|
||||
local mod_prop
|
||||
mod_prop="$(dirname "$0")/../module.prop"
|
||||
if [ -f "$mod_prop" ]; then
|
||||
version=$(grep "version=" "$mod_prop" | cut -d'=' -f2)
|
||||
echo "$version"
|
||||
else
|
||||
echo "Unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
function get_debug_mode() {
|
||||
local config_file="/data/adb/beesrv/config.txt"
|
||||
if [ -f "$config_file" ] && grep -q "^DEBUG=true$" "$config_file"; then
|
||||
echo "true"
|
||||
else
|
||||
echo "false"
|
||||
fi
|
||||
}
|
||||
|
||||
while getopts "e:s:vd" opt; do
|
||||
case ${opt} in
|
||||
e )
|
||||
set_email "$OPTARG"
|
||||
|
@ -44,6 +72,12 @@ while getopts "e:s:" opt; do
|
|||
s )
|
||||
set_server "$OPTARG"
|
||||
;;
|
||||
v )
|
||||
get_version
|
||||
;;
|
||||
d )
|
||||
get_debug_mode
|
||||
;;
|
||||
\? )
|
||||
echo "[ERROR] Invalid option -$OPTARG"
|
||||
show_usage
|
||||
|
|
|
@ -9,15 +9,15 @@
|
|||
"build:css:watch": "bunx @tailwindcss/cli -i ./src/css/style-pre.css -o ./src/css/style.css --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/cli": "^4.1.4",
|
||||
"@tailwindcss/cli": "^4.1.11",
|
||||
"kernelsu": "^1.0.6",
|
||||
"tailwindcss": "^4.1.4"
|
||||
"tailwindcss": "^4.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"buffer": "^6.0.3",
|
||||
"events": "^3.3.0",
|
||||
"os-browserify": "^0.3.0",
|
||||
"parcel": "^2.14.4",
|
||||
"parcel": "^2.15.4",
|
||||
"process": "^0.11.10",
|
||||
"util": "^0.12.5",
|
||||
"vm-browserify": "^1.1.2"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*! tailwindcss v4.1.3 | MIT License | https://tailwindcss.com */
|
||||
/*! tailwindcss v4.1.11 | MIT License | https://tailwindcss.com */
|
||||
@layer properties;
|
||||
@layer theme, base, components, utilities;
|
||||
@layer theme {
|
||||
|
@ -8,25 +8,36 @@
|
|||
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
||||
"Courier New", monospace;
|
||||
--color-red-400: oklch(70.4% 0.191 22.216);
|
||||
--color-red-500: oklch(63.7% 0.237 25.331);
|
||||
--color-red-900: oklch(39.6% 0.141 25.723);
|
||||
--color-green-400: oklch(79.2% 0.209 151.711);
|
||||
--color-green-600: oklch(62.7% 0.194 149.214);
|
||||
--color-green-700: oklch(52.7% 0.154 150.069);
|
||||
--color-blue-400: oklch(70.7% 0.165 254.624);
|
||||
--color-blue-500: oklch(62.3% 0.214 259.815);
|
||||
--color-blue-600: oklch(54.6% 0.245 262.881);
|
||||
--color-blue-700: oklch(48.8% 0.243 264.376);
|
||||
--color-slate-500: oklch(55.4% 0.046 257.417);
|
||||
--color-slate-600: oklch(44.6% 0.043 257.281);
|
||||
--color-slate-700: oklch(37.2% 0.044 257.287);
|
||||
--color-gray-300: oklch(87.2% 0.01 258.338);
|
||||
--color-gray-400: oklch(70.7% 0.022 261.325);
|
||||
--color-gray-600: oklch(44.6% 0.03 256.802);
|
||||
--color-gray-700: oklch(37.3% 0.034 259.733);
|
||||
--color-gray-800: oklch(27.8% 0.033 256.848);
|
||||
--color-gray-900: oklch(21% 0.034 264.665);
|
||||
--color-white: #fff;
|
||||
--spacing: 0.25rem;
|
||||
--text-xl: 1.25rem;
|
||||
--text-xl--line-height: calc(1.75 / 1.25);
|
||||
--text-xs: 0.75rem;
|
||||
--text-xs--line-height: calc(1 / 0.75);
|
||||
--text-sm: 0.875rem;
|
||||
--text-sm--line-height: calc(1.25 / 0.875);
|
||||
--text-lg: 1.125rem;
|
||||
--text-lg--line-height: calc(1.75 / 1.125);
|
||||
--text-2xl: 1.5rem;
|
||||
--text-2xl--line-height: calc(2 / 1.5);
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--animate-spin: spin 1s linear infinite;
|
||||
--default-transition-duration: 150ms;
|
||||
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
@ -180,17 +191,41 @@
|
|||
}
|
||||
}
|
||||
@layer utilities {
|
||||
.visible {
|
||||
visibility: visible;
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
.mt-4 {
|
||||
margin-top: calc(var(--spacing) * 4);
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
.mr-1 {
|
||||
margin-right: calc(var(--spacing) * 1);
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
.mr-2 {
|
||||
margin-right: calc(var(--spacing) * 2);
|
||||
.inset-y-0 {
|
||||
inset-block: calc(var(--spacing) * 0);
|
||||
}
|
||||
.right-0 {
|
||||
right: calc(var(--spacing) * 0);
|
||||
}
|
||||
.bottom-0 {
|
||||
bottom: calc(var(--spacing) * 0);
|
||||
}
|
||||
.left-0 {
|
||||
left: calc(var(--spacing) * 0);
|
||||
}
|
||||
.my-4 {
|
||||
margin-block: calc(var(--spacing) * 4);
|
||||
}
|
||||
.mt-2 {
|
||||
margin-top: calc(var(--spacing) * 2);
|
||||
}
|
||||
.mt-3 {
|
||||
margin-top: calc(var(--spacing) * 3);
|
||||
}
|
||||
.mr-3 {
|
||||
margin-right: calc(var(--spacing) * 3);
|
||||
}
|
||||
.mb-1 {
|
||||
margin-bottom: calc(var(--spacing) * 1);
|
||||
}
|
||||
.mb-2 {
|
||||
margin-bottom: calc(var(--spacing) * 2);
|
||||
|
@ -198,11 +233,8 @@
|
|||
.mb-4 {
|
||||
margin-bottom: calc(var(--spacing) * 4);
|
||||
}
|
||||
.ml-2 {
|
||||
margin-left: calc(var(--spacing) * 2);
|
||||
}
|
||||
.block {
|
||||
display: block;
|
||||
.mb-6 {
|
||||
margin-bottom: calc(var(--spacing) * 6);
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
|
@ -210,15 +242,18 @@
|
|||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.table {
|
||||
display: table;
|
||||
}
|
||||
.h-4 {
|
||||
height: calc(var(--spacing) * 4);
|
||||
}
|
||||
.h-5 {
|
||||
height: calc(var(--spacing) * 5);
|
||||
}
|
||||
.h-16 {
|
||||
height: calc(var(--spacing) * 16);
|
||||
}
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
.w-4 {
|
||||
width: calc(var(--spacing) * 4);
|
||||
}
|
||||
|
@ -228,37 +263,93 @@
|
|||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
.border-collapse {
|
||||
border-collapse: collapse;
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
.transform {
|
||||
transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y);
|
||||
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
|
||||
}
|
||||
.animate-spin {
|
||||
animation: var(--animate-spin);
|
||||
}
|
||||
.resize {
|
||||
resize: both;
|
||||
}
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.gap-2 {
|
||||
gap: calc(var(--spacing) * 2);
|
||||
}
|
||||
.rounded-md {
|
||||
border-radius: var(--radius-md);
|
||||
.space-y-3 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.space-y-4 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.space-y-6 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.rounded-lg {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
.border {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 1px;
|
||||
}
|
||||
.border-t {
|
||||
border-top-style: var(--tw-border-style);
|
||||
border-top-width: 1px;
|
||||
}
|
||||
.border-t-2 {
|
||||
border-top-style: var(--tw-border-style);
|
||||
border-top-width: 2px;
|
||||
}
|
||||
.border-blue-400 {
|
||||
border-color: var(--color-blue-400);
|
||||
}
|
||||
.border-gray-600 {
|
||||
border-color: var(--color-gray-600);
|
||||
}
|
||||
.border-gray-700 {
|
||||
border-color: var(--color-gray-700);
|
||||
}
|
||||
.border-red-500 {
|
||||
border-color: var(--color-red-500);
|
||||
}
|
||||
.border-red-500\/30 {
|
||||
border-color: color-mix(in srgb, oklch(63.7% 0.237 25.331) 30%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
border-color: color-mix(in oklab, var(--color-red-500) 30%, transparent);
|
||||
}
|
||||
}
|
||||
.bg-blue-600 {
|
||||
background-color: var(--color-blue-600);
|
||||
}
|
||||
.bg-gray-600 {
|
||||
background-color: var(--color-gray-600);
|
||||
}
|
||||
.bg-gray-700 {
|
||||
background-color: var(--color-gray-700);
|
||||
}
|
||||
|
@ -268,8 +359,14 @@
|
|||
.bg-gray-900 {
|
||||
background-color: var(--color-gray-900);
|
||||
}
|
||||
.bg-slate-500 {
|
||||
background-color: var(--color-slate-500);
|
||||
.bg-green-600 {
|
||||
background-color: var(--color-green-600);
|
||||
}
|
||||
.bg-red-900\/20 {
|
||||
background-color: color-mix(in srgb, oklch(39.6% 0.141 25.723) 20%, transparent);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
background-color: color-mix(in oklab, var(--color-red-900) 20%, transparent);
|
||||
}
|
||||
}
|
||||
.bg-slate-600 {
|
||||
background-color: var(--color-slate-600);
|
||||
|
@ -277,31 +374,61 @@
|
|||
.p-2 {
|
||||
padding: calc(var(--spacing) * 2);
|
||||
}
|
||||
.p-3 {
|
||||
padding: calc(var(--spacing) * 3);
|
||||
}
|
||||
.p-4 {
|
||||
padding: calc(var(--spacing) * 4);
|
||||
}
|
||||
.px-3 {
|
||||
padding-inline: calc(var(--spacing) * 3);
|
||||
.p-6 {
|
||||
padding: calc(var(--spacing) * 6);
|
||||
}
|
||||
.px-1 {
|
||||
padding-inline: calc(var(--spacing) * 1);
|
||||
}
|
||||
.px-4 {
|
||||
padding-inline: calc(var(--spacing) * 4);
|
||||
}
|
||||
.py-1 {
|
||||
padding-block: calc(var(--spacing) * 1);
|
||||
.py-0\.5 {
|
||||
padding-block: calc(var(--spacing) * 0.5);
|
||||
}
|
||||
.py-2 {
|
||||
padding-block: calc(var(--spacing) * 2);
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
.py-3 {
|
||||
padding-block: calc(var(--spacing) * 3);
|
||||
}
|
||||
.text-left {
|
||||
text-align: left;
|
||||
.pr-3 {
|
||||
padding-right: calc(var(--spacing) * 3);
|
||||
}
|
||||
.pb-1\.5 {
|
||||
padding-bottom: calc(var(--spacing) * 1.5);
|
||||
}
|
||||
.pb-20 {
|
||||
padding-bottom: calc(var(--spacing) * 20);
|
||||
}
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
.font-mono {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.text-2xl {
|
||||
font-size: var(--text-2xl);
|
||||
line-height: var(--tw-leading, var(--text-2xl--line-height));
|
||||
}
|
||||
.text-lg {
|
||||
font-size: var(--text-lg);
|
||||
line-height: var(--tw-leading, var(--text-lg--line-height));
|
||||
}
|
||||
.text-sm {
|
||||
font-size: var(--text-sm);
|
||||
line-height: var(--tw-leading, var(--text-sm--line-height));
|
||||
}
|
||||
.text-xs {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--tw-leading, var(--text-xs--line-height));
|
||||
}
|
||||
.font-bold {
|
||||
--tw-font-weight: var(--font-weight-bold);
|
||||
font-weight: var(--font-weight-bold);
|
||||
|
@ -310,9 +437,15 @@
|
|||
--tw-font-weight: var(--font-weight-semibold);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
.text-blue-400 {
|
||||
color: var(--color-blue-400);
|
||||
}
|
||||
.text-gray-300 {
|
||||
color: var(--color-gray-300);
|
||||
}
|
||||
.text-gray-400 {
|
||||
color: var(--color-gray-400);
|
||||
}
|
||||
.text-green-400 {
|
||||
color: var(--color-green-400);
|
||||
}
|
||||
|
@ -322,57 +455,125 @@
|
|||
.text-white {
|
||||
color: var(--color-white);
|
||||
}
|
||||
.underline {
|
||||
text-decoration-line: underline;
|
||||
.placeholder-gray-400 {
|
||||
&::placeholder {
|
||||
color: var(--color-gray-400);
|
||||
}
|
||||
}
|
||||
.outline {
|
||||
outline-style: var(--tw-outline-style);
|
||||
outline-width: 1px;
|
||||
.transition-all {
|
||||
transition-property: all;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
}
|
||||
.transition-colors {
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
}
|
||||
.hover\:bg-slate-600 {
|
||||
.hover\:bg-blue-700 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--color-slate-600);
|
||||
background-color: var(--color-blue-700);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-slate-700 {
|
||||
.hover\:bg-gray-600 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--color-slate-700);
|
||||
background-color: var(--color-gray-600);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-gray-700 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--color-gray-700);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-green-700 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--color-green-700);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-slate-500 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--color-slate-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:text-gray-300 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
color: var(--color-gray-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
.focus\:border-blue-500 {
|
||||
&:focus {
|
||||
border-color: var(--color-blue-500);
|
||||
}
|
||||
}
|
||||
.focus\:ring-1 {
|
||||
&:focus {
|
||||
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
}
|
||||
.focus\:ring-blue-500 {
|
||||
&:focus {
|
||||
--tw-ring-color: var(--color-blue-500);
|
||||
}
|
||||
}
|
||||
.focus\:outline-none {
|
||||
&:focus {
|
||||
--tw-outline-style: none;
|
||||
outline-style: none;
|
||||
}
|
||||
}
|
||||
.disabled\:cursor-not-allowed {
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
.disabled\:opacity-50 {
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
}
|
||||
}
|
||||
.md\:hidden {
|
||||
@media (width >= 48rem) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@property --tw-rotate-x {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: rotateX(0);
|
||||
}
|
||||
@property --tw-rotate-y {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: rotateY(0);
|
||||
}
|
||||
@property --tw-rotate-z {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: rotateZ(0);
|
||||
}
|
||||
@property --tw-skew-x {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: skewX(0);
|
||||
}
|
||||
@property --tw-skew-y {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: skewY(0);
|
||||
}
|
||||
@property --tw-space-y-reverse {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-border-style {
|
||||
syntax: "*";
|
||||
|
@ -383,10 +584,70 @@
|
|||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-outline-style {
|
||||
@property --tw-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: solid;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@property --tw-shadow-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-shadow-alpha {
|
||||
syntax: "<percentage>";
|
||||
inherits: false;
|
||||
initial-value: 100%;
|
||||
}
|
||||
@property --tw-inset-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@property --tw-inset-shadow-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-inset-shadow-alpha {
|
||||
syntax: "<percentage>";
|
||||
inherits: false;
|
||||
initial-value: 100%;
|
||||
}
|
||||
@property --tw-ring-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-ring-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@property --tw-inset-ring-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-inset-ring-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@property --tw-ring-inset {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-ring-offset-width {
|
||||
syntax: "<length>";
|
||||
inherits: false;
|
||||
initial-value: 0px;
|
||||
}
|
||||
@property --tw-ring-offset-color {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: #fff;
|
||||
}
|
||||
@property --tw-ring-offset-shadow {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0 0 #0000;
|
||||
}
|
||||
@keyframes spin {
|
||||
to {
|
||||
|
@ -396,14 +657,28 @@
|
|||
@layer properties {
|
||||
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
||||
*, ::before, ::after, ::backdrop {
|
||||
--tw-rotate-x: rotateX(0);
|
||||
--tw-rotate-y: rotateY(0);
|
||||
--tw-rotate-z: rotateZ(0);
|
||||
--tw-skew-x: skewX(0);
|
||||
--tw-skew-y: skewY(0);
|
||||
--tw-rotate-x: initial;
|
||||
--tw-rotate-y: initial;
|
||||
--tw-rotate-z: initial;
|
||||
--tw-skew-x: initial;
|
||||
--tw-skew-y: initial;
|
||||
--tw-space-y-reverse: 0;
|
||||
--tw-border-style: solid;
|
||||
--tw-font-weight: initial;
|
||||
--tw-outline-style: solid;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-color: initial;
|
||||
--tw-shadow-alpha: 100%;
|
||||
--tw-inset-shadow: 0 0 #0000;
|
||||
--tw-inset-shadow-color: initial;
|
||||
--tw-inset-shadow-alpha: 100%;
|
||||
--tw-ring-color: initial;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-inset-ring-color: initial;
|
||||
--tw-inset-ring-shadow: 0 0 #0000;
|
||||
--tw-ring-inset: initial;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,148 +9,230 @@
|
|||
<link rel="stylesheet" href="css/style.css">
|
||||
|
||||
<script type="module" src="js/app.js"></script>
|
||||
|
||||
<style>
|
||||
button, .nav-tab {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:active, .nav-tab:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-900 text-white">
|
||||
<header class="bg-gray-800 p-4">
|
||||
<h1 class="text-2xl font-bold">BeeSrv WebUI</h1>
|
||||
</header>
|
||||
<div class="pb-20">
|
||||
<header class="bg-gray-800 p-4">
|
||||
<h1 class="text-2xl font-bold">BeeSrv WebUI</h1>
|
||||
</header>
|
||||
|
||||
<main class="p-4">
|
||||
<p class="mt-2">Made with ❤️ by ihatenodejs</p>
|
||||
<h2 class="text-2xl font-bold my-4">Self-Check</h2>
|
||||
<table class="w-full bg-gray-700 border-collapse border border-gray-600 mb-4">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="border border-gray-600 p-2 text-left">Internet Connection</th>
|
||||
<td class="border border-gray-600 p-2">
|
||||
<button class="bg-slate-500 text-white px-3 py-1 rounded-md hover:bg-slate-600 transition-colors" id="checkConnection">
|
||||
<main class="p-4">
|
||||
<div id="home-tab" class="tab-content">
|
||||
<p class="mt-2">Made with ❤️ by ihatenodejs</p>
|
||||
<h2 class="text-2xl font-bold my-4">Self-Check</h2>
|
||||
<div class="bg-gray-800 rounded-lg p-4 pb-1.5 mb-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold mb-1">Internet Connection</h3>
|
||||
<button class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2" onclick="testConnection()" id="checkConnection">
|
||||
<span id="testBtn" class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-router-icon lucide-router w-4 h-4"><rect width="20" height="8" x="2" y="14" rx="2"/><path d="M6.01 18H6"/><path d="M10.01 18H10"/><path d="M15 10v4"/><path d="M17.84 7.17a4 4 0 0 0-5.66 0"/><path d="M20.66 4.34a8 8 0 0 0-11.31 0"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><rect width="20" height="8" x="2" y="14" rx="2"/><path d="M6.01 18H6"/><path d="M10.01 18H10"/><path d="M15 10v4"/><path d="M17.84 7.17a4 4 0 0 0-5.66 0"/><path d="M20.66 4.34a8 8 0 0 0-11.31 0"/></svg>
|
||||
Test
|
||||
</span>
|
||||
<span id="testBtnLoading" class="hidden items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-icon lucide-loader w-4 h-4 animate-spin"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 animate-spin"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
Testing...
|
||||
</span>
|
||||
</button>
|
||||
<span id="connectionStatus" class="ml-2 text-green-400 hidden items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check w-5 h-5"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<span id="connectionStatus" class="hidden items-center gap-2 text-green-400 mb-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
Connected
|
||||
</span>
|
||||
<span id="connectionError" class="ml-2 text-red-400 hidden items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x w-5 h-5"><path d="M18 6 6 18"/><path d="M6 6l12 12"/></svg>
|
||||
<span id="connectionError" class="hidden items-center gap-2 text-red-400 mb-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><path d="M18 6 6 18"/><path d="M6 6l12 12"/></svg>
|
||||
Error
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="errorBox" class="bg-gray-700 border border-gray-600 p-4 rounded-md mb-4 hidden">
|
||||
<div class="flex items-center gap-2 text-red-400 mb-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-alert-triangle w-5 h-5"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
|
||||
<span class="font-semibold">Error</span>
|
||||
<div id="errorBox" class="bg-red-900/20 border border-red-500/30 p-4 rounded-lg mb-6 hidden">
|
||||
<div class="flex items-center gap-2 text-red-400 mb-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
|
||||
<span class="font-semibold">Error</span>
|
||||
</div>
|
||||
<p id="errorMessage" class="text-gray-300">No error</p>
|
||||
</div>
|
||||
|
||||
<h2 class="text-2xl font-bold mb-4">Module</h2>
|
||||
<div class="space-y-3">
|
||||
<div class="bg-gray-800 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold mb-1">Version</h3>
|
||||
<div class="text-right">
|
||||
<span id="version" class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 animate-spin" id="versionLoader"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
<span id="versionText" class="font-mono">Loading...</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold mb-1">Debug Mode</h3>
|
||||
<div class="text-right">
|
||||
<span id="debug" class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 animate-spin" id="debugLoader"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
<span id="debugText" class="font-mono">Loading...</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p id="errorMessage" class="text-gray-300">No error</p>
|
||||
</div>
|
||||
|
||||
<h2 class="text-2xl font-bold mb-4">Module</h2>
|
||||
<table class="w-full bg-gray-700 border-collapse border border-gray-600">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="border border-gray-600 p-2 text-left">Version</th>
|
||||
<td class="border border-gray-600 p-2">
|
||||
<span id="version" class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-icon lucide-loader w-5 h-5 animate-spin" id="versionLoader"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
<span id="versionText">Loading...</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="border border-gray-600 p-2 text-left">Debug Mode</th>
|
||||
<td class="border border-gray-600 p-2">
|
||||
<span id="debug" class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-icon lucide-loader w-5 h-5 animate-spin" id="debugLoader"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
<span id="debugText">Loading...</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="settings-tab" class="tab-content hidden">
|
||||
<h2 class="text-2xl font-bold mb-6">Configuration</h2>
|
||||
<div id="configErrorBox" class="bg-red-900/20 border border-red-500/30 p-4 rounded-lg mb-6 hidden">
|
||||
<div class="flex items-center gap-2 text-red-400 mb-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
|
||||
<span class="font-semibold">Configuration Error</span>
|
||||
</div>
|
||||
<p id="configErrorMessage" class="text-gray-300">No error</p>
|
||||
</div>
|
||||
|
||||
<h2 class="text-2xl font-bold my-4">Configuration</h2>
|
||||
<table class="w-full bg-gray-700 border-collapse border border-gray-600">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="border border-gray-600 p-2 text-left">Server</th>
|
||||
<td class="border border-gray-600 p-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span id="server" class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-icon lucide-loader w-5 h-5 animate-spin" id="serverLoader"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
<span id="serverText">Loading...</span>
|
||||
</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="text" id="serverInput" class="hidden bg-gray-700 border border-gray-600 rounded-md p-1 text-white w-full sm:w-64" placeholder="Enter server URL">
|
||||
<button id="editServerBtn" class="bg-slate-500 text-white px-3 py-1 rounded-md hover:bg-slate-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pencil w-4 h-4"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
||||
</button>
|
||||
<button id="saveServerBtn" class="hidden bg-slate-500 text-white px-3 py-1 rounded-md hover:bg-slate-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check w-4 h-4"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
</button>
|
||||
<button id="cancelServerBtn" class="hidden bg-gray-600 text-white px-3 py-1 rounded-md hover:bg-gray-700 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x w-4 h-4"><path d="M18 6 6 18"/><path d="M6 6l12 12"/></svg>
|
||||
</button>
|
||||
<div class="space-y-6">
|
||||
<div class="bg-gray-800 rounded-lg p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold mb-1">Server URL</h3>
|
||||
<p class="text-gray-400 text-sm">BeeSrv server endpoint</p>
|
||||
</div>
|
||||
<button onclick="startServerEdit()" id="editServerBtn" class="bg-gray-700 text-white p-2 rounded-lg hover:bg-gray-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<div id="server-display" class="flex items-center justify-between">
|
||||
<span id="server" class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 animate-spin" id="serverLoader"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
<span id="serverText" class="font-mono text-gray-300">Loading...</span>
|
||||
</span>
|
||||
</div>
|
||||
<div id="server-edit" class="hidden space-y-3">
|
||||
<div class="relative">
|
||||
<input type="url" id="serverInput" class="w-full bg-gray-700 border border-gray-600 rounded-lg p-3 text-white placeholder-gray-400 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors" placeholder="https://example.com" autocomplete="url">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 text-gray-400"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button onclick="saveServer()" id="saveServerBtn" class="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
Save
|
||||
</button>
|
||||
<button onclick="cancelServerEdit()" id="cancelServerBtn" class="bg-gray-600 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><path d="M18 6 6 18"/><path d="M6 6l12 12"/></svg>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-xs text-gray-400">
|
||||
Press <kbd class="px-1 py-0.5 bg-gray-600 rounded text-gray-300">Enter</kbd> to save, <kbd class="px-1 py-0.5 bg-gray-600 rounded text-gray-300">Esc</kbd> to cancel
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="border border-gray-600 p-2 text-left">Email</th>
|
||||
<td class="border border-gray-600 p-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span id="email" class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-icon lucide-loader w-5 h-5 animate-spin" id="emailLoader"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
<span id="emailText">Loading...</span>
|
||||
</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="email" id="emailInput" class="hidden bg-gray-700 border border-gray-600 rounded-md p-1 text-white w-full sm:w-64" placeholder="Enter your email">
|
||||
<button id="editEmailBtn" class="bg-slate-500 text-white px-3 py-1 rounded-md hover:bg-slate-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pencil w-4 h-4"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
||||
</button>
|
||||
<button id="saveEmailBtn" class="hidden bg-slate-500 text-white px-3 py-1 rounded-md hover:bg-slate-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check w-4 h-4"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
</button>
|
||||
<button id="cancelEmailBtn" class="hidden bg-gray-600 text-white px-3 py-1 rounded-md hover:bg-gray-700 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x w-4 h-4"><path d="M18 6 6 18"/><path d="M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800 rounded-lg p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold mb-1">Email Address</h3>
|
||||
<button onclick="startEmailEdit()" id="editEmailBtn" class="bg-gray-700 text-white p-2 rounded-lg hover:bg-gray-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<div id="email-display" class="flex items-center justify-between">
|
||||
<span id="email" class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 animate-spin" id="emailLoader"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>
|
||||
<span id="emailText" class="font-mono text-gray-300">Loading...</span>
|
||||
</span>
|
||||
</div>
|
||||
<div id="email-edit" class="hidden space-y-3">
|
||||
<div class="relative">
|
||||
<input type="email" id="emailInput" class="w-full bg-gray-700 border border-gray-600 rounded-lg p-3 text-white placeholder-gray-400 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors" placeholder="user@example.com" autocomplete="email">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 text-gray-400"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-10 5L2 7"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button onclick="saveEmail()" id="saveEmailBtn" class="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
Save
|
||||
</button>
|
||||
<button onclick="cancelEmailEdit()" id="cancelEmailBtn" class="bg-gray-600 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><path d="M18 6 6 18"/><path d="M6 6l12 12"/></svg>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-xs text-gray-400">
|
||||
Press <kbd class="px-1 py-0.5 bg-gray-600 rounded text-gray-300">Enter</kbd> to save, <kbd class="px-1 py-0.5 bg-gray-600 rounded text-gray-300">Esc</kbd> to cancel
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="text-2xl font-bold mt-4">Join Us</h2>
|
||||
<div class="flex flex-wrap gap-2 mt-4">
|
||||
<a href="https://t.me/pontushub">
|
||||
<button class="flex items-center bg-slate-600 text-white px-4 py-2 rounded-md hover:bg-slate-700 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-send-icon lucide-send mr-1 w-5 h-5"><path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/><path d="m21.854 2.147-10.94 10.939"/></svg>
|
||||
Telegram
|
||||
</button>
|
||||
</a>
|
||||
<a href="https://t.me/pontushubchat">
|
||||
<button class="flex items-center bg-slate-600 text-white px-4 py-2 rounded-md hover:bg-slate-700 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle-question-icon lucide-message-circle-question mr-1 w-5 h-5"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>
|
||||
Support
|
||||
</button>
|
||||
</a>
|
||||
<a href="https://donate.stripe.com/cN28yxe1wf5teC4dQS">
|
||||
<button class="flex items-center bg-slate-600 text-white px-4 py-2 rounded-md hover:bg-slate-700 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-hand-coins-icon lucide-hand-coins mr-1 w-5 h-5"><path d="M11 15h2a2 2 0 1 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 17"/><path d="m7 21 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a2 2 0 0 0-2.75-2.91l-4.2 3.9"/><path d="m2 16 6 6"/><circle cx="16" cy="9" r="2.9"/><circle cx="6" cy="5" r="3"/></svg>
|
||||
Donate
|
||||
</button>
|
||||
</a>
|
||||
<div id="support-tab" class="tab-content hidden">
|
||||
<h2 class="text-2xl font-bold mb-6">Support</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="bg-gray-800 rounded-lg p-6">
|
||||
<h3 class="text-lg font-semibold mb-2">Get Help</h3>
|
||||
<p class="text-gray-300 mb-4">Need assistance with BeeSrv? We're here to help!</p>
|
||||
<div class="space-y-3">
|
||||
<a href="https://t.me/pontushubchat" class="flex items-center bg-slate-600 text-white px-4 py-3 rounded-lg hover:bg-slate-500 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-3 w-5 h-5"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>
|
||||
Join Support Chat
|
||||
</a>
|
||||
<a href="https://t.me/pontushub" class="flex items-center bg-slate-600 text-white px-4 py-3 rounded-lg hover:bg-slate-500 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-3 w-5 h-5"><path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/><path d="m21.854 2.147-10.94 10.939"/></svg>
|
||||
Telegram Channel
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800 rounded-lg p-6">
|
||||
<h3 class="text-lg font-semibold mb-2">Support the Project</h3>
|
||||
<p class="text-gray-300 mb-4">Help the developers pay their bills!</p>
|
||||
<a href="https://donate.stripe.com/cN28yxe1wf5teC4dQS" class="flex items-center bg-slate-600 text-white px-4 py-3 rounded-lg hover:bg-slate-500 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-3 w-5 h-5"><path d="M11 15h2a2 2 0 1 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 17"/><path d="m7 21 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a2 2 0 0 0-2.75-2.91l-4.2 3.9"/><path d="m2 16 6 6"/><circle cx="16" cy="9" r="2.9"/><circle cx="6" cy="5" r="3"/></svg>
|
||||
Donate
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<nav class="fixed bottom-0 left-0 right-0 bg-gray-800 border-t border-gray-700 md:hidden">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
<button class="nav-tab flex flex-col items-center justify-center flex-1 h-full text-blue-400 border-t-2 border-blue-400" onclick="switchToTab('home-tab', this)" id="home-nav">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-house-icon lucide-house"><path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"/><path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
|
||||
<span class="text-xs">Home</span>
|
||||
</button>
|
||||
<button class="nav-tab flex flex-col items-center justify-center flex-1 h-full text-gray-400 hover:text-gray-300 transition-colors" onclick="switchToTab('settings-tab', this)" id="settings-nav">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-cog-icon lucide-cog"><path d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z"/><path d="M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z"/><path d="M12 2v2"/><path d="M12 22v-2"/><path d="m17 20.66-1-1.73"/><path d="M11 10.27 7 3.34"/><path d="m20.66 17-1.73-1"/><path d="m3.34 7 1.73 1"/><path d="M14 12h8"/><path d="M2 12h2"/><path d="m20.66 7-1.73 1"/><path d="m3.34 17 1.73-1"/><path d="m17 3.34-1 1.73"/><path d="m11 13.73-4 6.93"/></svg>
|
||||
<span class="text-xs">Settings</span>
|
||||
</button>
|
||||
<button class="nav-tab flex flex-col items-center justify-center flex-1 h-full text-gray-400 hover:text-gray-300 transition-colors" onclick="switchToTab('support-tab', this)" id="support-nav">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle-question-icon lucide-message-circle-question"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>
|
||||
<span class="text-xs">Support</span>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</nav>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
|
||||
<script>eruda.init();</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,6 +3,230 @@ import { exec } from "kernelsu"
|
|||
const modules_dir = "/data/adb/modules/BeeSrv"
|
||||
const persist_dir = "/data/adb/beesrv"
|
||||
|
||||
class ConfigManager {
|
||||
constructor() {
|
||||
this.state = {
|
||||
server: {
|
||||
value: '',
|
||||
isEditing: false,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
isDirty: false
|
||||
},
|
||||
email: {
|
||||
value: '',
|
||||
isEditing: false,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
isDirty: false
|
||||
}
|
||||
}
|
||||
this.validators = {
|
||||
server: (value) => {
|
||||
if (!value.trim()) return 'Server URL cannot be empty'
|
||||
try {
|
||||
new URL(value.trim())
|
||||
return null
|
||||
} catch {
|
||||
return 'Please enter a valid URL'
|
||||
}
|
||||
},
|
||||
email: (value) => {
|
||||
if (!value.trim()) return 'Email cannot be empty'
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
if (!emailRegex.test(value.trim())) return 'Please enter a valid email address'
|
||||
return null
|
||||
}
|
||||
}
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.setupEventListeners()
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
const target = e.target
|
||||
if (target.id === 'serverInput' && this.state.server.isEditing) {
|
||||
e.preventDefault()
|
||||
this.save('server')
|
||||
}
|
||||
if (target.id === 'emailInput' && this.state.email.isEditing) {
|
||||
e.preventDefault()
|
||||
this.save('email')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
updateState(field, updates) {
|
||||
this.state[field] = { ...this.state[field], ...updates }
|
||||
this.renderField(field)
|
||||
}
|
||||
|
||||
startEdit(field) {
|
||||
if (this.state[field].isLoading) return
|
||||
|
||||
hideConfigError()
|
||||
this.updateState(field, {
|
||||
isEditing: true,
|
||||
error: null,
|
||||
isDirty: false
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
const input = document.getElementById(`${field}Input`)
|
||||
if (input) {
|
||||
input.value = this.state[field].value
|
||||
input.focus()
|
||||
input.select()
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
cancelEdit(field) {
|
||||
if (this.state[field].isLoading) return
|
||||
|
||||
this.updateState(field, {
|
||||
isEditing: false,
|
||||
error: null,
|
||||
isDirty: false
|
||||
})
|
||||
}
|
||||
|
||||
async save(field) {
|
||||
const input = document.getElementById(`${field}Input`)
|
||||
if (!input) return
|
||||
|
||||
const newValue = input.value.trim()
|
||||
const validation = this.validators[field](newValue)
|
||||
|
||||
if (validation) {
|
||||
this.updateState(field, { error: validation })
|
||||
showConfigError(validation)
|
||||
return
|
||||
}
|
||||
|
||||
this.updateState(field, {
|
||||
isLoading: true,
|
||||
error: null
|
||||
})
|
||||
|
||||
try {
|
||||
const flag = field === 'server' ? '-s' : '-e'
|
||||
const sanitizedValue = newValue
|
||||
.replace(/['"]/g, '')
|
||||
.replace(/`/g, '')
|
||||
.replace(/\$/g, '')
|
||||
.trim()
|
||||
const { errno, stderr, stdout } = await exec(`${modules_dir}/util/config.sh ${flag} "${sanitizedValue}"`)
|
||||
if (errno !== 0) {
|
||||
const debug = await getDebugMode()
|
||||
const errorMsg = debug ? (stderr || `Command failed with exit code ${errno}`) : `Failed to update ${field} configuration`
|
||||
this.updateState(field, {
|
||||
isLoading: false,
|
||||
error: errorMsg
|
||||
})
|
||||
showConfigError(errorMsg)
|
||||
} else {
|
||||
if (stdout && stdout.includes('Success')) {
|
||||
this.updateState(field, {
|
||||
value: newValue,
|
||||
isEditing: false,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
isDirty: false
|
||||
})
|
||||
hideConfigError()
|
||||
} else {
|
||||
const errorMsg = `Configuration update may have failed - unexpected response`
|
||||
this.updateState(field, {
|
||||
isLoading: false,
|
||||
error: errorMsg
|
||||
})
|
||||
showConfigError(errorMsg)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const debug = await getDebugMode()
|
||||
const errorMsg = debug ? error.toString() : `Error updating ${field} configuration`
|
||||
this.updateState(field, {
|
||||
isLoading: false,
|
||||
error: errorMsg
|
||||
})
|
||||
showConfigError(errorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
renderField(field) {
|
||||
const state = this.state[field]
|
||||
const displayElement = document.getElementById(`${field}-display`)
|
||||
const editElement = document.getElementById(`${field}-edit`)
|
||||
const textElement = document.getElementById(field)
|
||||
const loaderElement = document.getElementById(`${field}Loader`)
|
||||
const inputElement = document.getElementById(`${field}Input`)
|
||||
const saveBtn = document.getElementById(`save${field.charAt(0).toUpperCase() + field.slice(1)}Btn`)
|
||||
|
||||
if (!displayElement || !editElement || !textElement) return
|
||||
|
||||
if (state.isEditing) {
|
||||
displayElement.classList.add('hidden')
|
||||
editElement.classList.remove('hidden')
|
||||
} else {
|
||||
displayElement.classList.remove('hidden')
|
||||
editElement.classList.add('hidden')
|
||||
}
|
||||
|
||||
if (state.isLoading) {
|
||||
loaderElement?.classList.remove('hidden')
|
||||
textElement.textContent = 'Saving...'
|
||||
if (saveBtn) saveBtn.disabled = true
|
||||
} else {
|
||||
loaderElement?.classList.add('hidden')
|
||||
if (saveBtn) saveBtn.disabled = false
|
||||
|
||||
if (state.error) {
|
||||
textElement.textContent = 'Error'
|
||||
} else if (state.value) {
|
||||
textElement.textContent = state.value
|
||||
} else {
|
||||
textElement.textContent = 'Not set'
|
||||
}
|
||||
}
|
||||
|
||||
if (inputElement && state.error) {
|
||||
inputElement.classList.add('border-red-500')
|
||||
inputElement.classList.remove('border-gray-600')
|
||||
} else if (inputElement) {
|
||||
inputElement.classList.remove('border-red-500')
|
||||
inputElement.classList.add('border-gray-600')
|
||||
}
|
||||
}
|
||||
|
||||
setValue(field, value) {
|
||||
this.updateState(field, { value: value || 'Not set' })
|
||||
}
|
||||
|
||||
setError(field, error) {
|
||||
this.updateState(field, {
|
||||
value: 'Error',
|
||||
error: error
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const configManager = new ConfigManager()
|
||||
|
||||
window.startServerEdit = () => configManager.startEdit('server')
|
||||
window.cancelServerEdit = () => configManager.cancelEdit('server')
|
||||
window.saveServer = () => configManager.save('server')
|
||||
|
||||
window.startEmailEdit = () => configManager.startEdit('email')
|
||||
window.cancelEmailEdit = () => configManager.cancelEdit('email')
|
||||
window.saveEmail = () => configManager.save('email')
|
||||
|
||||
function showError(message) {
|
||||
const errorBox = document.getElementById("errorBox")
|
||||
const errorMessage = document.getElementById("errorMessage")
|
||||
|
@ -15,29 +239,117 @@ function hideError() {
|
|||
errorBox.classList.add("hidden")
|
||||
}
|
||||
|
||||
function showConfigError(message) {
|
||||
const errorBox = document.getElementById("configErrorBox")
|
||||
const errorMessage = document.getElementById("configErrorMessage")
|
||||
errorMessage.textContent = message
|
||||
errorBox.classList.remove("hidden")
|
||||
}
|
||||
|
||||
function hideConfigError() {
|
||||
const errorBox = document.getElementById("configErrorBox")
|
||||
errorBox.classList.add("hidden")
|
||||
}
|
||||
|
||||
window.switchToTab = function(tabId, clickedButton) {
|
||||
if (tabId !== 'settings-tab') {
|
||||
hideConfigError()
|
||||
}
|
||||
|
||||
document.getElementById('home-tab').classList.add('hidden')
|
||||
document.getElementById('settings-tab').classList.add('hidden')
|
||||
document.getElementById('support-tab').classList.add('hidden')
|
||||
document.getElementById(tabId).classList.remove('hidden')
|
||||
const navButtons = document.querySelectorAll('.nav-tab')
|
||||
navButtons.forEach(btn => {
|
||||
btn.classList.remove('text-blue-400', 'border-t-2', 'border-blue-400')
|
||||
btn.classList.add('text-gray-400')
|
||||
})
|
||||
clickedButton.classList.remove('text-gray-400')
|
||||
clickedButton.classList.add('text-blue-400', 'border-t-2', 'border-blue-400')
|
||||
}
|
||||
|
||||
window.testConnection = async function() {
|
||||
const testBtn = document.getElementById("testBtn")
|
||||
const testBtnLoading = document.getElementById("testBtnLoading")
|
||||
const connectionStatus = document.getElementById("connectionStatus")
|
||||
const connectionError = document.getElementById("connectionError")
|
||||
const checkConnectionBtn = document.getElementById("checkConnection")
|
||||
|
||||
testBtn.classList.add("hidden")
|
||||
testBtnLoading.classList.remove("hidden")
|
||||
testBtnLoading.classList.add("flex")
|
||||
connectionStatus.classList.add("hidden")
|
||||
connectionStatus.classList.remove("flex")
|
||||
connectionError.classList.add("hidden")
|
||||
connectionError.classList.remove("flex")
|
||||
checkConnectionBtn.disabled = true
|
||||
hideError()
|
||||
|
||||
try {
|
||||
const response = await fetch('https://httpbin.org/get', {
|
||||
method: 'GET',
|
||||
signal: AbortSignal.timeout(5000)
|
||||
});
|
||||
testBtnLoading.classList.add("hidden")
|
||||
testBtnLoading.classList.remove("flex")
|
||||
testBtn.classList.remove("hidden")
|
||||
checkConnectionBtn.disabled = false
|
||||
|
||||
if (response.ok) {
|
||||
connectionStatus.classList.remove("hidden")
|
||||
connectionStatus.classList.add("flex")
|
||||
connectionError.classList.add("hidden")
|
||||
connectionError.classList.remove("flex")
|
||||
checkConnectionBtn.classList.add("hidden")
|
||||
} else {
|
||||
connectionStatus.classList.add("hidden")
|
||||
connectionStatus.classList.remove("flex")
|
||||
connectionError.classList.remove("hidden")
|
||||
connectionError.classList.add("flex")
|
||||
showError("No internet connection detected")
|
||||
checkConnectionBtn.classList.add("hidden")
|
||||
}
|
||||
} catch (error) {
|
||||
testBtnLoading.classList.add("hidden")
|
||||
testBtnLoading.classList.remove("flex")
|
||||
testBtn.classList.remove("hidden")
|
||||
checkConnectionBtn.disabled = false
|
||||
|
||||
connectionStatus.classList.add("hidden")
|
||||
connectionStatus.classList.remove("flex")
|
||||
connectionError.classList.remove("hidden")
|
||||
connectionError.classList.add("flex")
|
||||
|
||||
let errorMessage = "Connection check failed. Please try again later."
|
||||
if (error.name === 'AbortError') {
|
||||
errorMessage = 'Connection check timed out. Please try again.'
|
||||
} else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {
|
||||
errorMessage = 'Unable to reach the internet. Please check your network connection.'
|
||||
}
|
||||
|
||||
showError(errorMessage)
|
||||
checkConnectionBtn.classList.add("hidden")
|
||||
}
|
||||
}
|
||||
|
||||
async function getDebugMode() {
|
||||
const { errno, stdout } = await exec(`cat ${persist_dir}/config.txt`)
|
||||
if (errno !== 0) {
|
||||
showError("Failed to read debug mode")
|
||||
try {
|
||||
const { errno, stdout } = await exec(`${modules_dir}/util/config.sh -d`)
|
||||
if (errno !== 0) {
|
||||
return false
|
||||
}
|
||||
return stdout.trim() === "true"
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
const debug = stdout.split("\n").find(line => line.startsWith("DEBUG="))
|
||||
if (!debug) {
|
||||
return false
|
||||
}
|
||||
return debug.split("=")[1] === "true"
|
||||
}
|
||||
|
||||
async function getEmail() {
|
||||
try {
|
||||
const { errno, stdout, stderr } = await exec(`cat ${persist_dir}/config.txt`)
|
||||
const { errno, stdout } = await exec(`cat ${persist_dir}/config.txt`)
|
||||
if (errno !== 0) {
|
||||
if (await getDebugMode() !== true) {
|
||||
showError("Failed to read email configuration")
|
||||
} else {
|
||||
showError(stderr)
|
||||
}
|
||||
return "Unknown"
|
||||
return "Not set"
|
||||
}
|
||||
const email = stdout.split("\n").find(line => line.startsWith("EMAIL="))
|
||||
if (!email) {
|
||||
|
@ -45,315 +357,70 @@ async function getEmail() {
|
|||
}
|
||||
return email.split("=")[1]
|
||||
} catch (error) {
|
||||
showError("Error reading email configuration")
|
||||
return "Unknown"
|
||||
return "Error"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function getVersion() {
|
||||
try {
|
||||
const { errno, stdout, stderr } = await exec(`cat ${modules_dir}/module.prop`)
|
||||
const { errno, stdout } = await exec(`${modules_dir}/util/config.sh -v`)
|
||||
if (errno !== 0) {
|
||||
if (await getDebugMode() !== true) {
|
||||
showError("Failed to read module version")
|
||||
} else {
|
||||
showError(stderr)
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
const version = stdout.split("\n").find(line => line.startsWith("version="))
|
||||
if (!version) {
|
||||
showError("Module version not found")
|
||||
return "Unknown"
|
||||
}
|
||||
return version.split("=")[1]
|
||||
return stdout.trim() || "Unknown"
|
||||
} catch (error) {
|
||||
showError("Error reading module version")
|
||||
return "Unknown"
|
||||
return "Error"
|
||||
}
|
||||
}
|
||||
|
||||
async function getServer() {
|
||||
try {
|
||||
const { errno, stdout, stderr } = await exec(`cat ${persist_dir}/config.txt`)
|
||||
const { errno, stdout } = await exec(`cat ${persist_dir}/config.txt`)
|
||||
if (errno !== 0) {
|
||||
if (await getDebugMode() !== true) {
|
||||
showError("Failed to read server configuration")
|
||||
} else {
|
||||
showError(stderr)
|
||||
}
|
||||
return "Unknown"
|
||||
return "Not set"
|
||||
}
|
||||
const server = stdout.split("\n").find(line => line.startsWith("SERVER="))
|
||||
if (!server) {
|
||||
showError("Server configuration not found")
|
||||
return "Unknown"
|
||||
return "Not set"
|
||||
}
|
||||
return server.split("=")[1]
|
||||
} catch (error) {
|
||||
showError("Error reading server configuration")
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
async function checkConnection() {
|
||||
try {
|
||||
const response = await fetch('https://httpbin.org/get', {
|
||||
method: 'GET',
|
||||
signal: AbortSignal.timeout(5000)
|
||||
});
|
||||
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') {
|
||||
throw new Error('Connection check timed out. Please try again.');
|
||||
} else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {
|
||||
throw new Error('Unable to reach the internet. Please check your network connection.');
|
||||
} else {
|
||||
throw new Error('Connection check failed. Please try again later.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setEmail(email) {
|
||||
try {
|
||||
const { errno, stderr } = await exec(`${modules_dir}/util/config.sh -e "${email.replace(/['"]/g, '')}"`)
|
||||
if (errno !== 0) {
|
||||
if (await getDebugMode() !== true) {
|
||||
showError("Failed to update email configuration")
|
||||
} else {
|
||||
showError(stderr)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
if (await getDebugMode() !== true) {
|
||||
showError("Error updating email configuration")
|
||||
} else {
|
||||
showError(error)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function setServer(server) {
|
||||
try {
|
||||
const { errno, stderr } = await exec(`${modules_dir}/util/config.sh -s "${server.replace(/['"]/g, '')}"`)
|
||||
if (errno !== 0) {
|
||||
if (await getDebugMode() !== true) {
|
||||
showError("Failed to update server configuration")
|
||||
} else {
|
||||
showError(stderr)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
if (await getDebugMode() !== true) {
|
||||
showError("Error updating server configuration")
|
||||
} else {
|
||||
showError(error)
|
||||
}
|
||||
return false
|
||||
return "Error"
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
const versionText = document.getElementById("versionText")
|
||||
const serverText = document.getElementById("serverText")
|
||||
const emailText = document.getElementById("emailText")
|
||||
const debugText = document.getElementById("debugText")
|
||||
const versionLoader = document.getElementById("versionLoader")
|
||||
const serverLoader = document.getElementById("serverLoader")
|
||||
const emailLoader = document.getElementById("emailLoader")
|
||||
const debugLoader = document.getElementById("debugLoader")
|
||||
const emailInput = document.getElementById("emailInput")
|
||||
const serverInput = document.getElementById("serverInput")
|
||||
const editEmailBtn = document.getElementById("editEmailBtn")
|
||||
const editServerBtn = document.getElementById("editServerBtn")
|
||||
const saveEmailBtn = document.getElementById("saveEmailBtn")
|
||||
const saveServerBtn = document.getElementById("saveServerBtn")
|
||||
const cancelEmailBtn = document.getElementById("cancelEmailBtn")
|
||||
const cancelServerBtn = document.getElementById("cancelServerBtn")
|
||||
const [version, server, email, debug] = await Promise.allSettled([
|
||||
getVersion(),
|
||||
getServer(),
|
||||
getEmail(),
|
||||
getDebugMode()
|
||||
]);
|
||||
|
||||
// Server editing
|
||||
function startServerEditing() {
|
||||
serverText.classList.add("hidden")
|
||||
serverInput.classList.remove("hidden")
|
||||
editServerBtn.classList.add("hidden")
|
||||
saveServerBtn.classList.remove("hidden")
|
||||
cancelServerBtn.classList.remove("hidden")
|
||||
serverInput.value = serverText.textContent
|
||||
serverInput.focus()
|
||||
document.getElementById("versionLoader")?.classList.add("hidden");
|
||||
document.getElementById("serverLoader")?.classList.add("hidden");
|
||||
document.getElementById("emailLoader")?.classList.add("hidden");
|
||||
document.getElementById("debugLoader")?.classList.add("hidden");
|
||||
|
||||
const versionText = document.getElementById("version");
|
||||
if (versionText) {
|
||||
versionText.textContent = version.status === "fulfilled" ? version.value : "Error";
|
||||
}
|
||||
|
||||
function stopServerEditing() {
|
||||
serverText.classList.remove("hidden")
|
||||
serverInput.classList.add("hidden")
|
||||
editServerBtn.classList.remove("hidden")
|
||||
saveServerBtn.classList.add("hidden")
|
||||
cancelServerBtn.classList.add("hidden")
|
||||
const debugText = document.getElementById("debug");
|
||||
if (debugText) {
|
||||
debugText.textContent = debug.status === "fulfilled" ? (debug.value ? "Enabled" : "Disabled") : "Error";
|
||||
}
|
||||
|
||||
editServerBtn.addEventListener("click", startServerEditing)
|
||||
|
||||
cancelServerBtn.addEventListener("click", stopServerEditing)
|
||||
|
||||
saveServerBtn.addEventListener("click", async () => {
|
||||
const newServer = serverInput.value.trim()
|
||||
if (!newServer) {
|
||||
showError("Server URL cannot be empty")
|
||||
return
|
||||
}
|
||||
|
||||
serverLoader.classList.remove("hidden")
|
||||
serverText.textContent = "Saving..."
|
||||
stopServerEditing()
|
||||
|
||||
const success = await setServer(newServer)
|
||||
if (success) {
|
||||
serverText.textContent = newServer
|
||||
} else {
|
||||
serverText.textContent = "Error"
|
||||
}
|
||||
serverLoader.classList.add("hidden")
|
||||
})
|
||||
|
||||
// Handle enter button for server input
|
||||
serverInput.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter") {
|
||||
saveServerBtn.click()
|
||||
} else if (event.key === "Escape") {
|
||||
cancelServerBtn.click()
|
||||
}
|
||||
})
|
||||
|
||||
// Email editing
|
||||
function startEditing() {
|
||||
emailText.classList.add("hidden")
|
||||
emailInput.classList.remove("hidden")
|
||||
editEmailBtn.classList.add("hidden")
|
||||
saveEmailBtn.classList.remove("hidden")
|
||||
cancelEmailBtn.classList.remove("hidden")
|
||||
emailInput.value = emailText.textContent
|
||||
emailInput.focus()
|
||||
if (server.status === "fulfilled") {
|
||||
configManager.setValue('server', server.value);
|
||||
} else {
|
||||
configManager.setError('server', 'Failed to load server configuration');
|
||||
}
|
||||
|
||||
function stopEditing() {
|
||||
emailText.classList.remove("hidden")
|
||||
emailInput.classList.add("hidden")
|
||||
editEmailBtn.classList.remove("hidden")
|
||||
saveEmailBtn.classList.add("hidden")
|
||||
cancelEmailBtn.classList.add("hidden")
|
||||
if (email.status === "fulfilled") {
|
||||
configManager.setValue('email', email.value);
|
||||
} else {
|
||||
configManager.setError('email', 'Failed to load email configuration');
|
||||
}
|
||||
|
||||
editEmailBtn.addEventListener("click", startEditing)
|
||||
|
||||
cancelEmailBtn.addEventListener("click", stopEditing)
|
||||
|
||||
saveEmailBtn.addEventListener("click", async () => {
|
||||
const newEmail = emailInput.value.trim()
|
||||
if (!newEmail) {
|
||||
showError("Email cannot be empty")
|
||||
return
|
||||
}
|
||||
|
||||
emailLoader.classList.remove("hidden")
|
||||
emailText.textContent = "Saving..."
|
||||
stopEditing()
|
||||
|
||||
const success = await setEmail(newEmail)
|
||||
if (success) {
|
||||
emailText.textContent = newEmail
|
||||
} else {
|
||||
emailText.textContent = "Error"
|
||||
}
|
||||
emailLoader.classList.add("hidden")
|
||||
})
|
||||
|
||||
// Handle enter button for email input
|
||||
emailInput.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter") {
|
||||
saveEmailBtn.click()
|
||||
} else if (event.key === "Escape") {
|
||||
cancelEmailBtn.click()
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
const version = await getVersion()
|
||||
const server = await getServer()
|
||||
const email = await getEmail()
|
||||
const debug = await getDebugMode()
|
||||
versionLoader.classList.add("hidden")
|
||||
serverLoader.classList.add("hidden")
|
||||
emailLoader.classList.add("hidden")
|
||||
debugLoader.classList.add("hidden")
|
||||
versionText.textContent = version
|
||||
serverText.textContent = server
|
||||
emailText.textContent = email
|
||||
debugText.textContent = debug ? "Enabled" : "Disabled"
|
||||
} catch (error) {
|
||||
versionLoader.classList.add("hidden")
|
||||
serverLoader.classList.add("hidden")
|
||||
emailLoader.classList.add("hidden")
|
||||
debugLoader.classList.add("hidden")
|
||||
versionText.textContent = "Error"
|
||||
serverText.textContent = "Error"
|
||||
emailText.textContent = "Error"
|
||||
debugText.textContent = "Error"
|
||||
}
|
||||
|
||||
const checkConnectionBtn = document.getElementById("checkConnection")
|
||||
const testBtn = document.getElementById("testBtn")
|
||||
const testBtnLoading = document.getElementById("testBtnLoading")
|
||||
const connectionStatus = document.getElementById("connectionStatus")
|
||||
const connectionError = document.getElementById("connectionError")
|
||||
|
||||
function resetButtonState() {
|
||||
testBtnLoading.classList.add("hidden")
|
||||
testBtn.classList.remove("hidden")
|
||||
checkConnectionBtn.disabled = false
|
||||
}
|
||||
|
||||
function setLoadingState() {
|
||||
testBtn.classList.add("hidden")
|
||||
testBtnLoading.classList.remove("hidden")
|
||||
connectionStatus.classList.add("hidden")
|
||||
connectionError.classList.add("hidden")
|
||||
checkConnectionBtn.disabled = true
|
||||
hideError()
|
||||
}
|
||||
|
||||
function showSuccessState() {
|
||||
connectionStatus.classList.remove("hidden")
|
||||
connectionError.classList.add("hidden")
|
||||
// Hide the button after successful test
|
||||
checkConnectionBtn.classList.add("hidden")
|
||||
}
|
||||
|
||||
function showErrorState(message) {
|
||||
connectionStatus.classList.add("hidden")
|
||||
connectionError.classList.remove("hidden")
|
||||
showError(message)
|
||||
checkConnectionBtn.classList.add("hidden")
|
||||
}
|
||||
|
||||
checkConnectionBtn.addEventListener("click", async () => {
|
||||
setLoadingState()
|
||||
try {
|
||||
const isConnected = await checkConnection()
|
||||
resetButtonState()
|
||||
if (isConnected) {
|
||||
showSuccessState()
|
||||
} else {
|
||||
showErrorState("No internet connection detected")
|
||||
}
|
||||
} catch (error) {
|
||||
resetButtonState()
|
||||
showErrorState(error.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue