mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-08-20 04:27:29 +00:00
feat: remove more buttons from android
This commit is contained in:
parent
af1897a333
commit
0f44a3761c
|
|
@ -144,13 +144,37 @@ const CapePreview = memo(function CapePreview({
|
|||
render();
|
||||
}
|
||||
};
|
||||
const onTouchStart = (e: TouchEvent) => {
|
||||
const t = e.touches[0];
|
||||
if (!t) return;
|
||||
isDragging = true;
|
||||
previousMousePosition = { x: t.clientX, y: t.clientY };
|
||||
};
|
||||
const onTouchMove = (e: TouchEvent) => {
|
||||
const t = e.touches[0];
|
||||
if (isDragging && groupRef.current && t) {
|
||||
e.preventDefault();
|
||||
groupRef.current.rotation.y +=
|
||||
(t.clientX - previousMousePosition.x) * 0.01;
|
||||
previousMousePosition = { x: t.clientX, y: t.clientY };
|
||||
render();
|
||||
}
|
||||
};
|
||||
const onTouchEnd = () => {
|
||||
isDragging = false;
|
||||
};
|
||||
renderer.domElement.addEventListener("mousedown", onMouseDown);
|
||||
window.addEventListener("mousemove", onMouseMove);
|
||||
window.addEventListener("mouseup", onMouseUp);
|
||||
renderer.domElement.addEventListener("touchstart", onTouchStart, { passive: true });
|
||||
window.addEventListener("touchmove", onTouchMove, { passive: false });
|
||||
window.addEventListener("touchend", onTouchEnd);
|
||||
return () => {
|
||||
active = false;
|
||||
window.removeEventListener("mousemove", onMouseMove);
|
||||
window.removeEventListener("mouseup", onMouseUp);
|
||||
window.removeEventListener("touchmove", onTouchMove);
|
||||
window.removeEventListener("touchend", onTouchEnd);
|
||||
scene.traverse((object) => {
|
||||
if (object instanceof THREE.Mesh) {
|
||||
if (object.geometry) object.geometry.dispose();
|
||||
|
|
|
|||
|
|
@ -404,15 +404,39 @@ const SkinViewer = memo(function SkinViewer({
|
|||
requestRenderRef.current?.();
|
||||
}
|
||||
};
|
||||
const onTouchStart = (e: TouchEvent) => {
|
||||
const t = e.touches[0];
|
||||
if (!t) return;
|
||||
isDragging = true;
|
||||
previousMousePosition = { x: t.clientX, y: t.clientY };
|
||||
};
|
||||
const onTouchMove = (e: TouchEvent) => {
|
||||
const t = e.touches[0];
|
||||
if (isDragging && t) {
|
||||
e.preventDefault();
|
||||
const delta = (t.clientX - previousMousePosition.x) * 0.01;
|
||||
playerGroup.rotation.y += delta;
|
||||
previousMousePosition = { x: t.clientX, y: t.clientY };
|
||||
requestRenderRef.current?.();
|
||||
}
|
||||
};
|
||||
const onTouchEnd = () => {
|
||||
isDragging = false;
|
||||
};
|
||||
|
||||
requestRenderRef.current = () => renderer.render(scene, camera);
|
||||
requestRenderRef.current();
|
||||
renderer.domElement.addEventListener("mousedown", onMouseDown);
|
||||
window.addEventListener("mousemove", onMouseMove);
|
||||
window.addEventListener("mouseup", onMouseUp);
|
||||
renderer.domElement.addEventListener("touchstart", onTouchStart, { passive: true });
|
||||
window.addEventListener("touchmove", onTouchMove, { passive: false });
|
||||
window.addEventListener("touchend", onTouchEnd);
|
||||
return () => {
|
||||
window.removeEventListener("mousemove", onMouseMove);
|
||||
window.removeEventListener("mouseup", onMouseUp);
|
||||
window.removeEventListener("touchmove", onTouchMove);
|
||||
window.removeEventListener("touchend", onTouchEnd);
|
||||
scene.traverse((object) => {
|
||||
if (object instanceof THREE.Mesh) {
|
||||
if (object.geometry) object.geometry.dispose();
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ const SettingsView = memo(function SettingsView() {
|
|||
runnerDownloadProgress,
|
||||
downloadRunner,
|
||||
} = useGame();
|
||||
const { isLinux, isMac } = usePlatform();
|
||||
const { isLinux, isMac, isAndroid } = usePlatform();
|
||||
const [focusIndex, setFocusIndex] = useState<number | null>(null);
|
||||
const [currentSubMenu, setCurrentSubMenu] = useState<
|
||||
"main" | "audio" | "video" | "launcher" | "game" | "plugins"
|
||||
|
|
@ -430,12 +430,14 @@ const SettingsView = memo(function SettingsView() {
|
|||
type: "button",
|
||||
onClick: handleFullscreenToggle,
|
||||
});
|
||||
items.push({
|
||||
id: "rpc",
|
||||
label: `Discord RPC: ${rpcEnabled ? "ON" : "OFF"}`,
|
||||
type: "button",
|
||||
onClick: handleRpcToggle,
|
||||
});
|
||||
if (!isAndroid) {
|
||||
items.push({
|
||||
id: "rpc",
|
||||
label: `Discord RPC: ${rpcEnabled ? "ON" : "OFF"}`,
|
||||
type: "button",
|
||||
onClick: handleRpcToggle,
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
id: "skip_intro",
|
||||
label: `Skip Intro: ${skipIntro ? "ON" : "OFF"}`,
|
||||
|
|
@ -448,7 +450,7 @@ const SettingsView = memo(function SettingsView() {
|
|||
type: "button",
|
||||
onClick: handleLegacyToggle,
|
||||
});
|
||||
if (isLinux) {
|
||||
if (isLinux && !isAndroid) {
|
||||
items.push({
|
||||
id: "runner",
|
||||
label: `Runner: ${selectedRunnerName}`,
|
||||
|
|
@ -479,33 +481,37 @@ const SettingsView = memo(function SettingsView() {
|
|||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
id: "export_settings",
|
||||
label: "Export Settings",
|
||||
type: "button",
|
||||
onClick: async () => {
|
||||
playPressSound();
|
||||
try {
|
||||
await TauriService.exportSettings();
|
||||
} catch (e) {
|
||||
if (e !== "CANCELED") console.error(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
items.push({
|
||||
id: "import_settings",
|
||||
label: "Import Settings",
|
||||
type: "button",
|
||||
onClick: async () => {
|
||||
playPressSound();
|
||||
try {
|
||||
await TauriService.importSettings();
|
||||
window.location.reload();
|
||||
} catch (e) {
|
||||
if (e !== "CANCELED") console.error(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
if (!isAndroid) {
|
||||
items.push({
|
||||
id: "export_settings",
|
||||
label: "Export Settings",
|
||||
type: "button",
|
||||
onClick: async () => {
|
||||
playPressSound();
|
||||
try {
|
||||
await TauriService.exportSettings();
|
||||
} catch (e) {
|
||||
if (e !== "CANCELED") console.error(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
if (!isAndroid) {
|
||||
items.push({
|
||||
id: "import_settings",
|
||||
label: "Import Settings",
|
||||
type: "button",
|
||||
onClick: async () => {
|
||||
playPressSound();
|
||||
try {
|
||||
await TauriService.importSettings();
|
||||
window.location.reload();
|
||||
} catch (e) {
|
||||
if (e !== "CANCELED") console.error(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
id: "reset_setup",
|
||||
label: "Reset Setup",
|
||||
|
|
@ -553,6 +559,7 @@ const SettingsView = memo(function SettingsView() {
|
|||
animationsEnabled,
|
||||
layout,
|
||||
isLinux,
|
||||
isAndroid,
|
||||
mangohudEnabled,
|
||||
selectedRunnerName,
|
||||
isRunnerDownloading,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ interface SetupViewProps {
|
|||
}
|
||||
|
||||
const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
||||
const { isLinux, isMac } = usePlatform();
|
||||
const { isLinux, isMac, isAndroid } = usePlatform();
|
||||
const {
|
||||
username, setUsername,
|
||||
setHasCompletedSetup,
|
||||
|
|
@ -82,7 +82,7 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
|||
setCurrentStep(2);
|
||||
setFocusIndex(0);
|
||||
} else if (currentStep === 2) {
|
||||
setConfigRpc(enableDiscordRPC);
|
||||
if (!isAndroid) setConfigRpc(enableDiscordRPC);
|
||||
setCurrentStep(3);
|
||||
setFocusIndex(0);
|
||||
} else if (currentStep === 3) {
|
||||
|
|
@ -108,7 +108,7 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
|||
if (isLinux) count = runners.length + 2;
|
||||
else if (isMac) count = 3;
|
||||
else count = 2;
|
||||
} else if (currentStep === 2) count = 4;
|
||||
} else if (currentStep === 2) count = isAndroid ? 3 : 4;
|
||||
else if (currentStep === 3) count = 2;
|
||||
if (e.key === "ArrowDown" || e.key === "Tab") {
|
||||
e.preventDefault();
|
||||
|
|
@ -134,10 +134,16 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
|||
else if (focusIndex === 1) handleNext();
|
||||
}
|
||||
} else if (currentStep === 2) {
|
||||
if (focusIndex === 0) { setEnableDiscordRPC(!enableDiscordRPC); playPressSound(); }
|
||||
else if (focusIndex === 1) { playPressSound(); }
|
||||
else if (focusIndex === 2) handleBack();
|
||||
else if (focusIndex === 3) handleNext();
|
||||
if (isAndroid) {
|
||||
if (focusIndex === 0) { playPressSound(); }
|
||||
else if (focusIndex === 1) handleBack();
|
||||
else if (focusIndex === 2) handleNext();
|
||||
} else {
|
||||
if (focusIndex === 0) { setEnableDiscordRPC(!enableDiscordRPC); playPressSound(); }
|
||||
else if (focusIndex === 1) { playPressSound(); }
|
||||
else if (focusIndex === 2) handleBack();
|
||||
else if (focusIndex === 3) handleNext();
|
||||
}
|
||||
} else if (currentStep === 3) {
|
||||
if (focusIndex === 0) handleBack();
|
||||
else if (focusIndex === 1) handleNext();
|
||||
|
|
@ -146,7 +152,7 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
|||
};
|
||||
window.addEventListener("keydown", handleKey);
|
||||
return () => window.removeEventListener("keydown", handleKey);
|
||||
}, [currentStep, focusIndex, runners, enableDiscordRPC, isLinux, isMac, tempUsername]);
|
||||
}, [currentStep, focusIndex, runners, enableDiscordRPC, isLinux, isMac, isAndroid, tempUsername]);
|
||||
|
||||
const handleMacosSetup = async () => {
|
||||
playPressSound();
|
||||
|
|
@ -412,44 +418,46 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
|||
<div
|
||||
className="p-5 flex flex-col gap-2 mc-options-bg"
|
||||
>
|
||||
<button
|
||||
onClick={() => { playPressSound(); setEnableDiscordRPC(!enableDiscordRPC); }}
|
||||
onMouseEnter={() => setFocusIndex(0)}
|
||||
className={`w-full h-10 flex items-center justify-between px-4 transition-all outline-none border-none rounded
|
||||
${focusIndex === 0 ? "bg-gray-200" : "bg-transparent"} hover:bg-gray-200`}
|
||||
>
|
||||
<span className={`tracking-widest uppercase text-lg ${focusIndex === 0 ? "text-[#FFFF55]" : "text-gray-800"}`}>
|
||||
Discord RPC
|
||||
</span>
|
||||
<div className="relative w-6 h-6 shrink-0">
|
||||
<img
|
||||
src={focusIndex === 0 ? "/images/checkbox_highlighted.png" : "/images/checkbox.png"}
|
||||
alt="checkbox"
|
||||
className="w-full h-full object-contain"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
{enableDiscordRPC && (
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={() => { playPressSound(); setEnableDiscordRPC(!enableDiscordRPC); }}
|
||||
onMouseEnter={() => setFocusIndex(0)}
|
||||
className={`w-full h-10 flex items-center justify-between px-4 transition-all outline-none border-none rounded
|
||||
${focusIndex === 0 ? "bg-gray-200" : "bg-transparent"} hover:bg-gray-200`}
|
||||
>
|
||||
<span className={`tracking-widest uppercase text-lg ${focusIndex === 0 ? "text-[#FFFF55]" : "text-gray-800"}`}>
|
||||
Discord RPC
|
||||
</span>
|
||||
<div className="relative w-6 h-6 shrink-0">
|
||||
<img
|
||||
src="/images/check.png"
|
||||
alt="checked"
|
||||
className="absolute inset-0 w-full h-full object-contain"
|
||||
src={focusIndex === 0 ? "/images/checkbox_highlighted.png" : "/images/checkbox.png"}
|
||||
alt="checkbox"
|
||||
className="w-full h-full object-contain"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
{enableDiscordRPC && (
|
||||
<img
|
||||
src="/images/check.png"
|
||||
alt="checked"
|
||||
className="absolute inset-0 w-full h-full object-contain"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => { playPressSound(); }}
|
||||
onMouseEnter={() => setFocusIndex(1)}
|
||||
onMouseEnter={() => setFocusIndex(isAndroid ? 0 : 1)}
|
||||
className={`w-full h-10 flex items-center justify-between px-4 transition-all outline-none border-none rounded
|
||||
${focusIndex === 1 ? "bg-gray-200" : "bg-transparent"} hover:bg-gray-200`}
|
||||
${focusIndex === (isAndroid ? 0 : 1) ? "bg-gray-200" : "bg-transparent"} hover:bg-gray-200`}
|
||||
>
|
||||
<span className={`tracking-widest uppercase text-lg ${focusIndex === 1 ? "text-[#FFFF55]" : "text-gray-800"}`}>
|
||||
<span className={`tracking-widest uppercase text-lg ${focusIndex === (isAndroid ? 0 : 1) ? "text-[#FFFF55]" : "text-gray-800"}`}>
|
||||
Animations
|
||||
</span>
|
||||
<div className="relative w-6 h-6 shrink-0">
|
||||
<img
|
||||
src={focusIndex === 1 ? "/images/checkbox_highlighted.png" : "/images/checkbox.png"}
|
||||
src={focusIndex === (isAndroid ? 0 : 1) ? "/images/checkbox_highlighted.png" : "/images/checkbox.png"}
|
||||
alt="checkbox"
|
||||
className="w-full h-full object-contain"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
|
|
@ -513,25 +521,27 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between px-4 h-10">
|
||||
<span className="text-gray-600 text-sm uppercase tracking-widest">Discord RPC</span>
|
||||
<div className="relative w-5 h-5">
|
||||
<img
|
||||
src="/images/checkbox.png"
|
||||
alt="checkbox"
|
||||
className="w-full h-full object-contain"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
{enableDiscordRPC && (
|
||||
{!isAndroid && (
|
||||
<div className="flex items-center justify-between px-4 h-10">
|
||||
<span className="text-gray-600 text-sm uppercase tracking-widest">Discord RPC</span>
|
||||
<div className="relative w-5 h-5">
|
||||
<img
|
||||
src="/images/check.png"
|
||||
alt="checked"
|
||||
className="absolute inset-0 w-full h-full object-contain"
|
||||
src="/images/checkbox.png"
|
||||
alt="checkbox"
|
||||
className="w-full h-full object-contain"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
)}
|
||||
{enableDiscordRPC && (
|
||||
<img
|
||||
src="/images/check.png"
|
||||
alt="checked"
|
||||
className="absolute inset-0 w-full h-full object-contain"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -543,16 +553,16 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
|||
onClick={handleBack}
|
||||
onMouseEnter={() => {
|
||||
if (currentStep === 3) setFocusIndex(0);
|
||||
else if (currentStep === 2) setFocusIndex(2);
|
||||
else if (currentStep === 2) setFocusIndex(isAndroid ? 1 : 2);
|
||||
else if (currentStep === 1) setFocusIndex(isLinux ? runners.length : (isMac ? 1 : 0));
|
||||
}}
|
||||
className={`w-36 h-10 flex items-center justify-center transition-colors mc-text-shadow outline-none border-none
|
||||
${(currentStep === 3 && focusIndex === 0) || (currentStep === 2 && focusIndex === 2) ||
|
||||
${(currentStep === 3 && focusIndex === 0) || (currentStep === 2 && focusIndex === (isAndroid ? 1 : 2)) ||
|
||||
(currentStep === 1 && ((isLinux && focusIndex === runners.length) || (isMac && focusIndex === 1) || (!isLinux && !isMac && focusIndex === 0)))
|
||||
? "text-[#FFFF55]" : "text-white"}`}
|
||||
style={navBtnStyle(
|
||||
(currentStep === 3 && focusIndex === 0) ||
|
||||
(currentStep === 2 && focusIndex === 2) ||
|
||||
(currentStep === 2 && focusIndex === (isAndroid ? 1 : 2)) ||
|
||||
(currentStep === 1 && ((isLinux && focusIndex === runners.length) || (isMac && focusIndex === 1) || (!isLinux && !isMac && focusIndex === 0)))
|
||||
)}
|
||||
>
|
||||
|
|
@ -567,7 +577,7 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
|||
onMouseEnter={() => {
|
||||
if (currentStep === 0) setFocusIndex(1);
|
||||
else if (currentStep === 1) setFocusIndex(isLinux ? runners.length + 1 : (isMac ? 2 : 1));
|
||||
else if (currentStep === 2) setFocusIndex(3);
|
||||
else if (currentStep === 2) setFocusIndex(isAndroid ? 2 : 3);
|
||||
else if (currentStep === 3) setFocusIndex(1);
|
||||
}}
|
||||
disabled={!canProceed()}
|
||||
|
|
@ -575,13 +585,13 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
|
|||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
${(currentStep === 0 && focusIndex === 1) ||
|
||||
(currentStep === 1 && ((isLinux && focusIndex === runners.length + 1) || (isMac && focusIndex === 2) || (!isLinux && !isMac && focusIndex === 1))) ||
|
||||
(currentStep === 2 && focusIndex === 3) ||
|
||||
(currentStep === 2 && focusIndex === (isAndroid ? 2 : 3)) ||
|
||||
(currentStep === 3 && focusIndex === 1)
|
||||
? "text-[#FFFF55]" : "text-white"}`}
|
||||
style={navBtnStyle(
|
||||
(currentStep === 0 && focusIndex === 1) ||
|
||||
(currentStep === 1 && ((isLinux && focusIndex === runners.length + 1) || (isMac && focusIndex === 2) || (!isLinux && !isMac && focusIndex === 1))) ||
|
||||
(currentStep === 2 && focusIndex === 3) ||
|
||||
(currentStep === 2 && focusIndex === (isAndroid ? 2 : 3)) ||
|
||||
(currentStep === 3 && focusIndex === 1)
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
useGame,
|
||||
} from "../../context/LauncherContext";
|
||||
import { ScreenshotImage } from "../common/ScreenshotImage";
|
||||
import { usePlatform } from "../../hooks/usePlatform";
|
||||
import type { Edition } from "../../types/edition";
|
||||
interface DeleteConfirmButtonProps {
|
||||
label: string;
|
||||
|
|
@ -84,6 +85,7 @@ const VersionsView = memo(function VersionsView() {
|
|||
saveCustomPath,
|
||||
} = useGame();
|
||||
const { isDayTime } = useConfig();
|
||||
const { isAndroid } = usePlatform();
|
||||
const [focusIndex, setFocusIndex] = useState<number>(0);
|
||||
const [focusBtn, setFocusBtn] = useState<number>(0);
|
||||
const [isImportModalOpen, setIsImportModalOpen] = useState(false);
|
||||
|
|
@ -510,7 +512,7 @@ const VersionsView = memo(function VersionsView() {
|
|||
Update Available!
|
||||
</button>
|
||||
)}
|
||||
{!isInstalled && (
|
||||
{!isAndroid && !isInstalled && (
|
||||
<button
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
|
|
@ -605,157 +607,169 @@ const VersionsView = memo(function VersionsView() {
|
|||
</button>
|
||||
)}
|
||||
<div className="h-[1px] bg-white/5 my-0.5 mx-1" />
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
TauriService.openInstanceFolder(edition.instanceId);
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<img
|
||||
src="/images/Folder_Icon.png"
|
||||
alt=""
|
||||
className="w-3.5 h-3.5 object-contain"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
Open Folder
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
const PANORAMA_PROFILES = [
|
||||
"legacy_evolved",
|
||||
"vanilla_tu19",
|
||||
"360revived",
|
||||
"vanilla_tu24",
|
||||
];
|
||||
const panoId = PANORAMA_PROFILES.includes(
|
||||
edition.id,
|
||||
)
|
||||
? edition.id
|
||||
: "vanilla_tu19";
|
||||
const panoramaUrl = `/panorama/${panoId}_Panorama_Background_${isDayTime ? "Day" : "Night"}.png`;
|
||||
addToSteam(
|
||||
edition.instanceId,
|
||||
edition.name,
|
||||
edition.titleImage ?? "",
|
||||
panoramaUrl,
|
||||
);
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<img
|
||||
src="/images/steam.png"
|
||||
alt=""
|
||||
className="w-3.5 h-3.5 object-contain invert brightness-0"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
Add to Steam
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
handleImportWorld(edition.instanceId);
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
className="w-3.5 h-3.5"
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
TauriService.openInstanceFolder(edition.instanceId);
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||
<polyline points="7 10 12 15 17 10" />
|
||||
<line x1="12" y1="15" x2="12" y2="3" />
|
||||
</svg>
|
||||
Import World
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
TauriService.backupInstance(edition.instanceId).catch((err) => {
|
||||
if (err !== "CANCELED") console.error(err);
|
||||
});
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
className="w-3.5 h-3.5"
|
||||
<img
|
||||
src="/images/Folder_Icon.png"
|
||||
alt=""
|
||||
className="w-3.5 h-3.5 object-contain"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
Open Folder
|
||||
</button>
|
||||
)}
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
const PANORAMA_PROFILES = [
|
||||
"legacy_evolved",
|
||||
"vanilla_tu19",
|
||||
"360revived",
|
||||
"vanilla_tu24",
|
||||
];
|
||||
const panoId = PANORAMA_PROFILES.includes(
|
||||
edition.id,
|
||||
)
|
||||
? edition.id
|
||||
: "vanilla_tu19";
|
||||
const panoramaUrl = `/panorama/${panoId}_Panorama_Background_${isDayTime ? "Day" : "Night"}.png`;
|
||||
addToSteam(
|
||||
edition.instanceId,
|
||||
edition.name,
|
||||
edition.titleImage ?? "",
|
||||
panoramaUrl,
|
||||
);
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
|
||||
<polyline points="17 21 17 13 7 13 7 21" />
|
||||
<polyline points="7 3 7 8 15 8" />
|
||||
</svg>
|
||||
Backup
|
||||
</button>
|
||||
<button
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
setOpenMenuId(null);
|
||||
try {
|
||||
await TauriService.restoreInstance();
|
||||
} catch (err) {
|
||||
if (err !== "CANCELED") console.error(err);
|
||||
}
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
className="w-3.5 h-3.5"
|
||||
<img
|
||||
src="/images/steam.png"
|
||||
alt=""
|
||||
className="w-3.5 h-3.5 object-contain invert brightness-0"
|
||||
style={{ imageRendering: "pixelated" }}
|
||||
/>
|
||||
Add to Steam
|
||||
</button>
|
||||
)}
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
handleImportWorld(edition.instanceId);
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<path d="M21 12a9 9 0 1 1-9-9 9 9 0 0 1 9 9z" />
|
||||
<polyline points="12 7 12 12 15 15" />
|
||||
</svg>
|
||||
Restore
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
setCustomizeTarget(edition);
|
||||
setIsCustomizeModalOpen(true);
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
className="w-3.5 h-3.5"
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
className="w-3.5 h-3.5"
|
||||
>
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||
<polyline points="7 10 12 15 17 10" />
|
||||
<line x1="12" y1="15" x2="12" y2="3" />
|
||||
</svg>
|
||||
Import World
|
||||
</button>
|
||||
)}
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
TauriService.backupInstance(edition.instanceId).catch((err) => {
|
||||
if (err !== "CANCELED") console.error(err);
|
||||
});
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<path d="M12 20h9M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" />
|
||||
</svg>
|
||||
Customize
|
||||
</button>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
className="w-3.5 h-3.5"
|
||||
>
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
|
||||
<polyline points="17 21 17 13 7 13 7 21" />
|
||||
<polyline points="7 3 7 8 15 8" />
|
||||
</svg>
|
||||
Backup
|
||||
</button>
|
||||
)}
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
setOpenMenuId(null);
|
||||
try {
|
||||
await TauriService.restoreInstance();
|
||||
} catch (err) {
|
||||
if (err !== "CANCELED") console.error(err);
|
||||
}
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
className="w-3.5 h-3.5"
|
||||
>
|
||||
<path d="M21 12a9 9 0 1 1-9-9 9 9 0 0 1 9 9z" />
|
||||
<polyline points="12 7 12 12 15 15" />
|
||||
</svg>
|
||||
Restore
|
||||
</button>
|
||||
)}
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playPressSound();
|
||||
setCustomizeTarget(edition);
|
||||
setIsCustomizeModalOpen(true);
|
||||
setOpenMenuId(null);
|
||||
}}
|
||||
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
className="w-3.5 h-3.5"
|
||||
>
|
||||
<path d="M12 20h9M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" />
|
||||
</svg>
|
||||
Customize
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ export function usePlatform() {
|
|||
if (typeof window === 'undefined') return { isLinux: false, isMac: false, isWindows: false, isAndroid: false };
|
||||
const ua = window.navigator.userAgent.toLowerCase();
|
||||
const plat = window.navigator.platform.toLowerCase();
|
||||
const isLinux = plat.includes('linux') || ua.includes('linux');
|
||||
const isMac = plat.includes('mac') || ua.includes('mac');
|
||||
const isWindows = plat.includes('win') || ua.includes('win');
|
||||
const isAndroid = ua.includes('android');
|
||||
const isLinux = !isAndroid && (plat.includes('linux') || ua.includes('linux'));
|
||||
const isMac = !isAndroid && (plat.includes('mac') || ua.includes('mac'));
|
||||
const isWindows = !isAndroid && (plat.includes('win') || ua.includes('win'));
|
||||
return { isLinux, isMac, isWindows, isAndroid };
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue