Search added, but change window and back lets app crash
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,2 @@
|
|||||||
.git/*
|
.git/*
|
||||||
CMakeLists.txt.*
|
|
||||||
build/*
|
build/*
|
||||||
|
|||||||
@@ -114,3 +114,35 @@ height: 31px;
|
|||||||
.Wt-dialog > div > div > div > h4 {
|
.Wt-dialog > div > div > div > h4 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
.handle {
|
||||||
|
top: calc(50% - 0.55rem) !important;
|
||||||
|
margin-left: 2px;
|
||||||
|
width: 1rem !important;
|
||||||
|
height: 1rem !important;
|
||||||
|
background-color: black;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 1rem;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 0 0 1px #fff, 0 0 0 .25rem rgba(red($primary), green($primary), blue($primary), .25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.search-line {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
.popup-style {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.countries-drop-down {
|
||||||
|
overflow: auto;
|
||||||
|
background: RGBA(248, 248, 248, 0.8);
|
||||||
|
position: absolute;
|
||||||
|
max-height: 10em;
|
||||||
|
display: inline-block;
|
||||||
|
top: 100%;
|
||||||
|
left: 58px;
|
||||||
|
z-index: 20;
|
||||||
|
border: 1px solid #666;
|
||||||
|
box-shadow: 2px 2px 4px #666;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|||||||
253
src/app.cpp
253
src/app.cpp
@@ -33,6 +33,9 @@
|
|||||||
#include <Wt/WFileUpload.h>
|
#include <Wt/WFileUpload.h>
|
||||||
#include <Wt/Utils.h>
|
#include <Wt/Utils.h>
|
||||||
#include <Wt/WSound.h>
|
#include <Wt/WSound.h>
|
||||||
|
#include <Wt/WSlider.h>
|
||||||
|
#include <Wt/WGridLayout.h>
|
||||||
|
#include <Wt/WCheckBox.h>
|
||||||
|
|
||||||
App::App(const Wt::WEnvironment &env, Broadcast &server):
|
App::App(const Wt::WEnvironment &env, Broadcast &server):
|
||||||
Wt::WApplication(env),
|
Wt::WApplication(env),
|
||||||
@@ -50,8 +53,10 @@ App::App(const Wt::WEnvironment &env, Broadcast &server):
|
|||||||
createUserListContainer(horizontalContainer);
|
createUserListContainer(horizontalContainer);
|
||||||
createContentContainer(horizontalContainer);
|
createContentContainer(horizontalContainer);
|
||||||
createImprintContainer(verticalContainer);
|
createImprintContainer(verticalContainer);
|
||||||
|
reSetUser();
|
||||||
if (userName == "") {
|
if (userName == "") {
|
||||||
showLogin();
|
showLogin();
|
||||||
|
Wt::WMessageBox::show("Cookie information", "By technical reasons we set a cookie. This cookie isn't tracking anything or will be saved in a database.", Wt::StandardButton::Ok);
|
||||||
} else {
|
} else {
|
||||||
startChat();
|
startChat();
|
||||||
}
|
}
|
||||||
@@ -68,6 +73,20 @@ void App::initApp() {
|
|||||||
useStyleSheet("style.css");
|
useStyleSheet("style.css");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::reSetUser() {
|
||||||
|
if (env_.cookies().contains("wtd")) {
|
||||||
|
auto userData = server_.reSetUser(env_.cookies().find("wtd")->second, sessionId());
|
||||||
|
Wt::Json::Object emptyObject = {};
|
||||||
|
if (userData != emptyObject) {
|
||||||
|
userName = (std::string)userData["username"];
|
||||||
|
gender = (std::string)userData["gender"];
|
||||||
|
country = (std::string)userData["country"];
|
||||||
|
isoCountryCode = (std::string)userData["iso-country-code"];
|
||||||
|
age = (int)userData["age"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Wt::WVBoxLayout *App::createVerticalLayout() {
|
Wt::WVBoxLayout *App::createVerticalLayout() {
|
||||||
auto verticalBox = root()->addNew<Wt::WContainerWidget>();
|
auto verticalBox = root()->addNew<Wt::WContainerWidget>();
|
||||||
verticalBox->setHeight(Wt::WLength(100, Wt::LengthUnit::Percentage));
|
verticalBox->setHeight(Wt::WLength(100, Wt::LengthUnit::Percentage));
|
||||||
@@ -177,6 +196,7 @@ void App::handleLogin(Wt::WLineEdit* userName, Wt::WComboBox* countryWidget, Wt:
|
|||||||
setUserData(nick, countryWidget, ageWidget, genderWidget);
|
setUserData(nick, countryWidget, ageWidget, genderWidget);
|
||||||
connectToServer();
|
connectToServer();
|
||||||
startChat();
|
startChat();
|
||||||
|
setCookie("wtd", sessionId(), 21000);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
Wt::WMessageBox::show("Attention", e.what(), Wt::StandardButton::Ok);
|
Wt::WMessageBox::show("Attention", e.what(), Wt::StandardButton::Ok);
|
||||||
}
|
}
|
||||||
@@ -243,7 +263,7 @@ void App::populateGenderComboBox(Wt::WComboBox *genderWidget) {
|
|||||||
std::string App::getGenderShortByGender(std::string gender) {
|
std::string App::getGenderShortByGender(std::string gender) {
|
||||||
for (const auto &genderItem: genders_) {
|
for (const auto &genderItem: genders_) {
|
||||||
if (gender == genderItem.second) {
|
if (gender == genderItem.second) {
|
||||||
return genderItem.first;
|
return genderItem.first.toUTF8();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
@@ -258,15 +278,7 @@ void App::updateUserlist(Wt::Json::Array unsortedUserList) {
|
|||||||
userListContainer_->setOverflow(Wt::Overflow::Auto);
|
userListContainer_->setOverflow(Wt::Overflow::Auto);
|
||||||
auto sortedUserList = sortUserList(unsortedUserList);
|
auto sortedUserList = sortUserList(unsortedUserList);
|
||||||
for (Wt::Json::Object &user: sortedUserList) {
|
for (Wt::Json::Object &user: sortedUserList) {
|
||||||
auto userName = (std::string)user["name"];
|
addUserItemToLayout(layout, user);
|
||||||
auto userItem = layout->addNew<Wt::WText>(Wt::WString("{1} ({2})").arg(userName).arg((int)user["age"]));
|
|
||||||
userItem->setStyleClass(Wt::WString("userlist-item userlist-gender-{1}").arg((std::string)user["gender"]));
|
|
||||||
userItem->setHeight(Wt::WLength(2, Wt::LengthUnit::FontEm));
|
|
||||||
userItem->setPadding(Wt::WLength(3, Wt::LengthUnit::Pixel));
|
|
||||||
userItem->clicked().connect([=, this]() {
|
|
||||||
requestConversation(userName);
|
|
||||||
});
|
|
||||||
Wt::WCssDecorationStyle style;
|
|
||||||
}
|
}
|
||||||
layout->addWidget(std::make_unique<Wt::WText>(), 1)->setStyleClass("height-spacer userlist");
|
layout->addWidget(std::make_unique<Wt::WText>(), 1)->setStyleClass("height-spacer userlist");
|
||||||
triggerUpdate();
|
triggerUpdate();
|
||||||
@@ -768,7 +780,7 @@ void App::addIdentifier() {
|
|||||||
|
|
||||||
void App::addSearchButton() {
|
void App::addSearchButton() {
|
||||||
auto searchButton = menuContainer_->addNew<Wt::WPushButton>("Search");
|
auto searchButton = menuContainer_->addNew<Wt::WPushButton>("Search");
|
||||||
searchButton->clicked().connect(this, &App::executeSearch);
|
searchButton->clicked().connect(this, &App::showSearchWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::addInboxButton() {
|
void App::addInboxButton() {
|
||||||
@@ -776,11 +788,203 @@ void App::addInboxButton() {
|
|||||||
inbox_->clicked().connect(this, &App::openInbox);
|
inbox_->clicked().connect(this, &App::openInbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::executeSearch() {
|
void App::showSearchWindow() {
|
||||||
|
auto contentLayout = resetSearchFields();
|
||||||
|
auto userNameField = setupNameSearchField(contentLayout);
|
||||||
|
auto ageSearchFields = setupSearchFields(contentLayout);
|
||||||
|
auto countryFields = setupCountryDropDown(contentLayout);
|
||||||
|
auto gendersFields = setupGendersDropDown(contentLayout);
|
||||||
|
auto searchResultContainer = setupSearchButton(contentLayout);
|
||||||
|
restoreSearchFields(searchResultContainer, userNameField, ageSearchFields.first, ageSearchFields.second,
|
||||||
|
countryFields.second, gendersFields.second, countryFields.first, gendersFields.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
Wt::WVBoxLayout *App::resetSearchFields() {
|
||||||
currentConversationWith_ = "";
|
currentConversationWith_ = "";
|
||||||
contentContainer_->clear();
|
contentContainer_->clear();
|
||||||
contentContainer_->addNew<Wt::WText>("search");
|
|
||||||
inboxOpen_ = false;
|
inboxOpen_ = false;
|
||||||
|
auto contentLayout = contentContainer_->setLayout(std::make_unique<Wt::WVBoxLayout>());
|
||||||
|
contentLayout->addNew<Wt::WText>("<h2>Search</h2>");
|
||||||
|
return contentLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Wt::WSpinBox*, Wt::WSpinBox*> App::setupSearchFields(Wt::WVBoxLayout *contentLayout) {
|
||||||
|
auto minAgeEdit = addSearchItemLine<Wt::WSpinBox>(contentLayout, "From age");
|
||||||
|
minAgeEdit->setRange(18, 150);
|
||||||
|
minAgeEdit->setValue(18);
|
||||||
|
minAgeEdit->changed().connect([=, this] { searchFields.minAge = minAgeEdit->value(); });
|
||||||
|
auto maxAgeEdit = addSearchItemLine<Wt::WSpinBox>(contentLayout, "To age");
|
||||||
|
maxAgeEdit->setRange(18, 150);
|
||||||
|
maxAgeEdit->setValue(150);
|
||||||
|
maxAgeEdit->changed().connect([=, this] { searchFields.maxAge = maxAgeEdit->value(); });
|
||||||
|
return {minAgeEdit, maxAgeEdit};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Wt::WPushButton*, Wt::WContainerWidget*> App::setupCountryDropDown(Wt::WVBoxLayout *contentLayout) {
|
||||||
|
auto countryDropDown = std::make_unique<Wt::WContainerWidget>();
|
||||||
|
countryDropDown->setInline(true);
|
||||||
|
countryDropDown->setStyleClass("countries-drop-down");
|
||||||
|
auto countryDropDownContainer = countryDropDown.get();
|
||||||
|
auto countryOpenList = addSearchItemLine<Wt::WPushButton>(contentLayout, "Country", std::move(countryDropDown));
|
||||||
|
countryDropDownContainer->hide();
|
||||||
|
countryOpenList->clicked().connect([=]() { if (countryDropDownContainer->isHidden()) { countryDropDownContainer->show();} else { countryDropDownContainer->hide(); } });
|
||||||
|
countryOpenList->setText("All");
|
||||||
|
std::map<Wt::WString, Wt::WString> countries = server_.countries();
|
||||||
|
addItem("All", countryDropDownContainer, countryOpenList, &searchFields.countries, true);
|
||||||
|
addItem(country, countryDropDownContainer, countryOpenList, &searchFields.countries);
|
||||||
|
for (const auto &itemCountry: countries) {
|
||||||
|
if (itemCountry.first.toUTF8() != country) {
|
||||||
|
addItem(itemCountry.first.toUTF8(), countryDropDownContainer, countryOpenList, &searchFields.countries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {countryOpenList, countryDropDownContainer};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Wt::WPushButton*, Wt::WContainerWidget*> App::setupGendersDropDown(Wt::WVBoxLayout *contentLayout) {
|
||||||
|
auto gendersDropDown = std::make_unique<Wt::WContainerWidget>();
|
||||||
|
gendersDropDown->setInline(true);
|
||||||
|
gendersDropDown->setStyleClass("countries-drop-down");
|
||||||
|
auto gendersDropDownContainer = gendersDropDown.get();
|
||||||
|
auto gendersOpenList = addSearchItemLine<Wt::WPushButton>(contentLayout, "Genders", std::move(gendersDropDown));
|
||||||
|
gendersDropDownContainer->hide();
|
||||||
|
gendersOpenList->clicked().connect([=]() { if (gendersDropDownContainer->isHidden()) { gendersDropDownContainer->show();} else { gendersDropDownContainer->hide(); } });
|
||||||
|
gendersOpenList->setText("All");
|
||||||
|
addItem("All", gendersDropDownContainer, gendersOpenList, &searchFields.gender, true);
|
||||||
|
std::map<Wt::WString, Wt::WString> swappedGenders;
|
||||||
|
for (const auto& pair : genders_) {
|
||||||
|
swappedGenders[pair.second] = pair.first;
|
||||||
|
}
|
||||||
|
for (const auto &itemGender: swappedGenders) {
|
||||||
|
addItem(itemGender.first.toUTF8(), gendersDropDownContainer, gendersOpenList, &searchFields.gender);
|
||||||
|
}
|
||||||
|
return {gendersOpenList, gendersDropDownContainer};
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::addItem(const std::string& country, Wt::WContainerWidget *dropDownContainer, Wt::WPushButton *openListButton, std::unordered_set<std::string> *saveItems, bool isSelected) {
|
||||||
|
auto menuItem = dropDownContainer->addNew<Wt::WCheckBox>(country);
|
||||||
|
menuItem->changed().connect([=, this]() mutable { itemChanged(menuItem, dropDownContainer, openListButton, saveItems); });
|
||||||
|
menuItem->setInline(false);
|
||||||
|
if (isSelected) {
|
||||||
|
menuItem->setChecked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::addUserItemToLayout(Wt::WVBoxLayout *layout, Wt::Json::Object userObject) {
|
||||||
|
auto userName = (std::string)userObject["name"];
|
||||||
|
auto userItem = layout->addNew<Wt::WText>(Wt::WString("{1} ({2})").arg(userName).arg((int)userObject["age"]));
|
||||||
|
userItem->setStyleClass(Wt::WString("userlist-item userlist-gender-{1}").arg((std::string)userObject["gender"]));
|
||||||
|
userItem->setHeight(Wt::WLength(2, Wt::LengthUnit::FontEm));
|
||||||
|
userItem->setPadding(Wt::WLength(3, Wt::LengthUnit::Pixel));
|
||||||
|
userItem->clicked().connect([=, this]() {
|
||||||
|
requestConversation(userName);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<std::string> App::gendersListToShortGendersList(std::unordered_set<std::string> gendersList) {
|
||||||
|
std::unordered_set<std::string> result;
|
||||||
|
for (const auto &gender: gendersList) {
|
||||||
|
result.insert(genderShortOfGender(gender));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string App::genderShortOfGender(const std::string incomingGender) {
|
||||||
|
for (const auto &genderItem: genders_) {
|
||||||
|
if (incomingGender == genderItem.second) {
|
||||||
|
return genderItem.first.toUTF8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return incomingGender;
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::itemChanged(Wt::WCheckBox *item, Wt::WContainerWidget *dropDownContainer, Wt::WPushButton *openButton, std::unordered_set<std::string> *saveItems) {
|
||||||
|
saveItems->clear();
|
||||||
|
bool unselect = (item->text() == "All" && item->isChecked());
|
||||||
|
for (auto &widgetItem: dropDownContainer->children()) {
|
||||||
|
auto widgetCheckBox = static_cast<Wt::WCheckBox*>(widgetItem);
|
||||||
|
if (unselect && widgetCheckBox->text() != "All" && widgetCheckBox->isChecked()) {
|
||||||
|
widgetCheckBox->setChecked(false);
|
||||||
|
}
|
||||||
|
if (widgetCheckBox->isChecked()) {
|
||||||
|
saveItems->insert(widgetCheckBox->text().toUTF8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (saveItems->size() > 1 && saveItems->find("All") != saveItems->end()) {
|
||||||
|
saveItems->erase(saveItems->find("All"));
|
||||||
|
static_cast<Wt::WCheckBox*>(dropDownContainer->children().at(0))->setChecked(false);
|
||||||
|
}
|
||||||
|
if (saveItems->empty()) {
|
||||||
|
saveItems->insert("All");
|
||||||
|
}
|
||||||
|
std::string result;
|
||||||
|
for (const auto &selected: *saveItems) {
|
||||||
|
result += (result.empty() ? "" : ", ") + selected;
|
||||||
|
}
|
||||||
|
openButton->setText(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::restoreSearchFields(Wt::WContainerWidget *searchResultContainer, Wt::WLineEdit *userNameEdit, Wt::WSpinBox *minAgeEdit,
|
||||||
|
Wt::WSpinBox *maxAgeEdit, Wt::WContainerWidget *countryDropDownContainer, Wt::WContainerWidget *gendersDropDownContainer,
|
||||||
|
Wt::WPushButton *countryOpenList, Wt::WPushButton *gendersOpenList) {
|
||||||
|
if (!searchFields.set) {
|
||||||
|
searchFields = Search(searchResultContainer);
|
||||||
|
} else {
|
||||||
|
userNameEdit->setValueText(searchFields.userName);
|
||||||
|
minAgeEdit->setValue(searchFields.minAge);
|
||||||
|
maxAgeEdit->setValue(searchFields.maxAge);
|
||||||
|
for (auto countryWidget: countryDropDownContainer->children()) {
|
||||||
|
auto countryCheckBox = (Wt::WCheckBox*)countryWidget;
|
||||||
|
countryCheckBox->setChecked(searchFields.countries.find(countryCheckBox->text().toUTF8()) != searchFields.countries.end());
|
||||||
|
}
|
||||||
|
itemChanged((Wt::WCheckBox*)*countryDropDownContainer->children().begin(), countryDropDownContainer, countryOpenList, &searchFields.countries);
|
||||||
|
for (auto genderWidget: gendersDropDownContainer->children()) {
|
||||||
|
auto genderCheckBox = (Wt::WCheckBox*)genderWidget;
|
||||||
|
genderCheckBox->setChecked(searchFields.gender.find(genderCheckBox->text().toUTF8()) != searchFields.gender.end());
|
||||||
|
}
|
||||||
|
itemChanged((Wt::WCheckBox*)*gendersDropDownContainer->children().begin(), gendersDropDownContainer, gendersOpenList, &searchFields.gender);
|
||||||
|
startSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Wt::WLineEdit *App::setupNameSearchField(Wt::WVBoxLayout *contentLayout) {
|
||||||
|
auto userNameEdit = addSearchItemLine<Wt::WLineEdit>(contentLayout, "Username includes");
|
||||||
|
userNameEdit->changed().connect([=, this] { searchFields.userName = userNameEdit->text().trim(); });
|
||||||
|
return userNameEdit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wt::WContainerWidget *App::setupSearchButton(Wt::WVBoxLayout *contentLayout) {
|
||||||
|
auto searchButton = addSearchItemLine<Wt::WPushButton>(contentLayout, "");
|
||||||
|
searchButton->setText("Search");
|
||||||
|
auto searchResultContainer = contentLayout->addWidget(std::make_unique<Wt::WContainerWidget>(), 1);
|
||||||
|
searchResultContainer->addNew<Wt::WText>("No results");
|
||||||
|
searchButton->clicked().connect(this, &App::startSearch);
|
||||||
|
return searchResultContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::startSearch() {
|
||||||
|
if (searchFields.minAge > searchFields.maxAge) {
|
||||||
|
searchFields.outputContainer->clear();
|
||||||
|
searchFields.outputContainer->addNew<Wt::WText>("Minimum age must be at least as large as or greater than the maximum age.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
server_.userSearch(sessionId(), searchFields.userName.toUTF8(), searchFields.minAge, searchFields.maxAge,
|
||||||
|
searchFields.countries, gendersListToShortGendersList(searchFields.gender), userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::showSearch(Wt::Json::Object broadcast) {
|
||||||
|
searchFields.outputContainer->clear();
|
||||||
|
auto searchResult = (Wt::Json::Array)broadcast["data"];
|
||||||
|
if (searchResult.size() == 0) {
|
||||||
|
searchFields.outputContainer->addNew<Wt::WText>("No results.");
|
||||||
|
}
|
||||||
|
auto searchListContainer = searchFields.outputContainer->addNew<Wt::WContainerWidget>();
|
||||||
|
auto searchList = searchListContainer->setLayout(std::make_unique<Wt::WVBoxLayout>());
|
||||||
|
searchListContainer->setOverflow(Wt::Overflow::Auto);
|
||||||
|
for (const Wt::Json::Object &searchItem: searchResult) {
|
||||||
|
addUserItemToLayout(searchList, searchItem);
|
||||||
|
}
|
||||||
|
triggerUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::openInbox() {
|
void App::openInbox() {
|
||||||
@@ -810,6 +1014,8 @@ void App::incomingBroadcast() {
|
|||||||
showSystemMessage(broadcast);
|
showSystemMessage(broadcast);
|
||||||
} else if (broadcast["type"] == "conversation-start") {
|
} else if (broadcast["type"] == "conversation-start") {
|
||||||
showConversation(broadcast);
|
showConversation(broadcast);
|
||||||
|
} else if (broadcast["type"] == "search-result") {
|
||||||
|
showSearch(broadcast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -912,3 +1118,24 @@ bool App::isNickAllowed(const std::string& nick) {
|
|||||||
return lowercaseNick.find(lowercasePhrase) == std::string::npos;
|
return lowercaseNick.find(lowercasePhrase) == std::string::npos;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Class>
|
||||||
|
Class *App::addSearchItemLine(Wt::WVBoxLayout *layout, std::string label, std::unique_ptr<Wt::WContainerWidget> additionalItem) {
|
||||||
|
auto lineContainer = layout->addNew<Wt::WContainerWidget>();
|
||||||
|
lineContainer->setStyleClass("search-line");
|
||||||
|
lineContainer->setPositionScheme(Wt::PositionScheme::Relative);
|
||||||
|
auto lineLayout = lineContainer->setLayout(std::make_unique<Wt::WHBoxLayout>());
|
||||||
|
lineContainer->setPadding(Wt::WLength("0"));
|
||||||
|
lineContainer->setMargin(Wt::WLength("0"));
|
||||||
|
lineLayout->addNew<Wt::WText>(label);
|
||||||
|
lineLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
lineLayout->setSpacing(0);
|
||||||
|
auto input = std::make_unique<Class>();
|
||||||
|
auto returnInput = input.get();
|
||||||
|
lineLayout->addWidget(std::move(input), 1);
|
||||||
|
if (additionalItem) {
|
||||||
|
lineLayout->addWidget(std::move(additionalItem));
|
||||||
|
lineContainer->setOverflow(Wt::Overflow::Visible);
|
||||||
|
}
|
||||||
|
return returnInput;
|
||||||
|
}
|
||||||
|
|||||||
38
src/app.h
38
src/app.h
@@ -7,6 +7,7 @@
|
|||||||
#include "curl/curl.h"
|
#include "curl/curl.h"
|
||||||
#include <libxml2/libxml/tree.h>
|
#include <libxml2/libxml/tree.h>
|
||||||
#include <libxml2/libxml/xpath.h>
|
#include <libxml2/libxml/xpath.h>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace Magick {
|
namespace Magick {
|
||||||
class Image;
|
class Image;
|
||||||
@@ -18,7 +19,7 @@ public:
|
|||||||
~App();
|
~App();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, std::string> genders_ {
|
std::map<Wt::WString, Wt::WString> genders_ {
|
||||||
{"F", "Female"},
|
{"F", "Female"},
|
||||||
{"M", "Male"},
|
{"M", "Male"},
|
||||||
{"P", "Pair"},
|
{"P", "Pair"},
|
||||||
@@ -46,6 +47,22 @@ private:
|
|||||||
{";p", Smiley("1F61C", "Twinkle tongue")},
|
{";p", Smiley("1F61C", "Twinkle tongue")},
|
||||||
{":'(", Smiley("1F622", "Cry")}
|
{":'(", Smiley("1F622", "Cry")}
|
||||||
};
|
};
|
||||||
|
struct Search {
|
||||||
|
Wt::WContainerWidget *outputContainer;
|
||||||
|
Wt::WString userName{""};
|
||||||
|
int minAge{18};
|
||||||
|
int maxAge{150};
|
||||||
|
std::unordered_set<std::string> gender;
|
||||||
|
std::unordered_set<std::string> countries;
|
||||||
|
Search()=default;
|
||||||
|
Search(Wt::WContainerWidget *outputContainer_):
|
||||||
|
outputContainer(outputContainer_) {
|
||||||
|
set = true;
|
||||||
|
gender.insert("All");
|
||||||
|
countries.insert("All");
|
||||||
|
}
|
||||||
|
bool set = false;
|
||||||
|
};
|
||||||
Wt::WString smileyPlaceholder_ = "&#x{1};";
|
Wt::WString smileyPlaceholder_ = "&#x{1};";
|
||||||
const Wt::WEnvironment &env_;
|
const Wt::WEnvironment &env_;
|
||||||
Broadcast &server_;
|
Broadcast &server_;
|
||||||
@@ -60,7 +77,9 @@ private:
|
|||||||
bool inboxOpen_{false};
|
bool inboxOpen_{false};
|
||||||
int messageCursorPosition_{-1};
|
int messageCursorPosition_{-1};
|
||||||
std::unique_ptr<Wt::WSound> messageReceived_;
|
std::unique_ptr<Wt::WSound> messageReceived_;
|
||||||
|
Search searchFields;
|
||||||
void initApp();
|
void initApp();
|
||||||
|
void reSetUser();
|
||||||
Wt::WVBoxLayout *createVerticalLayout();
|
Wt::WVBoxLayout *createVerticalLayout();
|
||||||
Wt::WHBoxLayout *createActionLayout(Wt::WVBoxLayout *verticalContainer);
|
Wt::WHBoxLayout *createActionLayout(Wt::WVBoxLayout *verticalContainer);
|
||||||
void createUserListContainer(Wt::WHBoxLayout *layout);
|
void createUserListContainer(Wt::WHBoxLayout *layout);
|
||||||
@@ -88,7 +107,10 @@ private:
|
|||||||
void addIdentifier();
|
void addIdentifier();
|
||||||
void addSearchButton();
|
void addSearchButton();
|
||||||
void addInboxButton();
|
void addInboxButton();
|
||||||
void executeSearch();
|
void showSearchWindow();
|
||||||
|
void startSearch();
|
||||||
|
void showSearch(Wt::Json::Object broadcast);
|
||||||
|
template<class Class> Class *addSearchItemLine(Wt::WVBoxLayout *layout, std::string label, std::unique_ptr<Wt::WContainerWidget> additionalItem = nullptr);
|
||||||
void openInbox();
|
void openInbox();
|
||||||
bool isNickAllowed(const std::string &nick);
|
bool isNickAllowed(const std::string &nick);
|
||||||
bool compareJsonObjects(const Wt::Json::Object &obj1, const Wt::Json::Object &obj2);
|
bool compareJsonObjects(const Wt::Json::Object &obj1, const Wt::Json::Object &obj2);
|
||||||
@@ -146,6 +168,18 @@ private:
|
|||||||
Wt::WWebWidget *createImageElement(Wt::Json::Object &line, const std::string &writer, Wt::WContainerWidget *outputContainer);
|
Wt::WWebWidget *createImageElement(Wt::Json::Object &line, const std::string &writer, Wt::WContainerWidget *outputContainer);
|
||||||
Wt::WWebWidget *createTextElement(const std::string &writer, const std::string &text, Wt::WContainerWidget *outputContainer);
|
Wt::WWebWidget *createTextElement(const std::string &writer, const std::string &text, Wt::WContainerWidget *outputContainer);
|
||||||
void createImprintContainer(Wt::WVBoxLayout *containerLayout);
|
void createImprintContainer(Wt::WVBoxLayout *containerLayout);
|
||||||
|
Wt::WContainerWidget *setupSearchButton(Wt::WVBoxLayout *contentLayout);
|
||||||
|
void restoreSearchFields(Wt::WContainerWidget *searchResultContainer, Wt::WLineEdit *userNameEdit, Wt::WSpinBox *minAgeEdit, Wt::WSpinBox *maxAgeEdit, Wt::WContainerWidget *countryDropDownContainer, Wt::WContainerWidget *gendersDropDownContainer, Wt::WPushButton *countryOpenList, Wt::WPushButton *gendersOpenList);
|
||||||
|
Wt::WLineEdit *setupNameSearchField(Wt::WVBoxLayout *contentLayout);
|
||||||
|
std::pair<Wt::WPushButton *, Wt::WContainerWidget *> setupGendersDropDown(Wt::WVBoxLayout *contentLayout);
|
||||||
|
std::pair<Wt::WPushButton *, Wt::WContainerWidget *> setupCountryDropDown(Wt::WVBoxLayout *contentLayout);
|
||||||
|
std::pair<Wt::WSpinBox*, Wt::WSpinBox*> setupSearchFields(Wt::WVBoxLayout *contentLayout);
|
||||||
|
Wt::WVBoxLayout *resetSearchFields();
|
||||||
|
void itemChanged(Wt::WCheckBox *item, Wt::WContainerWidget *dropDownContainer, Wt::WPushButton *openButton, std::unordered_set<std::string> *saveItems);
|
||||||
|
void addItem(const std::string &country, Wt::WContainerWidget *dropDownContainer, Wt::WPushButton *openListButton, std::unordered_set<std::string> *saveItems, bool isSelected = false);
|
||||||
|
void addUserItemToLayout(Wt::WVBoxLayout *layout, Wt::Json::Object userObject);
|
||||||
|
std::unordered_set<std::string> gendersListToShortGendersList(std::unordered_set<std::string> gendersList);
|
||||||
|
std::string genderShortOfGender(const std::string incomingGender);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // APP_H
|
#endif // APP_H
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ Broadcast::~Broadcast() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Broadcast::connect(Client *client, const std::function<void ()> &fct) {
|
void Broadcast::connect(Client *client, const std::function<void ()> &fct) {
|
||||||
std::unique_lock<std::mutex> lock(mutex_);
|
|
||||||
connections_.push_back(std::make_unique<Connection>(Wt::WApplication::instance()->sessionId(), client, fct));
|
connections_.push_back(std::make_unique<Connection>(Wt::WApplication::instance()->sessionId(), client, fct));
|
||||||
auto broadcast = createUserList();
|
auto broadcast = createUserList();
|
||||||
for (auto &connection: connections_) {
|
for (auto &connection: connections_) {
|
||||||
@@ -53,6 +52,23 @@ void Broadcast::disconnect(Client *client) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Wt::Json::Object Broadcast::reSetUser(std::string oldSessionId, std::string newSessionId) {
|
||||||
|
for (auto& connection : connections_) {
|
||||||
|
if (connection->setSessionId(oldSessionId, newSessionId)) {
|
||||||
|
Wt::Json::Object userData {
|
||||||
|
{"gender", Wt::Json::Value(connection->gender())},
|
||||||
|
{"country", Wt::Json::Value(connection->country())},
|
||||||
|
{"iso-country-code", Wt::Json::Value(getCountryIsoCodeByCountry(connection->country()))},
|
||||||
|
{"username", Wt::Json::Value(connection->userName())},
|
||||||
|
{"age", Wt::Json::Value(connection->age())},
|
||||||
|
};
|
||||||
|
reSetSessionIdInMessages(oldSessionId, newSessionId);
|
||||||
|
return userData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
int Broadcast::count() const {
|
int Broadcast::count() const {
|
||||||
std::unique_lock<std::mutex> lock(mutex_);
|
std::unique_lock<std::mutex> lock(mutex_);
|
||||||
return connections_.size();
|
return connections_.size();
|
||||||
@@ -349,6 +365,28 @@ void Broadcast::requestConversation(std::string sendToSessionId, std::string wit
|
|||||||
addMessageToSessionBroadcast(sendToSessionId, broadcast);
|
addMessageToSessionBroadcast(sendToSessionId, broadcast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Broadcast::userSearch(std::string toSession, std::string nameIncludes, int minAge, int maxAge, std::unordered_set<std::string> countries, std::unordered_set<std::string> genders, std::string excludeName) {
|
||||||
|
Wt::Json::Array searchResult;
|
||||||
|
for (const auto &user: connections_) {
|
||||||
|
std::cout << user->gender() << std::endl;
|
||||||
|
if (
|
||||||
|
(nameIncludes == "" || user->userName().find(nameIncludes) != std::string::npos)
|
||||||
|
&& (minAge <= user->age())
|
||||||
|
&& (maxAge >= user->age())
|
||||||
|
&& (countries.contains("All") || countries.contains(user->country()) || countries.size() == 0)
|
||||||
|
&& (genders.contains("All") || genders.contains(user->gender()) || genders.size() == 0)
|
||||||
|
&& (user->userName() != excludeName)
|
||||||
|
) {
|
||||||
|
searchResult.push_back(user->client()->json());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Wt::Json::Object broadcast{
|
||||||
|
{"type", "search-result"},
|
||||||
|
{"data", searchResult}
|
||||||
|
};
|
||||||
|
addMessageToSessionBroadcast(toSession, broadcast);
|
||||||
|
}
|
||||||
|
|
||||||
bool Broadcast::parseCountriesData() {
|
bool Broadcast::parseCountriesData() {
|
||||||
std::istringstream iss(responseData_);
|
std::istringstream iss(responseData_);
|
||||||
std::string line;
|
std::string line;
|
||||||
@@ -366,6 +404,12 @@ bool Broadcast::parseCountriesData() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Broadcast::reSetSessionIdInMessages(std::string oldSessionId, std::string newSessionId) {
|
||||||
|
for (auto &conversation: conversations_) {
|
||||||
|
conversation.second.setNewSesionId(oldSessionId, newSessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Wt::Json::Object Broadcast::createUserList() {
|
Wt::Json::Object Broadcast::createUserList() {
|
||||||
Wt::Json::Array userList;
|
Wt::Json::Array userList;
|
||||||
for (const auto &connection: connections_) {
|
for (const auto &connection: connections_) {
|
||||||
@@ -505,6 +549,14 @@ std::list<Wt::Json::Object> Broadcast::Connection::getBroadcasts(bool clear) {
|
|||||||
return broadcastCopy;
|
return broadcastCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Broadcast::Connection::setSessionId(std::string searchedSessionId, std::string newSessionId) {
|
||||||
|
if (searchedSessionId == sessionId_) {
|
||||||
|
sessionId_ = newSessionId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::function<void ()> Broadcast::Connection::fct() {
|
std::function<void ()> Broadcast::Connection::fct() {
|
||||||
return fct_;
|
return fct_;
|
||||||
}
|
}
|
||||||
@@ -557,3 +609,15 @@ Wt::Json::Object Broadcast::Message::json() {
|
|||||||
json["image"] = Wt::Json::Value(image);
|
json["image"] = Wt::Json::Value(image);
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Broadcast::Message::setNewSesionId(std::string oldSessionId, std::string newSessionId) {
|
||||||
|
if (fromSessionId == oldSessionId) {
|
||||||
|
fromSessionId = newSessionId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Broadcast::MessageQueue::setNewSesionId(std::string oldSessionId, std::string newSessionId) {
|
||||||
|
for (auto &message: messages) {
|
||||||
|
message.setNewSesionId(oldSessionId, newSessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
#include <Wt/WServer.h>
|
#include <Wt/WServer.h>
|
||||||
#include <Wt/WDate.h>
|
#include <Wt/WDate.h>
|
||||||
#include <Wt/Json/Object.h>
|
#include <Wt/Json/Object.h>
|
||||||
@@ -49,16 +50,19 @@ public:
|
|||||||
Wt::WString message;
|
Wt::WString message;
|
||||||
Wt::Json::Object image;
|
Wt::Json::Object image;
|
||||||
Wt::Json::Object json();
|
Wt::Json::Object json();
|
||||||
|
void setNewSesionId(std::string oldSessionId, std::string newSessionId);
|
||||||
};
|
};
|
||||||
struct MessageQueue {
|
struct MessageQueue {
|
||||||
bool user1Read{false};
|
bool user1Read{false};
|
||||||
bool user2Read{false};
|
bool user2Read{false};
|
||||||
std::vector<Message> messages;
|
std::vector<Message> messages;
|
||||||
|
void setNewSesionId(std::string oldSessionId, std::string newSessionId);
|
||||||
};
|
};
|
||||||
Broadcast(Wt::WServer *server);
|
Broadcast(Wt::WServer *server);
|
||||||
~Broadcast();
|
~Broadcast();
|
||||||
void connect(Client *client, const std::function<void ()> &fct);
|
void connect(Client *client, const std::function<void ()> &fct);
|
||||||
void disconnect(Client *client);
|
void disconnect(Client *client);
|
||||||
|
Wt::Json::Object reSetUser(std::string oldSessionId, std::string newSessionId);
|
||||||
int count() const;
|
int count() const;
|
||||||
std::string userNameForSessionId(std::string sessionId);
|
std::string userNameForSessionId(std::string sessionId);
|
||||||
std::string sessionIdForUserName(std::string userName);
|
std::string sessionIdForUserName(std::string userName);
|
||||||
@@ -78,6 +82,8 @@ public:
|
|||||||
void sendUserInformation(std::string sendToSessionId, std::string userName, std::string requestingUserName);
|
void sendUserInformation(std::string sendToSessionId, std::string userName, std::string requestingUserName);
|
||||||
void toggleBlockUser(std::string blockingUserName, std::string blockedUser, std::string blockingUserSessionId);
|
void toggleBlockUser(std::string blockingUserName, std::string blockedUser, std::string blockingUserSessionId);
|
||||||
void requestConversation(std::string sendToSessionId, std::string withUserName, std::string requestingUserName);
|
void requestConversation(std::string sendToSessionId, std::string withUserName, std::string requestingUserName);
|
||||||
|
void userSearch(std::string toSession, std::string nameIncludes, int minAge, int maxAge,
|
||||||
|
std::unordered_set<std::string> countries, std::unordered_set<std::string> genders, std::string excludeName);
|
||||||
protected:
|
protected:
|
||||||
struct Connection {
|
struct Connection {
|
||||||
Connection(const std::string &id, Client *client, const std::function<void ()> &fct);
|
Connection(const std::string &id, Client *client, const std::function<void ()> &fct);
|
||||||
@@ -93,6 +99,7 @@ protected:
|
|||||||
void addBroadcast(Wt::Json::Object broadcast);
|
void addBroadcast(Wt::Json::Object broadcast);
|
||||||
std::list<Wt::Json::Object> getBroadcasts(bool clear = false);
|
std::list<Wt::Json::Object> getBroadcasts(bool clear = false);
|
||||||
std::unordered_map<std::string, std::vector<std::string> > blockings_;
|
std::unordered_map<std::string, std::vector<std::string> > blockings_;
|
||||||
|
bool setSessionId(std::string searchedSessionId, std::string newSessionId);
|
||||||
private:
|
private:
|
||||||
std::string sessionId_;
|
std::string sessionId_;
|
||||||
Client *client_;
|
Client *client_;
|
||||||
@@ -118,6 +125,7 @@ private:
|
|||||||
Wt::Json::Object logoutBroadcast();
|
Wt::Json::Object logoutBroadcast();
|
||||||
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output);
|
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output);
|
||||||
bool parseCountriesData();
|
bool parseCountriesData();
|
||||||
|
void reSetSessionIdInMessages(std::string oldSessionId, std::string newSessionId);
|
||||||
Wt::Json::Object createUserList();
|
Wt::Json::Object createUserList();
|
||||||
void sendMessageQueueToSession(std::string receiverSessionId, std::string user1, std::string user2, std::vector<Message> messages);
|
void sendMessageQueueToSession(std::string receiverSessionId, std::string user1, std::string user2, std::vector<Message> messages);
|
||||||
void addMessageToSessionBroadcast(std::string sessionId, Wt::Json::Object message);
|
void addMessageToSessionBroadcast(std::string sessionId, Wt::Json::Object message);
|
||||||
|
|||||||
Reference in New Issue
Block a user