Files
singlechat/src/app.h

229 lines
11 KiB
C++

#ifndef APP_H
#define APP_H
#include <Wt/WApplication.h>
#include <Wt/WEnvironment.h>
#include "broadcast.h"
#include "curl/curl.h"
#include <libxml2/libxml/tree.h>
#include <libxml2/libxml/xpath.h>
#include <unordered_set>
namespace Magick {
class Image;
class Blob;
}
class App : public Wt::WApplication, public Client {
public:
App(const Wt::WEnvironment& env, Broadcast& server);
~App();
private:
const std::string adminName {"comiciusadmin"};
const std::string adminPassword {"p3Lv9!7?+Qq"};
std::map<Wt::WString, Wt::WString> genders_ {
{"F", "Female"},
{"M", "Male"},
{"P", "Pair"},
{"TF", "Transgender (M->F)"},
{"TM", "Transgender (F->M)"}
};
std::vector<std::string> notAllowedNickPhrases_ {
"whore",
"hitler",
"nazi",
"admin"
};
struct Smiley {
std::string code;
std::string tooltip;
Smiley()=default;
Smiley(std::string code_, std::string tooltip_): code(code_), tooltip(tooltip_) {};
};
std::unordered_map<std::string, Smiley> smileys_ {
{":)", Smiley("1F642", "Smile")},
{":D", Smiley("1F600", "Laugh")},
{":(", Smiley("1F641", "Sad")},
{";)", Smiley("1F609", "Twinkle")},
{":p", Smiley("1F60B", "Tongue")},
{";p", Smiley("1F61C", "Twinkle tongue")},
{"O)", Smiley("1F607", "Angel")},
{":*", Smiley("1F617", "Kiss")},
{"(h)", Smiley("1FA77", "Heart")},
{"xD", Smiley("1F602", "Laughing hard")},
{":@", Smiley("1F635", "Confused")},
{":O", Smiley("1F632", "Surprised")},
{":3", Smiley("1F63A", "Cat face")},
{":|", Smiley("1F610", "Neutral")},
{":/", Smiley("1FAE4", "Skeptical")},
{":#", Smiley("1F912", "Sick")},
{"#)", Smiley("1F973", "Partied")},
{"%)", Smiley("1F974", "Drunk")},
{"(t)", Smiley("1F44D", "Thumbs up")},
{":'(", Smiley("1F622", "Cry")}
};
struct Search {
Wt::WContainerWidget *outputContainer = nullptr;
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};";
const Wt::WEnvironment &env_;
Broadcast &server_;
Wt::WContainerWidget *menuContainer_;
Wt::WContainerWidget *userListContainer_;
Wt::WContainerWidget *contentContainer_;
Wt::JSignal<std::string, std::string> updateLocationSignal_;
std::string responseData_{""};
Wt::WPushButton *inbox_;
std::vector<std::string> searchResults_;
std::string currentConversationWith_{""};
bool inboxOpen_{false};
std::shared_ptr<int> messageCursorPosition_;
std::unique_ptr<Wt::WSound> messageReceived_;
Search searchFields;
Wt::WTimer *loginTimer_;
Wt::WTimer *timeoutRemainingTimer_;
bool isLoggedInAsAdmin {false};
void setMetaTags();
void initLocale();
Wt::WLocale determineLocaleFromBrowser() const;
void initApp();
void reSetUser();
Wt::WVBoxLayout *createVerticalLayout();
Wt::WHBoxLayout *createActionLayout(Wt::WVBoxLayout *verticalContainer);
void createUserListContainer(Wt::WHBoxLayout *layout);
void createContentContainer(Wt::WHBoxLayout *layout);
void createHeadContainer(Wt::WVBoxLayout *layout);
void createMenuContainer(Wt::WVBoxLayout *layout);
void showLogin();
void incomingBroadcast();
void startChat();
void updateLocation();
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, std::string *output) {
size_t totalSize = size * nmemb;
output->append(reinterpret_cast<const char *>(contents), totalSize);
return totalSize;
};
void populateCountryComboBox(Wt::WComboBox *countryWidget);
void handleLogin(Wt::WLineEdit *userName, Wt::WComboBox *countryWidget, Wt::WSpinBox *ageWidget, Wt::WComboBox *genderWidget);
void populateGenderComboBox(Wt::WComboBox *genderWidget);
std::string getGenderShortByGender(std::string gender);
void updateUserlist(Wt::Json::Array userList, int size);
std::vector<Wt::Json::Object> sortUserList(Wt::Json::Array unsortedUserList);
void createMenu();
void addLeaveButton();
void logout();
void addHistoryButton();
void addIdentifier();
void addSearchButton();
void addInboxButton();
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);
Wt::WContainerWidget *addSearchItemContainer(Wt::WVBoxLayout *layout, std::string label);
void openInbox();
bool isNickAllowed(const std::string &nick);
bool compareJsonObjects(const Wt::Json::Object &obj1, const Wt::Json::Object &obj2);
void requestConversation(std::string conversationWith);
void showConversation(Wt::Json::Object data);
void sendMessage(Wt::WLineEdit *inputLine);
void renderConversation(Wt::Json::Object conversation);
void showUnreadMessages(Wt::Json::Object data);
void showOpenInbox(Wt::Json::Object data);
void updateUserinfo(Wt::Json::Object data);
std::unique_ptr<Wt::WContainerWidget> createSmileysBar(Wt::WLineEdit *inputLine, std::shared_ptr<int> cursorPosition);
void toggleSmileysBar(Wt::WContainerWidget *smileyBar);
void systemEvent(Wt::Json::Object broadcast);
void addStartChatButton(Wt::WGridLayout *contentGrid, Wt::WLineEdit *userName, Wt::WComboBox *country, Wt::WSpinBox *age, Wt::WComboBox *gender);
Wt::WComboBox *addCountrySelection(Wt::WGridLayout *contentGrid);
Wt::WSpinBox *addAgeInput(Wt::WGridLayout *contentGrid);
Wt::WLineEdit *addUsernameInput(Wt::WGridLayout *contentGrid);
Wt::WComboBox *addGenderSelection(Wt::WGridLayout *contentGrid);
void createLoginContainer();
void connectToServer();
void setUserData(const std::string &nick, Wt::WComboBox *countryWidget, Wt::WSpinBox *ageWidget, Wt::WComboBox *genderWidget);
void validateAge(Wt::WSpinBox *ageWidget);
bool isGenderSelected(Wt::WComboBox *genderWidget);
void validateGender(Wt::WComboBox *genderWidget);
bool isNameAlreadyInUse(const std::string &nick);
bool isInvalidName(const std::string &nick);
void validateName(const std::string &nick);
std::string extractTrimmedUserName(Wt::WLineEdit *userName);
Wt::WPushButton *createSendButton(Wt::WHBoxLayout *inputLayout, Wt::WLineEdit *inputLine);
Wt::WContainerWidget *createSmileyBar(Wt::WContainerWidget *parent, Wt::WLineEdit *inputLine, std::shared_ptr<int> cursorPosition);
Wt::WContainerWidget *createSmileyButton(Wt::WHBoxLayout *inputLayout, Wt::WLineEdit *inputLine, std::shared_ptr<int> cursorPosition);
Wt::WImage *createSendImageButton(Wt::WHBoxLayout *inputLayout);
Wt::WLineEdit *createInputLine(Wt::WHBoxLayout *inputLayout);
Wt::WContainerWidget *createInputContainer(Wt::WVBoxLayout *layout);
std::unique_ptr<Wt::WText> createInfoText(Wt::Json::Object userData);
Wt::WContainerWidget *createInfoWidget(Wt::WVBoxLayout *layout, Wt::Json::Object userData);
void setupConversationUI(Wt::Json::Object userData);
bool shouldShowConversation(Wt::Json::Object userData);
Wt::Json::Object extractUserData(Wt::Json::Object data);
std::unique_ptr<Wt::WPushButton> createBlockButton(Wt::Json::Object userData);
std::string replaceSmileys(std::string outputText);
void renderChatLine(Wt::Json::Object &line, Wt::Json::Object conversation, Wt::WContainerWidget *outputContainer);
std::string getChatLineWriter(Wt::Json::Object &line, Wt::Json::Object conversation);
void renderChatLines(Wt::Json::Object conversation, Wt::WContainerWidget *outputContainer);
void updateOutputContainer(Wt::Json::Object conversation);
void handleException(const std::exception &e);
void processXmlNode(xmlNodePtr node);
void parseXmlDocument(xmlDocPtr doc);
void processCurlResponse();
void performCurlRequest(CURL *curl);
void setCurlOptions(CURL *curl, const std::string &apiUrl);
std::string buildApiUrl(const std::string &userIP);
std::string getUserIP();
Wt::WWebWidget *createImageElement(Wt::Json::Object &line, const std::string &writer, Wt::WContainerWidget *outputContainer, std::string id);
Wt::WWebWidget *createTextElement(const std::string &writer, const std::string &text, Wt::WContainerWidget *outputContainer, std::string id);
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::WContainerWidget *countryOpenList, Wt::WContainerWidget *gendersOpenList);
Wt::WLineEdit *setupNameSearchField(Wt::WVBoxLayout *contentLayout);
std::pair<Wt::WContainerWidget *, Wt::WContainerWidget *> setupGendersDropDown(Wt::WVBoxLayout *contentLayout);
std::pair<Wt::WContainerWidget *, 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::WContainerWidget *openButton, std::unordered_set<std::string> *saveItems);
void addItem(const std::string &country, Wt::WContainerWidget *dropDownContainer, Wt::WContainerWidget *container, 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);
void extendSearchResultIfNeeded(Wt::Json::Object broadcast);
void removeUserFromSearch(Wt::Json::Object broadcast);
void requestHistory();
void showHistory(Wt::Json::Object broadcast);
void connectionTimedOut();
void addLoginTimeView();
void addTimeoutView();
void showPartnerSites();
void sendImage();
void imageUploaded(Wt::WFileUpload *fileWidget, std::shared_ptr<Magick::Blob> localImage, Wt::WImage *image, Wt::WPushButton *okButton);
std::list<Magick::Image> resizeImages(std::list<Magick::Image>& images, int maxWidth, int maxHeight);
bool isAnimatedGIF(const Magick::Blob &blob);
void showStandardPage();
void onInternalPathChanged(const std::string &path);
void showAdminPage(std::string page);
void showAdminLogin(std::string page);
void showPageNotExists();
void showAdminLogins();
void showAdminStarts();
};
#endif // APP_H