diff --git a/src/components/views/LceOnlineView.tsx b/src/components/views/LceOnlineView.tsx index 27d2e32..a8d64e6 100644 --- a/src/components/views/LceOnlineView.tsx +++ b/src/components/views/LceOnlineView.tsx @@ -17,19 +17,20 @@ const LceOnlineView = memo(function LceOnlineView({ const { playPressSound, playBackSound } = useAudio(); const game = useGame(); const [isSignedIn, setIsSignedIn] = useState(lceOnlineService.signedIn); - const [currentTab, setCurrentTab] = useState<"friends" | "requests">( + const [currentTab, setCurrentTab] = useState<"friends" | "requests" | "invites">( "friends", ); const [focusIndex, setFocusIndex] = useState(0); const [friends, setFriends] = useState([]); const [incomingReqs, setIncomingReqs] = useState([]); const [outgoingReqs, setOutgoingReqs] = useState([]); + const [invites, setInvites] = useState>([]); const [isHosting, setIsHosting] = useState(false); const [isAddingFriend, setIsAddingFriend] = useState(false); const [addFriendUsername, setAddFriendUsername] = useState(""); const addFriendInputRef = useRef(null); const [errorModal, setErrorModal] = useState(null); - const [joinTarget, setJoinTarget] = useState(null); + const [joinTarget, setJoinTarget] = useState<{ sessionId: string; hostName: string } | null>(null); const [authMode, setAuthMode] = useState<"login" | "register">("login"); const [authUsername, setAuthUsername] = useState(""); const [authPassword, setAuthPassword] = useState(""); @@ -48,6 +49,12 @@ const LceOnlineView = memo(function LceOnlineView({ } catch (e: unknown) { console.error(e); } + try { + const invitesData = await lceOnlineService.getInvites(); + setInvites(invitesData); + } catch { + setInvites([]); + } }; useEffect(() => { @@ -120,7 +127,7 @@ const LceOnlineView = memo(function LceOnlineView({ type MenuItem = { id: string; - type: "button" | "friend" | "request_in" | "request_out"; + type: "button" | "friend" | "request_in" | "request_out" | "invite"; label: string; onClick: () => void; onClickSecondary?: () => void; @@ -166,10 +173,9 @@ const LceOnlineView = memo(function LceOnlineView({ type: "friend", label: f, onClick: () => handleAction(() => lceOnlineService.removeFriend(f)), - onClickSecondary: () => { - playPressSound(); - setJoinTarget(f); - }, + onClickSecondary: isHosting + ? () => handleAction(() => lceOnlineService.sendInvite(f)) + : undefined, }); }); } else if (currentTab === "requests") { @@ -193,6 +199,21 @@ const LceOnlineView = memo(function LceOnlineView({ handleAction(() => lceOnlineService.declineFriendRequest(r)), }); }); + } else if (currentTab === "invites") { + invites.forEach((inv) => { + items.push({ + id: `invite_${inv.from}`, + type: "invite", + label: inv.from, + onClick: () => + handleAction(async () => { + const sessionId = await lceOnlineService.acceptInvite(inv.from); + setJoinTarget({ sessionId, hostName: inv.from }); + }), + onClickSecondary: () => + handleAction(() => lceOnlineService.declineInvite(inv.from)), + }); + }); } return items; @@ -201,11 +222,12 @@ const LceOnlineView = memo(function LceOnlineView({ friends, incomingReqs, outgoingReqs, + invites, playPressSound, isHosting, ]); - const tabs: ("friends" | "requests")[] = ["friends", "requests"]; + const tabs: ("friends" | "requests" | "invites")[] = ["friends", "requests", "invites"]; useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (errorModal) { @@ -455,21 +477,23 @@ const LceOnlineView = memo(function LceOnlineView({
{item.type === "friend" && ( <> - + {item.onClickSecondary && ( + + )} + + + )} {item.type === "request_in" && ( <>
))} @@ -717,12 +779,12 @@ const LceOnlineView = memo(function LceOnlineView({ editions={game.editions} installs={game.installs} invite={{ - inviteId: joinTarget, - from: joinTarget, + inviteId: joinTarget.sessionId, + from: joinTarget.hostName, hostIp: "", hostPort: 0, - hostName: joinTarget, - signalingSessionId: joinTarget, + hostName: joinTarget.hostName, + signalingSessionId: joinTarget.sessionId, status: "pending", }} /> diff --git a/src/services/LceOnlineService.ts b/src/services/LceOnlineService.ts index 9298cae..ef9595b 100644 --- a/src/services/LceOnlineService.ts +++ b/src/services/LceOnlineService.ts @@ -210,6 +210,39 @@ export class LceOnlineService { throw new Error(res); } } + + async sendInvite(target: string): Promise { + const res = await this.request("POST", "/invite", target); + if (typeof res === "string" && res !== "Invite Sent") { + throw new Error(res); + } + } + + async acceptInvite(from: string): Promise { + const res = await this.request("POST", "/acceptinvite", from); + if (typeof res !== "string") throw new Error("Failed to accept invite"); + return res; + } + + async declineInvite(from: string): Promise { + try { + await this.request("POST", "/declineinvite", from); + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : ""; + if (msg !== "Declined Invite") throw e; + } + } + + async getInvites(): Promise> { + const res = await this.request("GET", "/getinvites", null); + if (typeof res !== "string") return []; + try { + const parsed = JSON.parse(res); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } + } } export const lceOnlineService = new LceOnlineService();