[UserList] Context menu invite (#7138)

* [Client] Send game invites from the user context menu via a private message

The user context menu gains an "Invite to Game" submenu listing the
inviteable games in the room (the inviter's own games, honoring the
buddy-only setting). Picking one opens a private message to the target
user with a cockatrice://joingame link naming the game, so the target
gets a clickable invite instead of a raw URL. Multi-game rooms offer a
picker; a single inviteable game sends directly. Sending a message to
an offline user no longer swallows the draft — it reports that the
user is offline and keeps the typed text.

Took 50 seconds


Took 3 minutes

* [Client] Extract sendPrivateMessage() to fix invite message draft overwrite

sendInviteMessage() was calling sayEdit->setText(text) then sendMessage(),
which overwrites any text the user had typed.  Extract the command-building
and sending logic into a new sendPrivateMessage(const QString &text) method
that takes the text directly.  sendMessage() now calls it after its guards
and clears sayEdit; sendInviteMessage() calls it directly without touching
the input field at all.

Took 33 minutes

* Rename method, address comments.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-08-17 10:49:19 +02:00 • committed by GitHub
parent 776f917ffc
commit 765ebf8fb1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 179 additions and 14 deletions

View file

@ -7,9 +7,12 @@
#ifndef USER_CONTEXT_MENU_H
#define USER_CONTEXT_MENU_H
#include <QObject>
#include <libcockatrice/network/server/remote/user_level.h>
#include "../../interface/widgets/server/game_link.h"
#include <QList>
#include <QObject>
#include <functional>
#include <libcockatrice/network/server/remote/user_level.h>
class AbstractGame;
class UserListProxy;
class AbstractClient;
@ -43,6 +46,7 @@ private:
QAction *aPromoteToJudge, *aDemoteFromJudge;
QAction *aWarnUser, *aWarnHistory;
QAction *aGetAdminNotes;
std::function<QList<GameInviteOption>()> gameInviteLinkProvider;
signals:
void openMessageDialog(const QString &userName, bool focus);
private slots:
@ -80,9 +84,28 @@ public:
return userListProxy;
}
void setGameInviteLinkProvider(std::function<QList<GameInviteOption>()> provider)
{
gameInviteLinkProvider = std::move(provider);
}
/**
* The games currently inviteable for @p userName, honoring the room's
* buddy-only setting (the inviter must be the game's creator and the
* target a buddy of theirs). Empty when there is no live provider.
*/
QList<GameInviteOption> inviteOptionsForUser(const QString &userName) const;
/** Whether at least one invite link is currently available for @p userName. */
bool hasGameInviteLink(const QString &userName) const
{
return !inviteOptionsForUser(userName).isEmpty();
}
// Individual action entry points — used by UserInfoPopup to trigger
// actions without re-running the full context menu flow.
void execChat(const QString &userName);
void execInvite(const QString &userName);
void execDetails(const QString &userName);
void execShowGames(const QString &userName);
void execAddToBuddy(const QString &userName);
@ -97,6 +120,9 @@ public:
void execAdminNotes(const QString &userName);
void execAdjustMod(const QString &userName, bool shouldBeMod);
void execAdjustJudge(const QString &userName, bool shouldBeJudge);
private:
void execInvite(const QString &userName, const GameInviteOption &option);
};
#endif