mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-09 10:58:06 +00:00
Integrated capacitor, adjusted the fetch urls and UI elements position to fit the safe area on mobile
This commit is contained in:
@@ -20,7 +20,7 @@ export class DarkModeButton extends LitElement {
|
||||
return html`
|
||||
<button
|
||||
title="Toggle Dark Mode"
|
||||
class="absolute top-0 right-0 md:top-[10px] md:right-[10px] border-none bg-none cursor-pointer text-2xl"
|
||||
class="absolute top-0 safe-top right-0 md:top-[10px] md:right-[10px] border-none bg-none cursor-pointer text-2xl"
|
||||
@click=${() => this.toggleDarkMode()}
|
||||
>
|
||||
${this.darkMode ? "☀️" : "🌙"}
|
||||
|
||||
@@ -342,7 +342,7 @@ export class HostLobbyModal extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="start-game-button-container">
|
||||
<div class="start-game-button-container">
|
||||
<button
|
||||
@click=${this.startGame}
|
||||
?disabled=${this.players.length < 2}
|
||||
@@ -471,28 +471,25 @@ export class HostLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
private async putGameConfig() {
|
||||
const config = await getServerConfigFromClient();
|
||||
const response = await fetch(
|
||||
`${window.location.origin}/${config.workerPath(this.lobbyId)}/api/game/${this.lobbyId}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
gameMap: this.selectedMap,
|
||||
difficulty: this.selectedDifficulty,
|
||||
disableNPCs: this.disableNPCs,
|
||||
bots: this.bots,
|
||||
infiniteGold: this.infiniteGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
gameMode: this.gameMode,
|
||||
disabledUnits: this.disabledUnits,
|
||||
playerTeams: this.teamCount,
|
||||
} satisfies Partial<GameConfig>),
|
||||
const url = await buildGameUrl(this.lobbyId, "game");
|
||||
const response = await fetch(url, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
gameMap: this.selectedMap,
|
||||
difficulty: this.selectedDifficulty,
|
||||
disableNPCs: this.disableNPCs,
|
||||
bots: this.bots,
|
||||
infiniteGold: this.infiniteGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
gameMode: this.gameMode,
|
||||
disabledUnits: this.disabledUnits,
|
||||
playerTeams: this.teamCount,
|
||||
} satisfies Partial<GameConfig>),
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -521,16 +518,13 @@ export class HostLobbyModal extends LitElement {
|
||||
`Starting private game with map: ${GameMapType[this.selectedMap]} ${this.useRandomMap ? " (Randomly selected)" : ""}`,
|
||||
);
|
||||
this.close();
|
||||
const config = await getServerConfigFromClient();
|
||||
const response = await fetch(
|
||||
`${window.location.origin}/${config.workerPath(this.lobbyId)}/api/start_game/${this.lobbyId}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
const url = await buildGameUrl(this.lobbyId, "start_game");
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
);
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -550,8 +544,8 @@ export class HostLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
private async pollPlayers() {
|
||||
const config = await getServerConfigFromClient();
|
||||
fetch(`/${config.workerPath(this.lobbyId)}/api/game/${this.lobbyId}`, {
|
||||
const url = await buildGameUrl(this.lobbyId, "game");
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -566,19 +560,16 @@ export class HostLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
async function createLobby(): Promise<GameInfo> {
|
||||
const config = await getServerConfigFromClient();
|
||||
try {
|
||||
const id = generateID();
|
||||
const response = await fetch(
|
||||
`/${config.workerPath(id)}/api/create_game/${id}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
// body: JSON.stringify(data), // Include this if you need to send data
|
||||
const url = await buildGameUrl(id, "create_game");
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
);
|
||||
// body: JSON.stringify(data), // Include this if you need to send data
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
@@ -593,3 +584,14 @@ async function createLobby(): Promise<GameInfo> {
|
||||
throw error; // Re-throw the error so the caller can handle it
|
||||
}
|
||||
}
|
||||
|
||||
export async function buildGameUrl(
|
||||
gameID: string,
|
||||
path: string,
|
||||
): Promise<string> {
|
||||
const config = await getServerConfigFromClient();
|
||||
|
||||
const apiPath = `/api/${path}/${gameID}`;
|
||||
const baseUrl = process.env.API_BASE_URL || "/";
|
||||
return `${baseUrl}${config.workerPath(gameID)}${apiPath}`;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ import { customElement, query, state } from "lit/decorators.js";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { GameInfo, GameRecord } from "../core/Schemas";
|
||||
import { generateID } from "../core/Util";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
import "./components/baseComponents/Button";
|
||||
import "./components/baseComponents/Modal";
|
||||
import { buildGameUrl } from "./HostLobbyModal";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
|
||||
@customElement("join-private-lobby-modal")
|
||||
export class JoinPrivateLobbyModal extends LitElement {
|
||||
@query("o-modal") private modalEl!: HTMLElement & {
|
||||
@@ -170,8 +171,7 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
private async checkActiveLobby(lobbyId: string): Promise<boolean> {
|
||||
const config = await getServerConfigFromClient();
|
||||
const url = `/${config.workerPath(lobbyId)}/api/game/${lobbyId}/exists`;
|
||||
const url = await buildGameUrl(lobbyId, "game/exists");
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
@@ -203,10 +203,9 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
private async checkArchivedGame(lobbyId: string): Promise<boolean> {
|
||||
const config = await getServerConfigFromClient();
|
||||
const archiveUrl = `/${config.workerPath(lobbyId)}/api/archived_game/${lobbyId}`;
|
||||
const url = await buildGameUrl(lobbyId, "archived_game");
|
||||
|
||||
const archiveResponse = await fetch(archiveUrl, {
|
||||
const archiveResponse = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
@@ -249,17 +248,14 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
|
||||
private async pollPlayers() {
|
||||
if (!this.lobbyIdInput?.value) return;
|
||||
const config = await getServerConfigFromClient();
|
||||
|
||||
fetch(
|
||||
`/${config.workerPath(this.lobbyIdInput.value)}/api/game/${this.lobbyIdInput.value}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
const url = await buildGameUrl(this.lobbyIdInput.value, "game");
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
)
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data: GameInfo) => {
|
||||
this.players = data.clients?.map((p) => p.username) ?? [];
|
||||
|
||||
@@ -56,7 +56,9 @@ export class PublicLobby extends LitElement {
|
||||
|
||||
async fetchLobbies(): Promise<GameInfo[]> {
|
||||
try {
|
||||
const response = await fetch(`/api/public_lobbies`);
|
||||
const response = await fetch(
|
||||
`${process.env.API_BASE_URL}/api/public_lobbies`,
|
||||
);
|
||||
if (!response.ok)
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
const data = await response.json();
|
||||
|
||||
@@ -204,7 +204,7 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
</style>
|
||||
<div
|
||||
class="${this._isVisible
|
||||
? "w-[320px] text-sm lg:text-m bg-gray-800/70 p-2 pr-3 lg:p-4 shadow-lg lg:rounded-lg backdrop-blur"
|
||||
? "w-[320px] text-sm lg:text-m bg-gray-800/70 p-2 pr-3 lg:p-4 shadow-lg lg:rounded-lg backdrop-blur safe-pb"
|
||||
: "hidden"}"
|
||||
@contextmenu=${(e) => e.preventDefault()}
|
||||
>
|
||||
|
||||
@@ -153,7 +153,7 @@ export class OptionsMenu extends LitElement implements Layer {
|
||||
}
|
||||
return html`
|
||||
<div
|
||||
class="top-0 lg:top-4 right-0 lg:right-4 z-50 pointer-events-auto"
|
||||
class="top-0 lg:top-4 right-0 lg:right-4 z-50 pointer-events-auto safe-pt"
|
||||
@contextmenu=${(e) => e.preventDefault()}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -54,7 +54,7 @@ export class TopBar extends LitElement implements Layer {
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="fixed top-0 z-50 bg-slate-800/40 backdrop-blur-sm shadow-xs text-white text-sm p-1 rounded-ee-sm lg:rounded grid grid-cols-1 sm:grid-cols-2 w-1/2 sm:w-2/3 md:w-1/2 lg:hidden"
|
||||
class="fixed top-0 safe-top z-50 bg-slate-800/40 backdrop-blur-sm shadow-xs text-white text-sm p-1 rounded-ee-sm lg:rounded grid grid-cols-1 sm:grid-cols-2 w-1/2 sm:w-2/3 md:w-1/2 lg:hidden"
|
||||
>
|
||||
<!-- Pop section (takes 2 columns on desktop) -->
|
||||
<div
|
||||
|
||||
+12
-9
@@ -2,7 +2,10 @@
|
||||
<html lang="en" class="h-full">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
||||
/>
|
||||
<title>OpenFront (ALPHA)</title>
|
||||
<link rel="manifest" href="../../resources/manifest.json" />
|
||||
|
||||
@@ -97,7 +100,7 @@
|
||||
<body
|
||||
class="h-full select-none font-sans min-h-screen bg-opacity-0 bg-cover bg-center bg-fixed transition-opacity duration-300 ease-in-out flex flex-col"
|
||||
>
|
||||
<header class="l-header">
|
||||
<header class="l-header safe-pt">
|
||||
<div class="l-header__content">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -209,11 +212,11 @@
|
||||
secondary
|
||||
></o-button>
|
||||
<!-- <o-button
|
||||
id="chat-button"
|
||||
title="Chat Test"
|
||||
block
|
||||
secondary
|
||||
></o-button> -->
|
||||
id="chat-button"
|
||||
title="Chat Test"
|
||||
block
|
||||
secondary
|
||||
></o-button> -->
|
||||
</div>
|
||||
|
||||
<o-button
|
||||
@@ -240,7 +243,7 @@
|
||||
<button
|
||||
id="settings-button"
|
||||
title="Settings"
|
||||
class="fixed bottom-4 right-4 z-50 rounded-full p-2 shadow-lg transition-colors duration-300 flex items-center justify-center"
|
||||
class="fixed bottom-4 right-4 z-50 rounded-full p-2 shadow-lg transition-colors duration-300 flex items-center justify-center safe-mb"
|
||||
style="width: 80px; height: 80px; background-color: #0075ff"
|
||||
>
|
||||
<img
|
||||
@@ -287,7 +290,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Footer section -->
|
||||
<footer class="l-footer">
|
||||
<footer class="l-footer safe-pb">
|
||||
<div class="l-footer__content">
|
||||
<div class="l-footer__col">
|
||||
<a
|
||||
|
||||
@@ -17,6 +17,22 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Add safe-area offsets for iOS native mobile device */
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
.safe-pt {
|
||||
padding-top: env(safe-area-inset-top);
|
||||
}
|
||||
.safe-pb {
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.safe-top {
|
||||
top: env(safe-area-inset-top);
|
||||
}
|
||||
.safe-mb {
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add custom scrollbar styles */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
|
||||
Reference in New Issue
Block a user