mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-21 09:05:10 -07:00
* [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 1 minute * [Client] Add invite-to-game dialog to the game window Took 15 seconds * [Client] Open the invite dialog taller by default without enforcing a minimum size * Move button to bottom Took 3 minutes * Address comments. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
112 lines
4.4 KiB
C++
112 lines
4.4 KiB
C++
#include "dlg_invite_to_game.h"
|
|
|
|
#include "../server/user/user_list_manager.h"
|
|
#include "../server/user/user_list_widget.h"
|
|
#include "../tabs/tab_supervisor.h"
|
|
|
|
#include <QGuiApplication>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QLineEdit>
|
|
#include <QPushButton>
|
|
#include <QScreen>
|
|
#include <QUrl>
|
|
#include <QUrlQuery>
|
|
#include <QVBoxLayout>
|
|
|
|
DlgInviteToGame::DlgInviteToGame(TabSupervisor *_tabSupervisor,
|
|
const QString &_inviteUrl,
|
|
bool _onlyBuddies,
|
|
const QStringList &_excludeUserNames,
|
|
QWidget *parent)
|
|
: QDialog(parent), tabSupervisor(_tabSupervisor), inviteUrl(_inviteUrl), onlyBuddies(_onlyBuddies),
|
|
excludeUserNames(_excludeUserNames)
|
|
{
|
|
setModal(true);
|
|
|
|
searchEdit = new QLineEdit(this);
|
|
searchEdit->setClearButtonEnabled(true);
|
|
connect(searchEdit, &QLineEdit::textChanged, this, &DlgInviteToGame::searchTextChanged);
|
|
|
|
// The embedded list is the real room user list without the hover popup:
|
|
// same manager, same delegate/painter, same sections, live via manager
|
|
// signals while the modal loop runs.
|
|
UserListManager *manager = tabSupervisor->getUserListManager();
|
|
userList = new UserListWidget(tabSupervisor, tabSupervisor->getClient(), UserListWidget::RoomList, this,
|
|
/*hasUserInfoPopup=*/false);
|
|
userList->setUserFilter([this, manager](const QString &name, bool online) {
|
|
return !excludeUserNames.contains(name) && online && !manager->isUserIgnored(name);
|
|
});
|
|
if (onlyBuddies) {
|
|
userList->setSectioned({UserListWidget::Section::Buddy});
|
|
} else {
|
|
userList->setSectioned({UserListWidget::Section::Buddy, UserListWidget::Section::Online});
|
|
}
|
|
userList->bind(manager);
|
|
userList->rebuild();
|
|
|
|
connect(userList, &UserListWidget::userActivated, this, &DlgInviteToGame::inviteCurrentUser);
|
|
connect(userList, &UserListWidget::currentUserChanged, this, [this](const QString &userName) {
|
|
currentUserName = userName;
|
|
inviteButton->setEnabled(!userName.isEmpty());
|
|
});
|
|
|
|
inviteButton = new QPushButton(this);
|
|
inviteButton->setEnabled(false);
|
|
inviteButton->setDefault(true);
|
|
connect(inviteButton, &QPushButton::clicked, this, [this] { inviteCurrentUser(currentUserName); });
|
|
|
|
cancelButton = new QPushButton(this);
|
|
connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
|
|
|
|
auto *buttonRow = new QHBoxLayout;
|
|
buttonRow->addStretch();
|
|
buttonRow->addWidget(inviteButton);
|
|
buttonRow->addWidget(cancelButton);
|
|
|
|
auto *layout = new QVBoxLayout(this);
|
|
layout->addWidget(searchEdit);
|
|
layout->addWidget(userList, 1);
|
|
layout->addLayout(buttonRow);
|
|
|
|
retranslateUi();
|
|
|
|
// Default to a comfortably tall dialog so the list has room to breathe,
|
|
// capped by the available screen. No minimum is enforced: small screens
|
|
// and manual resizing can go shorter than this.
|
|
const QRect availableScreen = QGuiApplication::primaryScreen()->availableGeometry();
|
|
resize(sizeHint().width(), qMin(sizeHint().height() * 3, availableScreen.height() * 4 / 5));
|
|
}
|
|
|
|
void DlgInviteToGame::searchTextChanged(const QString &text)
|
|
{
|
|
userList->setFilterText(text);
|
|
}
|
|
|
|
void DlgInviteToGame::inviteCurrentUser(const QString &userName)
|
|
{
|
|
if (userName.isEmpty()) {
|
|
return;
|
|
}
|
|
// The invite link carries the game's id and, when the game has one, its
|
|
// description (makeGameJoinLink embeds both). Read them back so the prefix
|
|
// names the game by description first, then its id — identical to the
|
|
// context-menu invite so recipients see one consistent message style.
|
|
const QUrl inviteUrlObj(inviteUrl);
|
|
const QUrlQuery inviteQuery(inviteUrlObj);
|
|
const int gameId = inviteQuery.queryItemValue("gameid").toInt();
|
|
const QString gameDescription = inviteQuery.queryItemValue("game");
|
|
const QString prefix = gameDescription.isEmpty()
|
|
? tr("Join my game (#%1):").arg(gameId)
|
|
: tr("Join my game \"%1\" (#%2):").arg(gameDescription).arg(gameId);
|
|
tabSupervisor->sendInviteToUser(userName, prefix + " " + inviteUrl);
|
|
accept();
|
|
}
|
|
|
|
void DlgInviteToGame::retranslateUi()
|
|
{
|
|
setWindowTitle(tr("Invite to Game"));
|
|
searchEdit->setPlaceholderText(tr("Search users..."));
|
|
inviteButton->setText(tr("Invite"));
|
|
cancelButton->setText(tr("Cancel"));
|
|
}
|