Launcher 0.3.3: Freunde-Eingabe bleibt, Lobby eines Freundes beitreten
Das Feld Benutzername wird beim Aktualisieren nicht mehr neu angelegt (Text, Fokus und Cursor bleiben), neu gezeichnet wird nur bei Aenderungen. Beitreten-Knopf bei Freunden mit Lobby und Liste der Freundes-Lobbys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<RootNamespace>BSO.Launcher</RootNamespace>
|
<RootNamespace>BSO.Launcher</RootNamespace>
|
||||||
<AssemblyName>BSO-Launcher</AssemblyName>
|
<AssemblyName>BSO-Launcher</AssemblyName>
|
||||||
<Version>0.3.2</Version>
|
<Version>0.3.3</Version>
|
||||||
<Company>MBM.wtf</Company>
|
<Company>MBM.wtf</Company>
|
||||||
<Product>BSO Launcher</Product>
|
<Product>BSO Launcher</Product>
|
||||||
<Description>BSO Launcher - Bus Simulator Online</Description>
|
<Description>BSO Launcher - Bus Simulator Online</Description>
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
Neue Version: hier einen Abschnitt `## <Version>` anlegen, `<Version>` in `BSO.Launcher.csproj` erhöhen, dann
|
Neue Version: hier einen Abschnitt `## <Version>` anlegen, `<Version>` in `BSO.Launcher.csproj` erhöhen, dann
|
||||||
`dotnet publish -c Release -o publish` – lädt die Version hoch; der Abschnitt wird der Changelog auf der Website und im Launcher.
|
`dotnet publish -c Release -o publish` – lädt die Version hoch; der Abschnitt wird der Changelog auf der Website und im Launcher.
|
||||||
|
|
||||||
|
## 0.3.3
|
||||||
|
- Freunde: Was man ins Feld „Benutzername“ tippt, verschwindet nicht mehr nach ein paar Sekunden
|
||||||
|
- Lobby eines Freundes beitreten, auch ohne Einladung („Beitreten“ beim Freund und unter „Lobbys deiner Freunde“)
|
||||||
|
|
||||||
## 0.3.2
|
## 0.3.2
|
||||||
- Auto-Update wie beim MSCO Launcher: ein neuer Launcher installiert sich beim Start sofort selbst und startet neu,
|
- Auto-Update wie beim MSCO Launcher: ein neuer Launcher installiert sich beim Start sofort selbst und startet neu,
|
||||||
neue BSO-Versionen kommen beim Start (beides in den Einstellungen abschaltbar)
|
neue BSO-Versionen kommen beim Start (beides in den Einstellungen abschaltbar)
|
||||||
|
|||||||
@@ -275,7 +275,11 @@ let socialTick = 0;
|
|||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
if ($("app").classList.contains("hidden")) return;
|
if ($("app").classList.contains("hidden")) return;
|
||||||
if (page !== "friends" && socialTick++ % 5 !== 0) return;
|
if (page !== "friends" && socialTick++ % 5 !== 0) return;
|
||||||
try { await loadSocial(); if (page === "friends") drawSocial(); } catch { }
|
const before = JSON.stringify(social);
|
||||||
|
try {
|
||||||
|
await loadSocial();
|
||||||
|
if (page === "friends" && JSON.stringify(social) !== before) drawSocial(); // nur neu zeichnen, wenn sich was geaendert hat
|
||||||
|
} catch { }
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
async function renderFriends(root) {
|
async function renderFriends(root) {
|
||||||
@@ -297,14 +301,34 @@ function action(label, cls, cmd, args, done) {
|
|||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eingabefeld "Freund hinzufuegen" bleibt ueber das Neuzeichnen hinweg dasselbe - sonst waere Getipptes weg
|
||||||
|
const friendName = h("input", { maxlength: "32", spellcheck: "false" });
|
||||||
|
|
||||||
function drawSocial() {
|
function drawSocial() {
|
||||||
const root = $("socialRoot");
|
const root = $("socialRoot");
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
|
const typing = document.activeElement === friendName;
|
||||||
|
const [start, end] = [friendName.selectionStart, friendName.selectionEnd];
|
||||||
root.replaceChildren(lobbyCard(), friendsCard());
|
root.replaceChildren(lobbyCard(), friendsCard());
|
||||||
|
if (typing) { friendName.focus(); friendName.setSelectionRange(start, end); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Der Lobby eines Freundes beitreten - auch ohne Einladung. Wer selbst eine Lobby erstellt hat, loest sie dabei auf. */
|
||||||
|
function joinButton(fl) {
|
||||||
|
const b = h("button", { class: "btn small primary", type: "button" }, t("Beitreten"));
|
||||||
|
b.disabled = fl.full;
|
||||||
|
if (fl.full) b.title = t("Die Lobby ist voll");
|
||||||
|
b.addEventListener("click", () => {
|
||||||
|
const mine = social.lobby.lobby;
|
||||||
|
if (mine && mine.isOwner && !confirm(t("Deine Lobby wird dabei aufgelöst. Trotzdem {0} beitreten?", fl.owner))) return;
|
||||||
|
busy(b, async () => { await call("lobbyJoin", { lobbyId: fl.lobbyId }); await loadSocial(); drawSocial(); });
|
||||||
|
});
|
||||||
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
function lobbyCard() {
|
function lobbyCard() {
|
||||||
const { lobby, invites } = social.lobby;
|
const { lobby, invites } = social.lobby;
|
||||||
|
const friendLobbies = social.lobby.friendLobbies || [];
|
||||||
const card = h("div", { class: "card" });
|
const card = h("div", { class: "card" });
|
||||||
for (const i of invites)
|
for (const i of invites)
|
||||||
card.append(h("div", { class: "invite" }, h("span", {}, h("b", {}, i.owner), t(" lädt dich in die Lobby ein")),
|
card.append(h("div", { class: "invite" }, h("span", {}, h("b", {}, i.owner), t(" lädt dich in die Lobby ein")),
|
||||||
@@ -314,6 +338,13 @@ function lobbyCard() {
|
|||||||
card.append(h("h3", {}, t("Lobby")),
|
card.append(h("h3", {}, t("Lobby")),
|
||||||
h("p", { class: "muted small" }, t("Erstelle eine Lobby und lade Freunde ein. Der Server läuft auf dem BSO-Relay – niemand muss etwas hosten oder Ports freigeben.")),
|
h("p", { class: "muted small" }, t("Erstelle eine Lobby und lade Freunde ein. Der Server läuft auf dem BSO-Relay – niemand muss etwas hosten oder Ports freigeben.")),
|
||||||
h("div", { class: "row" }, action(t("Lobby erstellen"), "primary", "lobbyCreate")));
|
h("div", { class: "row" }, action(t("Lobby erstellen"), "primary", "lobbyCreate")));
|
||||||
|
if (friendLobbies.length)
|
||||||
|
card.append(h("div", { class: "section-title" }, t("Lobbys deiner Freunde")),
|
||||||
|
h("div", { class: "people" }, ...friendLobbies.map((fl) => h("div", { class: "person" },
|
||||||
|
h("span", { class: "dot " + (fl.serverOnline ? "ok" : "") }),
|
||||||
|
h("span", { class: "name" }, t("Lobby von {0}", fl.owner)),
|
||||||
|
h("span", { class: "muted small" }, t("{0} Spieler", fl.members)),
|
||||||
|
joinButton(fl)))));
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +381,8 @@ function lobbyCard() {
|
|||||||
function friendsCard() {
|
function friendsCard() {
|
||||||
const { friends, incoming, outgoing } = social.friends;
|
const { friends, incoming, outgoing } = social.friends;
|
||||||
const lobby = social.lobby.lobby;
|
const lobby = social.lobby.lobby;
|
||||||
const name = h("input", { placeholder: t("Benutzername"), maxlength: "32", spellcheck: "false" });
|
const name = friendName;
|
||||||
|
name.placeholder = t("Benutzername");
|
||||||
const add = h("button", { class: "btn small primary", type: "button" }, t("Hinzufügen"));
|
const add = h("button", { class: "btn small primary", type: "button" }, t("Hinzufügen"));
|
||||||
const send = () => busy(add, async () => {
|
const send = () => busy(add, async () => {
|
||||||
if (!name.value.trim()) return;
|
if (!name.value.trim()) return;
|
||||||
@@ -361,7 +393,7 @@ function friendsCard() {
|
|||||||
drawSocial();
|
drawSocial();
|
||||||
});
|
});
|
||||||
add.addEventListener("click", send);
|
add.addEventListener("click", send);
|
||||||
name.addEventListener("keydown", (e) => { if (e.key === "Enter") send(); });
|
name.onkeydown = (e) => { if (e.key === "Enter") send(); }; // onkeydown statt addEventListener: das Feld bleibt, der Knopf ist neu
|
||||||
|
|
||||||
const card = h("div", { class: "card" }, h("h3", {}, t("Freunde")), h("div", { class: "row" }, name, add));
|
const card = h("div", { class: "card" }, h("h3", {}, t("Freunde")), h("div", { class: "row" }, name, add));
|
||||||
if (incoming.length)
|
if (incoming.length)
|
||||||
@@ -373,12 +405,14 @@ function friendsCard() {
|
|||||||
card.append(h("div", { class: "people" }, ...friends.map((f) => {
|
card.append(h("div", { class: "people" }, ...friends.map((f) => {
|
||||||
const inLobby = lobby && lobby.members.some((m) => m.accountId === f.accountId);
|
const inLobby = lobby && lobby.members.some((m) => m.accountId === f.accountId);
|
||||||
const invited = lobby && lobby.invited.includes(f.accountId);
|
const invited = lobby && lobby.invited.includes(f.accountId);
|
||||||
|
const theirLobby = (social.lobby.friendLobbies || []).find((fl) => fl.ownerId === f.accountId);
|
||||||
const remove = h("button", { class: "x", type: "button", title: t("Freund entfernen") }, "");
|
const remove = h("button", { class: "x", type: "button", title: t("Freund entfernen") }, "");
|
||||||
remove.addEventListener("click", () => { if (confirm(t("{0} als Freund entfernen?", f.username))) busy(remove, async () => { await call("friendRemove", { id: f.id }); await loadSocial(); drawSocial(); }); });
|
remove.addEventListener("click", () => { if (confirm(t("{0} als Freund entfernen?", f.username))) busy(remove, async () => { await call("friendRemove", { id: f.id }); await loadSocial(); drawSocial(); }); });
|
||||||
return h("div", { class: "person" },
|
return h("div", { class: "person" },
|
||||||
h("span", { class: "dot " + (f.online ? "ok" : "") }), h("span", { class: "name" }, f.username),
|
h("span", { class: "dot " + (f.online ? "ok" : "") }), h("span", { class: "name" }, f.username),
|
||||||
lobby && lobby.isOwner && !inLobby ? (invited ? h("span", { class: "muted small" }, t("eingeladen")) : action(t("Einladen"), "", "lobbyInvite", { accountId: f.accountId })) : null,
|
lobby && lobby.isOwner && !inLobby ? (invited ? h("span", { class: "muted small" }, t("eingeladen")) : action(t("Einladen"), "", "lobbyInvite", { accountId: f.accountId })) : null,
|
||||||
inLobby ? h("span", { class: "muted small" }, t("in der Lobby")) : null,
|
inLobby ? h("span", { class: "muted small" }, t("in der Lobby")) : null,
|
||||||
|
!inLobby && theirLobby ? joinButton(theirLobby) : null,
|
||||||
remove);
|
remove);
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ const EN = {
|
|||||||
"Server läuft – alle können SPIELEN drücken": "Server is running – everyone can press PLAY",
|
"Server läuft – alle können SPIELEN drücken": "Server is running – everyone can press PLAY",
|
||||||
"Drück SPIELEN – der Server startet dann für die ganze Lobby": "Press PLAY – the server then starts for the whole lobby",
|
"Drück SPIELEN – der Server startet dann für die ganze Lobby": "Press PLAY – the server then starts for the whole lobby",
|
||||||
"Warte, bis {0} SPIELEN drückt – dann startet der Server": "Waiting for {0} to press PLAY – then the server starts", "Server stoppen": "Stop server",
|
"Warte, bis {0} SPIELEN drückt – dann startet der Server": "Waiting for {0} to press PLAY – then the server starts", "Server stoppen": "Stop server",
|
||||||
|
"Lobbys deiner Freunde": "Your friends' lobbies", "{0} Spieler": "{0} players", "Die Lobby ist voll": "The lobby is full",
|
||||||
|
"Deine Lobby wird dabei aufgelöst. Trotzdem {0} beitreten?": "Your lobby will be closed. Join {0} anyway?",
|
||||||
"Ersteller": "Owner", "Entfernen": "Remove", "Deine Lobby": "Your lobby", "Lobby von {0}": "{0}'s lobby", "Mitglieder ({0})": "Members ({0})",
|
"Ersteller": "Owner", "Entfernen": "Remove", "Deine Lobby": "Your lobby", "Lobby von {0}": "{0}'s lobby", "Mitglieder ({0})": "Members ({0})",
|
||||||
"{0} Einladung(en) offen": "{0} invitation(s) pending", "Lobby auflösen": "Close lobby", "Lobby verlassen": "Leave lobby",
|
"{0} Einladung(en) offen": "{0} invitation(s) pending", "Lobby auflösen": "Close lobby", "Lobby verlassen": "Leave lobby",
|
||||||
"Hinzufügen": "Add", "Anfrage an {0} geschickt": "Request sent to {0}", "Anfragen": "Requests", "Annehmen": "Accept",
|
"Hinzufügen": "Add", "Anfrage an {0} geschickt": "Request sent to {0}", "Anfragen": "Requests", "Annehmen": "Accept",
|
||||||
|
|||||||
Reference in New Issue
Block a user