mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-03 14:40:48 +00:00
can creater & join lobby
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
import {LitElement, html, css} from 'lit';
|
||||
import {customElement, property, state} from 'lit/decorators.js';
|
||||
import {GameMap} from '../core/game/Game';
|
||||
import {Lobby} from '../core/Schemas';
|
||||
|
||||
@customElement('host-lobby-modal')
|
||||
export class HostLobbyModal extends LitElement {
|
||||
@state() private isModalOpen = false;
|
||||
@state() private selectedMap: GameMap = GameMap.World;
|
||||
@state() private lobbyId = 'a345d';
|
||||
@state() private copySuccess = false;
|
||||
|
||||
static styles = css`
|
||||
.modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: white;
|
||||
margin: 15% auto;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s;
|
||||
display: inline-block;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 8px;
|
||||
font-size: 16px;
|
||||
margin-top: 10px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.lobby-id-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.clipboard-icon {
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.clipboard-icon:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.copy-success {
|
||||
color: green;
|
||||
font-size: 14px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
`;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="modal-overlay" style="display: ${this.isModalOpen ? 'block' : 'none'}">
|
||||
<div class="modal-content">
|
||||
<span class="close" @click=${this.close}>×</span>
|
||||
<h2>Private Lobby</h2>
|
||||
<div class="lobby-id-container">
|
||||
<h3>Lobby ID: ${this.lobbyId}</h3>
|
||||
<svg @click=${this.copyToClipboard} class="clipboard-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path>
|
||||
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect>
|
||||
</svg>
|
||||
</div>
|
||||
${this.copySuccess ? html`<p class="copy-success">Copied to clipboard!</p>` : ''}
|
||||
<div>
|
||||
<label for="map-select">Map: </label>
|
||||
<select id="map-select" @change=${this.handleMapChange}>
|
||||
${Object.entries(GameMap)
|
||||
.filter(([key]) => isNaN(Number(key)))
|
||||
.map(([key, value]) => html`
|
||||
<option value=${value} ?selected=${this.selectedMap === value}>
|
||||
${key}
|
||||
</option>
|
||||
`)}
|
||||
</select>
|
||||
</div>
|
||||
<button @click=${this.startGame}>Start Game</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public open() {
|
||||
createLobby().then((lobby) => {
|
||||
this.lobbyId = lobby.id
|
||||
// join lobby
|
||||
}).then(() => {
|
||||
this.dispatchEvent(new CustomEvent('join-lobby', {
|
||||
detail: {
|
||||
singlePlayer: false,
|
||||
lobby: {
|
||||
id: this.lobbyId,
|
||||
},
|
||||
map: this.selectedMap,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
|
||||
})
|
||||
this.isModalOpen = true;
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.isModalOpen = false;
|
||||
this.copySuccess = false;
|
||||
}
|
||||
|
||||
private handleMapChange(e: Event) {
|
||||
this.selectedMap = Number((e.target as HTMLSelectElement).value) as GameMap;
|
||||
}
|
||||
private async startGame() {
|
||||
console.log(`Starting single player game with map: ${GameMap[this.selectedMap]}`);
|
||||
this.close();
|
||||
const response = await fetch(`/start_private_lobby/${this.lobbyId}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async copyToClipboard() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(this.lobbyId);
|
||||
this.copySuccess = true;
|
||||
setTimeout(() => {
|
||||
this.copySuccess = false;
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy text: ', err);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function createLobby(): Promise<Lobby> {
|
||||
try {
|
||||
const response = await fetch('/private_lobby', {
|
||||
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}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Success:', data);
|
||||
|
||||
// Assuming the server returns an object with an 'id' property
|
||||
const lobby: Lobby = {
|
||||
id: data.id,
|
||||
// Add other properties as needed
|
||||
};
|
||||
|
||||
return lobby;
|
||||
} catch (error) {
|
||||
console.error('Error creating lobby:', error);
|
||||
throw error; // Re-throw the error so the caller can handle it
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import {LitElement, html, css} from 'lit';
|
||||
import {customElement, property, state, query} from 'lit/decorators.js';
|
||||
import {GameMap} from '../core/game/Game';
|
||||
|
||||
@customElement('join-private-lobby-modal')
|
||||
export class JoinPrivateLobbyModal extends LitElement {
|
||||
@state() private isModalOpen = false;
|
||||
@query('#lobbyIdInput') private lobbyIdInput!: HTMLInputElement;
|
||||
|
||||
static styles = css`
|
||||
.modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.modal-content {
|
||||
background-color: white;
|
||||
margin: 15% auto;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
text-align: center;
|
||||
}
|
||||
.close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
.lobby-id-container {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.lobby-id-container input {
|
||||
flex-grow: 1;
|
||||
max-width: 200px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.lobby-id-container button {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
.join-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="modal-overlay" style="display: ${this.isModalOpen ? 'block' : 'none'}">
|
||||
<div class="modal-content">
|
||||
<span class="close" @click=${this.close}>×</span>
|
||||
<h2>Join Private Lobby</h2>
|
||||
<div class="lobby-id-container">
|
||||
<input type="text" id="lobbyIdInput" placeholder="Enter Lobby ID">
|
||||
<button @click=${this.pasteFromClipboard}>Paste</button>
|
||||
</div>
|
||||
<button class="join-button" @click=${this.joinLobby}>Join Lobby</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public open() {
|
||||
this.isModalOpen = true;
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.isModalOpen = false;
|
||||
}
|
||||
|
||||
private async pasteFromClipboard() {
|
||||
try {
|
||||
const clipText = await navigator.clipboard.readText();
|
||||
this.lobbyIdInput.value = clipText;
|
||||
} catch (err) {
|
||||
console.error('Failed to read clipboard contents: ', err);
|
||||
}
|
||||
}
|
||||
|
||||
private joinLobby() {
|
||||
const lobbyId = this.lobbyIdInput.value;
|
||||
// Add your logic here to join the lobby using the lobbyId
|
||||
console.log(`Joining lobby with ID: ${lobbyId}`);
|
||||
this.dispatchEvent(new CustomEvent('join-lobby', {
|
||||
detail: {
|
||||
lobby: {id: lobbyId},
|
||||
singlePlayer: false,
|
||||
map: GameMap.World,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}))
|
||||
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
+19
-11
@@ -4,15 +4,13 @@ import favicon from '../../resources/images/Favicon.png';
|
||||
|
||||
import './PublicLobby';
|
||||
import './UsernameInput';
|
||||
|
||||
|
||||
import './styles.css';
|
||||
import {UsernameInput} from "./UsernameInput";
|
||||
import {SinglePlayerModal} from "./SinglePlayerModal";
|
||||
import {GameMap} from "../core/game/Game";
|
||||
import {HostLobbyModal as HostPrivateLobbyModal} from "./HostLobbyModal";
|
||||
import {JoinPrivateLobbyModal} from "./JoinPrivateLobbyModal";
|
||||
|
||||
|
||||
const usernameKey: string = 'username';
|
||||
|
||||
|
||||
class Client {
|
||||
@@ -39,14 +37,24 @@ class Client {
|
||||
document.addEventListener('single-player', this.handleSinglePlayer.bind(this));
|
||||
|
||||
|
||||
const singlePlayerButton = document.getElementById('single-player');
|
||||
const modal = document.querySelector('single-player-modal') as SinglePlayerModal;
|
||||
const spModal = document.querySelector('single-player-modal') as SinglePlayerModal;
|
||||
spModal instanceof SinglePlayerModal
|
||||
document.getElementById('single-player').addEventListener('click', () => {
|
||||
spModal.open();
|
||||
})
|
||||
|
||||
const hostModal = document.querySelector('host-lobby-modal') as HostPrivateLobbyModal;
|
||||
hostModal instanceof HostPrivateLobbyModal
|
||||
document.getElementById('host-lobby-button').addEventListener('click', () => {
|
||||
hostModal.open();
|
||||
})
|
||||
|
||||
const joinModal = document.querySelector('join-private-lobby-modal') as JoinPrivateLobbyModal;
|
||||
joinModal instanceof JoinPrivateLobbyModal
|
||||
document.getElementById('join-private-lobby-button').addEventListener('click', () => {
|
||||
joinModal.open();
|
||||
})
|
||||
|
||||
if (singlePlayerButton && modal instanceof SinglePlayerModal) {
|
||||
singlePlayerButton.addEventListener('click', () => {
|
||||
modal.open();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -126,4 +126,4 @@ export class PublicLobby extends LitElement {
|
||||
this.currLobby = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@
|
||||
|
||||
<!-- Create and Join Lobby buttons stacked -->
|
||||
<div class="flex-1 space-y-4">
|
||||
<button id="create-lobby"
|
||||
<button id="host-lobby-button"
|
||||
class="w-full h-12 px-4 py-4 text-sm font-medium text-blue-700 bg-blue-100 rounded-md hover:bg-blue-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition duration-300 ease-in-out">
|
||||
Create Lobby
|
||||
</button>
|
||||
<button id="join-lobby"
|
||||
<button id="join-private-lobby-button"
|
||||
class="w-full h-12 px-4 py-4 text-sm font-medium text-blue-700 bg-blue-100 rounded-md hover:bg-blue-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition duration-300 ease-in-out">
|
||||
Join Lobby
|
||||
</button>
|
||||
@@ -68,6 +68,8 @@
|
||||
<div id="radialMenu" class="radial-menu"></div>
|
||||
|
||||
<single-player-modal></single-player-modal>
|
||||
<host-lobby-modal></host-lobby-modal>
|
||||
<join-private-lobby-modal></join-private-lobby-modal>
|
||||
<emoji-table></emoji-table>
|
||||
<leader-board></leader-board>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user