diff --git a/docroot/style.css b/docroot/style.css index 072f5f4..8a3295b 100644 --- a/docroot/style.css +++ b/docroot/style.css @@ -177,3 +177,11 @@ main { .flag-icon { margin: 0.25em 0.5em 0 0; } +.menu-info-text { + display: inline-block; + padding: 0.375em 0.4em; + color: #2E7D32; + border: 1px solid #fff; + background-color: lightgray; + margin: 0.1em 0.2em; +} diff --git a/src/app.cpp b/src/app.cpp index e972ac6..4937a92 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -36,6 +36,7 @@ #include #include #include +#include App::App(const Wt::WEnvironment &env, Broadcast &server): Wt::WApplication(env), @@ -786,6 +787,8 @@ void App::createMenu() { addSearchButton(); addInboxButton(); addHistoryButton(); + addLoginTimeView(); + addTimeoutView(); } void App::addLeaveButton() { @@ -833,6 +836,7 @@ void App::addInboxButton() { } void App::showSearchWindow() { + setActivity(); auto contentLayout = resetSearchFields(); auto userNameField = setupNameSearchField(contentLayout); auto ageSearchFields = setupSearchFields(contentLayout); @@ -975,6 +979,7 @@ void App::removeUserFromSearch(Wt::Json::Object) { } void App::requestHistory() { + setActivity(); server_.sendHistory(sessionId()); } @@ -1000,6 +1005,51 @@ void App::showHistory(Wt::Json::Object broadcast) { triggerUpdate(); } +void App::connectionTimedOut() { + showLogin(); + triggerUpdate(); +} + +void App::addLoginTimeView() { + auto loggedinTimeWidget = menuContainer_->addNew(); + loggedinTimeWidget->setStyleClass("menu-info-text"); + auto loginTimer = root()->addChild(std::make_unique()); + loginTimer->setInterval(std::chrono::seconds(1)); + auto loggedInRefresh = [=, this]() { + auto currentLoginSeconds = currentlyLoggedInSeconds(); + int hours = currentLoginSeconds / 3600; + int minutes = (currentLoginSeconds % 3600) / 60; + std::stringstream elapsedTimeStream; + elapsedTimeStream << std::setw(2) << std::setfill('0') << hours << ":" + << std::setw(2) << std::setfill('0') << minutes << " h"; + std::string elapsedTimeString = elapsedTimeStream.str(); + loggedinTimeWidget->setText(Wt::WString("In chat for {1}").arg(elapsedTimeString)); + }; + loginTimer->timeout().connect(loggedInRefresh); + loggedInRefresh(); + loginTimer->start(); +} + +void App::addTimeoutView() { + auto timeoutRemainingWidget = menuContainer_->addNew(); + timeoutRemainingWidget->setStyleClass("menu-info-text"); + auto timeoutRemainingTimer = root()->addChild(std::make_unique()); + timeoutRemainingTimer->setInterval(std::chrono::milliseconds(500)); + auto timeoutRemainingRefresh = [=, this]() { + auto remainingLoginSeconds = remainingSecondsToTimeout(); + int minutes = remainingLoginSeconds / 60; + int seconds = (remainingLoginSeconds % 60); + std::stringstream remainingTimeStream; + remainingTimeStream << std::setw(2) << std::setfill('0') << minutes << ":" + << std::setw(2) << std::setfill('0') << seconds << " m"; + std::string elapsedTimeString = remainingTimeStream.str(); + timeoutRemainingWidget->setText(Wt::WString("Remaining time before logout is {1}").arg(elapsedTimeString)); + }; + timeoutRemainingTimer->timeout().connect(timeoutRemainingRefresh); + timeoutRemainingRefresh(); + timeoutRemainingTimer->start(); +} + void App::itemChanged(Wt::WCheckBox *item, Wt::WContainerWidget *dropDownContainer, Wt::WPushButton *openButton, std::unordered_set *saveItems) { saveItems->clear(); bool unselect = (item->text() == "All" && item->isChecked()); @@ -1093,6 +1143,7 @@ void App::showSearch(Wt::Json::Object broadcast) { } void App::openInbox() { + setActivity(); currentConversationWith_ = ""; contentContainer_->clear(); contentContainer_->addNew("

Inbox

"); @@ -1128,6 +1179,8 @@ void App::incomingBroadcast() { removeUserFromSearch(broadcast); } else if (broadcast["type"] == "history") { showHistory(broadcast); + } else if (broadcast["type"] == "timedout") { + connectionTimedOut(); } } } @@ -1136,6 +1189,7 @@ void App::startChat() { createMenu(); contentContainer_->clear(); contentContainer_->addNew(Wt::WString::tr("introduction"), Wt::TextFormat::UnsafeXHTML); + setLoggedIn(); } void App::createUserListContainer(Wt::WHBoxLayout *layout) { diff --git a/src/app.h b/src/app.h index 7798d30..786eeb0 100644 --- a/src/app.h +++ b/src/app.h @@ -198,6 +198,9 @@ private: void removeUserFromSearch(Wt::Json::Object broadcast); void requestHistory(); void showHistory(Wt::Json::Object broadcast); + void connectionTimedOut(); + void addLoginTimeView(); + void addTimeoutView(); }; #endif // APP_H diff --git a/src/broadcast.cpp b/src/broadcast.cpp index e6ffc91..5e69faf 100644 --- a/src/broadcast.cpp +++ b/src/broadcast.cpp @@ -59,7 +59,6 @@ void Broadcast::disconnect(Client *client) { ++it; } } - } Wt::Json::Object Broadcast::reSetUser(std::string oldSessionId, std::string newSessionId) { @@ -140,6 +139,10 @@ bool Broadcast::nameIsFree(std::string userNameToCheck) { } void Broadcast::run() { + Wt::Json::Object timedoutBroadcast { + {"type", "timedout"}, + {"data", "dummy"} + }; for (;;) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (stop_) { @@ -149,6 +152,7 @@ void Broadcast::run() { for (auto &connection: connections_) { if (connection->client()->activitiesTimedOut()) { disconnect(connection->client()); + addMessageToSessionBroadcast(connection->sessionId(), timedoutBroadcast); continue; } if (connection->getBroadcasts().size() == 0) { @@ -602,7 +606,12 @@ void Client::setActivity() { bool Client::activitiesTimedOut() { auto timeDifference = Wt::WDateTime::currentDateTime().secsTo(lastActivity_); - return timeDifference > 1800; + return timeDifference > timeoutSeconds; +} + +int Client::currentlyLoggedInSeconds() { + auto currentDateTime = Wt::WDateTime::currentDateTime(); + return loginTimeStamp_.secsTo(currentDateTime); } Wt::Json::Object Client::json() { @@ -615,6 +624,16 @@ Wt::Json::Object Client::json() { }; } +int Client::remainingSecondsToTimeout(){ + auto timeDifference = lastActivity_.secsTo(Wt::WDateTime::currentDateTime()); + return timeoutSeconds - timeDifference; +} + +void Client::setLoggedIn() { + loginTimeStamp_ = Wt::WDateTime::currentDateTime(); + lastActivity_ = loginTimeStamp_; +} + Broadcast::Message::Message(std::string fromSessionId_, Wt::WString message_): fromSessionId(fromSessionId_), sendType("text"), diff --git a/src/broadcast.h b/src/broadcast.h index 5840491..7de4168 100644 --- a/src/broadcast.h +++ b/src/broadcast.h @@ -32,10 +32,17 @@ public: std::unordered_map > conversations; void setActivity(); bool activitiesTimedOut(); + int currentlyLoggedInSeconds(); virtual void incomingBroadcast() { std::cout << "incoming" << std::endl;}; Wt::Json::Object json(); + int remainingSecondsToTimeout(); +protected: + void setLoggedIn(); + void setLoggedOut(); private: + const int timeoutSeconds {1800}; Wt::WDateTime lastActivity_; + Wt::WDateTime loginTimeStamp_; }; class Broadcast {