aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------depends/odhtdb0
-rw-r--r--include/Channel.hpp9
-rw-r--r--include/MessageBoard.hpp1
-rw-r--r--src/Channel.cpp67
-rw-r--r--src/MessageBoard.cpp34
-rw-r--r--src/main.cpp251
6 files changed, 302 insertions, 60 deletions
diff --git a/depends/odhtdb b/depends/odhtdb
-Subproject 670e3eed2703dcee1dee0508e45d8454cae7854
+Subproject 8da5ba7f057978b224868028679cbda9e11089a
diff --git a/include/Channel.hpp b/include/Channel.hpp
index 5650eb1..be6e7c7 100644
--- a/include/Channel.hpp
+++ b/include/Channel.hpp
@@ -7,6 +7,7 @@
#include <vector>
#include <odhtdb/DatabaseNode.hpp>
#include <odhtdb/Signature.hpp>
+#include <odhtdb/Group.hpp>
namespace odhtdb
{
@@ -20,18 +21,24 @@ namespace dchat
public:
Channel(const std::string &name, const odhtdb::DatabaseNode &databaseNodeInfo = odhtdb::DatabaseNode(), User *localUser = nullptr, odhtdb::Database *database = nullptr);
virtual ~Channel();
+ Channel(const Channel& other) = delete;
+ Channel& operator = (const Channel &other) = delete;
User* getLocalUser();
+ SystemUser* getSystemUser();
MessageBoard& getMessageBoard();
const std::string& getName() const;
const std::vector<User*> getUsers() const;
User* getUserByPublicKey(const odhtdb::Signature::PublicKey &publicKey);
+ std::shared_ptr<odhtdb::Hash> getId();
// If timestamp is 0, then timestamp is not used
void addLocalMessage(const std::string &msg, User *owner, u64 timestampSeconds = 0);
void addMessage(const std::string &msg);
- void addUser(User *user);
+ void addUserLocally(User *user);
+ bool addUser(const odhtdb::Signature::PublicKey &userId, const std::string &groupId);
+ void replaceLocalUser(User *newLocalUser);
void processEvent(const sf::Event &event);
void draw(sf::RenderWindow &window, Cache &cache);
diff --git a/include/MessageBoard.hpp b/include/MessageBoard.hpp
index 739f161..e84396d 100644
--- a/include/MessageBoard.hpp
+++ b/include/MessageBoard.hpp
@@ -36,5 +36,6 @@ namespace dchat
double scrollSpeed;
sf::Clock frameTimer;
double totalHeight;
+ bool scrollToBottom;
};
}
diff --git a/src/Channel.cpp b/src/Channel.cpp
index 748be49..b922833 100644
--- a/src/Channel.cpp
+++ b/src/Channel.cpp
@@ -16,7 +16,7 @@ namespace dchat
messageBoard(sf::Vector2u(1.0f, 1.0f)),
localUser(_localUser ? _localUser : new OfflineUser("You"))
{
- addUser(localUser);
+ addUserLocally(localUser);
{
Message *message = new Message(&systemUser, u8"hello, worldåäö1![emoji](https://discordemoji.com/assets/emoji/playtime.png)");
messageBoard.addMessage(message);
@@ -72,6 +72,11 @@ namespace dchat
return localUser;
}
+ SystemUser* Channel::getSystemUser()
+ {
+ return &systemUser;
+ }
+
MessageBoard& Channel::getMessageBoard()
{
return messageBoard;
@@ -95,6 +100,11 @@ namespace dchat
return nullptr;
}
+ std::shared_ptr<odhtdb::Hash> Channel::getId()
+ {
+ return databaseNodeInfo.getRequestHash();
+ }
+
void Channel::addLocalMessage(const std::string &msg, User *owner, u64 timestampSeconds)
{
assert(owner);
@@ -106,16 +116,16 @@ namespace dchat
if(database && localUser->type == User::Type::ONLINE)
{
addLocalMessage(msg, localUser, database->getSyncedTimestampUtc().seconds);
- auto onlineUser = static_cast<OnlineUser*>(localUser);
- assert(onlineUser->databaseUser->getType() == odhtdb::User::Type::LOCAL);
- database->addData(databaseNodeInfo, static_cast<const odhtdb::LocalUser*>(onlineUser->databaseUser), odhtdb::DataView((void*)msg.data(), msg.size()));
+ auto localOnlineUser = static_cast<OnlineUser*>(localUser);
+ assert(localOnlineUser->databaseUser->getType() == odhtdb::User::Type::LOCAL);
+ database->addData(databaseNodeInfo, static_cast<const odhtdb::LocalUser*>(localOnlineUser->databaseUser), odhtdb::DataView((void*)msg.data(), msg.size()));
database->commit();
}
else
addLocalMessage(msg, localUser, 0);
}
- void Channel::addUser(User *user)
+ void Channel::addUserLocally(User *user)
{
users.push_back(user);
if(user->type == User::Type::ONLINE)
@@ -125,6 +135,53 @@ namespace dchat
}
}
+ bool Channel::addUser(const odhtdb::Signature::PublicKey &userId, const string &groupId)
+ {
+ assert(database);
+ if(!database || localUser->type != User::Type::ONLINE)
+ return false;
+
+ if(groupId.size() != odhtdb::GROUP_ID_LENGTH)
+ {
+ fprintf(stderr, "Group id is wrong size. Expected to be %u bytes, was %u byte(s)\n", odhtdb::GROUP_ID_LENGTH, groupId.size());
+ return false;
+ }
+
+ auto localOnlineUser = static_cast<OnlineUser*>(localUser);
+ assert(localOnlineUser->databaseUser->getType() == odhtdb::User::Type::LOCAL);
+
+ uint8_t groupIdRaw[odhtdb::GROUP_ID_LENGTH];
+ memcpy(groupIdRaw, groupId.data(), groupId.size());
+ auto groupToAddUserTo = database->getStorage().getGroupById(*databaseNodeInfo.getRequestHash(), groupIdRaw);
+ if(!groupToAddUserTo)
+ {
+ fprintf(stderr, "Group with id %s does not exist in channel %s\n", groupId.c_str(), databaseNodeInfo.getRequestHash()->toString().c_str());
+ return false;
+ }
+ database->addUser(databaseNodeInfo, static_cast<const odhtdb::LocalUser*>(localOnlineUser->databaseUser), "noname", userId, groupToAddUserTo);
+
+ auto addedUser = database->getStorage().getUserByPublicKey(*databaseNodeInfo.getRequestHash(), userId);
+ assert(addedUser);
+ addUserLocally(new OnlineUser(addedUser));
+ return true;
+ }
+
+ void Channel::replaceLocalUser(User *newLocalUser)
+ {
+ for(vector<User*>::iterator it = users.begin(); it != users.end(); ++it)
+ {
+ if(*it == localUser)
+ {
+ users.erase(it);
+ delete localUser;
+ break;
+ }
+ }
+
+ localUser = newLocalUser;
+ users.push_back(newLocalUser);
+ }
+
void Channel::processEvent(const sf::Event &event)
{
chatbar.processEvent(event, this);
diff --git a/src/MessageBoard.cpp b/src/MessageBoard.cpp
index bcdf5c7..031bbdf 100644
--- a/src/MessageBoard.cpp
+++ b/src/MessageBoard.cpp
@@ -61,7 +61,8 @@ namespace dchat
leftMouseButtonPressed(false),
scroll(0.0),
scrollSpeed(0.0),
- totalHeight(0.0)
+ totalHeight(0.0),
+ scrollToBottom(false)
{
updateStaticContentTexture(size);
}
@@ -85,6 +86,7 @@ namespace dchat
{
messages.push_back(message);
dirty = true;
+ scrollToBottom = true;
}
void MessageBoard::processEvent(const sf::Event &event)
@@ -118,7 +120,7 @@ namespace dchat
}
else if(event.type == sf::Event::MouseWheelScrolled && event.mouseWheelScroll.wheel == sf::Mouse::Wheel::VerticalWheel)
{
- scrollSpeed += (event.mouseWheelScroll.delta * 30.0);
+ scrollSpeed += (event.mouseWheelScroll.delta * 5.0);
}
if(selectingText && !leftMouseButtonPressed)
@@ -186,9 +188,10 @@ namespace dchat
{
time_t time = (time_t)message->timestampSeconds;
struct tm *localTimePtr = localtime(&time);
- char *timeStr = asctime(localTimePtr);
+ char date[30];
+ strftime(date, sizeof(date), "%Y-%m-%d at %T", localTimePtr);
- sf::Text timestamp(timeStr, *timestampFont, timestampTextCharacterSize);
+ sf::Text timestamp(date, *timestampFont, timestampTextCharacterSize);
timestamp.setFillColor(ColorScheme::getTextRegularColor() * sf::Color(255, 255, 255, 30));
timestamp.setPosition(sf::Vector2f(floor(position.x + PADDING_SIDE + usernameText.getLocalBounds().width + USERNAME_TIMESTAMP_SIDE_PADDING * Settings::getScaling()), floor(position.y + 2.0f * Settings::getScaling() + usernameTextHeight * 0.5f - timestampTextHeight * 0.5f)));
window.draw(timestamp);
@@ -216,7 +219,19 @@ namespace dchat
}
scroll += scrollSpeed;
- scrollSpeed /= (deltaTimeMicro * 0.0001);
+
+ double deltaTimeScrollMultiplier = deltaTimeMicro * 0.00004;
+ if(scrollSpeed > 0.0)
+ {
+ scrollSpeed -= deltaTimeScrollMultiplier;
+ }
+ else
+ {
+ scrollSpeed += deltaTimeScrollMultiplier;
+ }
+
+ if(abs(scrollSpeed - deltaTimeScrollMultiplier) <= deltaTimeScrollMultiplier)
+ scrollSpeed = 0.0;
double textOverflow = backgroundSize.y - totalHeight;
if(scroll > 0.0 || textOverflow > 0.0)
@@ -230,6 +245,15 @@ namespace dchat
scrollSpeed = 0.0;
}
+ if(scrollToBottom)
+ {
+ scrollToBottom = false;
+ if(textOverflow < 0.0)
+ scroll = textOverflow;
+ else
+ scroll = 0.0;
+ }
+
//staticContentTexture.display();
dirty = false;
diff --git a/src/main.cpp b/src/main.cpp
index 364b2a9..7245b6e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -47,6 +47,19 @@ odhtdb::DatabaseNode createDatabaseNodeFromJoinKey(const string &joinKey)
return result;
}
+void channelAddStoredMessage(Channel *channel, odhtdb::DatabaseStorageObject *nodeStorageObject)
+{
+ User *user = channel->getUserByPublicKey(nodeStorageObject->creatorPublicKey);
+ if(!user)
+ {
+ fprintf(stderr, "Missing user? %s\n", nodeStorageObject->creatorPublicKey.toString().c_str());
+ return;
+ }
+
+ string msg((const char*)nodeStorageObject->decryptedObject.data.data, nodeStorageObject->decryptedObject.data.size);
+ channel->addLocalMessage(msg, user, ntp::NtpTimestamp::fromCombined(nodeStorageObject->createdTimestamp).seconds);
+}
+
void channelAddStoredMessages(Channel *channel, const odhtdb::DatabaseStorageObjectList *nodeStorage)
{
printf("Load %u messages in channel %s\n", nodeStorage->objects.size(), channel->getName().c_str());
@@ -54,14 +67,7 @@ void channelAddStoredMessages(Channel *channel, const odhtdb::DatabaseStorageObj
{
if(nodeStorageAddedObject->decryptedObject.operation == odhtdb::DatabaseOperation::ADD_DATA)
{
- User *user = channel->getUserByPublicKey(nodeStorageAddedObject->creatorPublicKey);
- if(!user)
- {
- fprintf(stderr, "Missing user? %s\n", nodeStorageAddedObject->creatorPublicKey.toString().c_str());
- continue;
- }
- string msg((const char*)nodeStorageAddedObject->decryptedObject.data.data, nodeStorageAddedObject->decryptedObject.data.size);
- channel->addLocalMessage(msg, user, ntp::NtpTimestamp::fromCombined(nodeStorageAddedObject->createdTimestamp).seconds);
+ channelAddStoredMessage(channel, nodeStorageAddedObject);
}
}
}
@@ -75,27 +81,16 @@ int main(int argc, char **argv)
boost::filesystem::current_path(parentPath); // Ensures loading of resources works no matter which path we run this executable from
*/
+ const int FRAMERATE_FOCUSED = 200;
+ const int FRAMERATE_NOT_FOCUSED = 30;
+
XInitThreads();
sf::RenderWindow window(sf::VideoMode(1920, 1080), "dchat");
window.setVerticalSyncEnabled(false);
- window.setFramerateLimit(60);
+ window.setFramerateLimit(FRAMERATE_FOCUSED);
odhtdb::Database database("bootstrap.ring.cx", 4222, Cache::getDchatDir());
- database.setOnCreateNodeCallback([](const odhtdb::DatabaseCreateNodeRequest &request)
- {
-
- });
-
- database.setOnAddNodeCallback([](const odhtdb::DatabaseAddNodeRequest &request)
- {
-
- });
-
- database.setOnAddUserCallback([](const odhtdb::DatabaseAddUserRequest &request)
- {
-
- });
//Video video(500, 500, "https://www.youtube.com/watch?v=bs0-EX9mJmg");
Cache cache;
@@ -108,10 +103,89 @@ int main(int argc, char **argv)
odhtdb::Signature::KeyPair *currentUserKeyPair = nullptr;
vector<odhtdb::NodeLocalUser> localNodeUsers;
+ vector<odhtdb::DatabaseNode> waitingToJoinChannels;
string currentUserName;
string currentUserPassword;
+ recursive_mutex channelMessageMutex;
+
+ database.setOnCreateNodeCallback([&waitingToJoinChannels, &database, &channels, &channelMessageMutex](const odhtdb::DatabaseCreateNodeRequest &request)
+ {
+ lock_guard<recursive_mutex> lock(channelMessageMutex);
+ for(vector<odhtdb::DatabaseNode>::iterator it = waitingToJoinChannels.begin(); it != waitingToJoinChannels.end(); ++it)
+ {
+ if(*request.nodeHash == *it->getRequestHash())
+ {
+ User *localUser = new OfflineUser("You");
+ Channel *channel = new Channel(request.name, *it, localUser, &database);
+ ChannelSidePanel::addChannel(channel);
+ channels.push_back(channel);
+ Channel::setCurrent(channel);
+
+ User *nodeCreatorUser = new OnlineUser(request.creatorUser);
+ channel->addUserLocally(nodeCreatorUser);
+
+ waitingToJoinChannels.erase(it);
+ return;
+ }
+ }
+ });
+
+ database.setOnAddNodeCallback([&channels, &channelMessageMutex](const odhtdb::DatabaseAddNodeRequest &request)
+ {
+ lock_guard<recursive_mutex> lock(channelMessageMutex);
+ for(Channel *channel : channels)
+ {
+ if(*request.nodeHash == *channel->getId())
+ {
+ User *user = channel->getUserByPublicKey(request.creatorUser->getPublicKey());
+ if(!user)
+ {
+ fprintf(stderr, "Missing user? %s\n", request.creatorUser->getPublicKey().toString().c_str());
+ return;
+ }
+
+ string msg((const char*)request.decryptedData.data, request.decryptedData.size);
+ channel->addLocalMessage(msg, user, ntp::NtpTimestamp::fromCombined(request.timestamp).seconds);
+ return;
+ }
+ }
+ });
+
+ database.setOnAddUserCallback([&currentUserKeyPair, &channels, &channelMessageMutex](const odhtdb::DatabaseAddUserRequest &request)
+ {
+ lock_guard<recursive_mutex> lock(channelMessageMutex);
+ if(currentUserKeyPair && request.userToAdd->getPublicKey() == currentUserKeyPair->getPublicKey())
+ {
+ printf("You were added to channel %s by %s\n", request.nodeHash->toString().c_str(), request.creatorUser->getName().c_str());
+ return;
+ }
+
+ for(Channel *channel : channels)
+ {
+ if(*request.nodeHash == *channel->getId())
+ {
+ User *userToAdd = channel->getUserByPublicKey(request.userToAdd->getPublicKey());
+ if(userToAdd && currentUserKeyPair && request.userToAdd->getPublicKey() == currentUserKeyPair->getPublicKey() && channel->getLocalUser()->type != User::Type::ONLINE)
+ {
+ channel->replaceLocalUser(new OnlineUser(request.userToAdd));
+ return;
+ }
+
+ if(userToAdd)
+ {
+ fprintf(stderr, "User %s already exists in channel\n", request.userToAdd->getPublicKey().toString().c_str());
+ return;
+ }
+
+ User *newRemoteUser = new OnlineUser(request.userToAdd);
+ channel->addUserLocally(newRemoteUser);
+ return;
+ }
+ }
+ });
- Command::add("login", [&currentUserKeyPair, &currentUserName, &currentUserPassword, &localNodeUsers, &database, &channels](const vector<string> &args)
+ // Login to account
+ Command::add("login", [&currentUserKeyPair, &currentUserName, &currentUserPassword, &localNodeUsers, &database, &channels, &channelMessageMutex](const vector<string> &args)
{
if(args.size() != 2)
{
@@ -131,6 +205,7 @@ int main(int argc, char **argv)
}
channels.clear();
+ lock_guard<recursive_mutex> lock(channelMessageMutex);
for(auto localNodeUser : localNodeUsers)
{
auto nodeStorage = database.getStorage().getStorage(localNodeUser.nodeHash);
@@ -149,7 +224,7 @@ int main(int argc, char **argv)
if(nodeUserIt.second != localNodeUser.localUser)
{
User *newRemoteUser = new OnlineUser(nodeUserIt.second);
- channel->addUser(newRemoteUser);
+ channel->addUserLocally(newRemoteUser);
}
}
@@ -173,8 +248,10 @@ int main(int argc, char **argv)
}
});
- Command::add("register", [&currentUserKeyPair, &currentUserName, &currentUserPassword, &localNodeUsers, &database](const vector<string> &args)
+ // Register account
+ Command::add("register", [&currentUserKeyPair, &currentUserName, &currentUserPassword, &localNodeUsers, &database, &channelMessageMutex](const vector<string> &args)
{
+ lock_guard<recursive_mutex> lock(channelMessageMutex);
if(args.size() != 2)
{
fprintf(stderr, "Expected 2 arguments for command register (username and password), got %u argument(s)\n", args.size());
@@ -206,8 +283,10 @@ int main(int argc, char **argv)
currentUserPassword = args[1];
});
- Command::add("cc", [&currentUserKeyPair, &currentUserName, &database, &channels](const vector<string> &args)
+ // Create channel
+ Command::add("cc", [&currentUserKeyPair, &currentUserName, &database, &channels, &channelMessageMutex](const vector<string> &args)
{
+ lock_guard<recursive_mutex> lock(channelMessageMutex);
if(args.size() != 1)
{
fprintf(stderr, "Expected 1 argument for command cc (channel name), got %u argument(s)\n", args.size());
@@ -232,8 +311,53 @@ int main(int argc, char **argv)
Channel::setCurrent(channel);
});
- Command::add("jc", [&currentUserKeyPair, &database, &localNodeUsers](const vector<string> &args)
+ // Add user
+ Command::add("au", [&offlineChannel, &channelMessageMutex](const vector<string> &args)
{
+ lock_guard<recursive_mutex> lock(channelMessageMutex);
+ if(args.size() != 2)
+ {
+ fprintf(stderr, "Expected 2 arguments for command au (user id, group id), got %u argument(s)\n", args.size());
+ return;
+ }
+
+ if(args[0].size() != odhtdb::PUBLIC_KEY_NUM_BYTES * 2)
+ {
+ fprintf(stderr, "User id is wrong size. Expected to be %u characters, was %u character(s)\n", odhtdb::PUBLIC_KEY_NUM_BYTES * 2, args[0].size());
+ return;
+ }
+
+ if(args[1].size() != odhtdb::GROUP_ID_LENGTH * 2)
+ {
+ fprintf(stderr, "Group id is wrong size. Expected to be %u characters, was %u character(s)\n", odhtdb::GROUP_ID_LENGTH * 2, args[1].size());
+ return;
+ }
+
+ Channel *currentChannel = Channel::getCurrent();
+ if(currentChannel == &offlineChannel)
+ {
+ Channel::getCurrent()->addLocalMessage("You need to be in a channel to add user to the channel", Channel::getCurrent()->getSystemUser());
+ return;
+ }
+
+ auto userIdRaw = odhtdb::hex2bin(args[0].c_str(), args[0].size());
+ odhtdb::Signature::PublicKey userPublicKey(userIdRaw.data(), userIdRaw.size());
+
+ auto groupIdRaw = odhtdb::hex2bin(args[1].c_str(), args[1].size());
+ bool userAddResult = currentChannel->addUser(userPublicKey, groupIdRaw);
+ if(userAddResult)
+ {
+ printf("Added user to your channel!\n");
+ }
+ else
+ {
+ fprintf(stderr, "Failed to add user to your channel!\n");
+ }
+ });
+
+ Command::add("jc", [&currentUserKeyPair, &database, &localNodeUsers, &channelMessageMutex](const vector<string> &args)
+ {
+ lock_guard<recursive_mutex> lock(channelMessageMutex);
if(args.size() != 1)
{
fprintf(stderr, "Expected 1 argument for command jc (channel join key), got %u argument(s)\n", args.size());
@@ -261,28 +385,15 @@ int main(int argc, char **argv)
return;
}
}
-#if 0
- User *newLocalUser = new OnlineUser(localNodeUser.localUser);
- odhtdb::DatabaseNode databaseNode(nodeDecryptionKeyResult.second, make_shared<odhtdb::Hash>(localNodeUser.nodeHash));
- Channel *channel = new Channel(nodeStorage->nodeName, databaseNode, newLocalUser, &database);
-
- auto nodeUserMapByPublicKey = database.getStorage().getNodeUsers(localNodeUser.nodeHash);
- for(auto nodeUserIt : *nodeUserMapByPublicKey)
- {
- if(nodeUserIt.second != localNodeUser.localUser)
- {
- User *newRemoteUser = new OnlineUser(nodeUserIt.second);
- channel->addUser(newRemoteUser);
- }
- }
- ChannelSidePanel::addChannel(channel);
- channels.push_back(channel);
- Channel::setCurrent(channel);
- channelAddStoredMessages(channel, nodeStorage);
-#endif
+ database.seed(databaseNode);
+ // TODO: Add the channel to join to a pending join list in a file and remove from it when we have joined the channel.
+ // The reason for doing that is so if we crash or lose internet connection before we have got `create node` request from remote peers,
+ // then we need to start seeding again when we login. Once we have `create node` request, then it's added to local cache and when you login,
+ // it will be used to seed the channel.
});
+ // Scale UI
Command::add("scale", [](const vector<string> &args)
{
if(args.size() != 1)
@@ -296,9 +407,31 @@ int main(int argc, char **argv)
printf("UI scaling set to %f\n", scaling);
});
+ // Get username and id (public key)
+ Command::add("whoami", [&currentUserKeyPair, &currentUserName](const vector<string> &args)
+ {
+ if(!currentUserKeyPair)
+ {
+ Channel::getCurrent()->addLocalMessage("You are not logged in", Channel::getCurrent()->getSystemUser());
+ return;
+ }
+
+ string response = "Username: ";
+ response += currentUserName;
+ response += ", id: ";
+ response += currentUserKeyPair->getPublicKey().toString();
+ printf("%s\n", response.c_str());
+ Channel::getCurrent()->addLocalMessage(response, Channel::getCurrent()->getSystemUser());
+ });
+
sf::Event event;
while (window.isOpen())
{
+ channelMessageMutex.lock();
+ Channel *currentChannel = Channel::getCurrent();
+ bool waitingToJoin = currentChannel != &offlineChannel && currentChannel->getLocalUser()->type != User::Type::ONLINE;
+ channelMessageMutex.unlock();
+
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
@@ -316,14 +449,34 @@ int main(int argc, char **argv)
sf::View view(viewRect);
window.setView(view);
}
- Channel::getCurrent()->processEvent(event);
+ else if(event.type == sf::Event::GainedFocus)
+ window.setFramerateLimit(FRAMERATE_FOCUSED);
+ else if(event.type == sf::Event::LostFocus)
+ window.setFramerateLimit(FRAMERATE_NOT_FOCUSED);
+ currentChannel->processEvent(event);
}
window.clear(ColorScheme::getBackgroundColor());
ChannelSidePanel::draw(window);
- Channel::getCurrent()->draw(window, cache);
+ currentChannel->draw(window, cache);
UsersSidePanel::draw(window);
ChannelTopPanel::draw(window);
+
+ if(waitingToJoin)
+ {
+ auto windowSize = window.getSize();
+
+ sf::RectangleShape shadeRect(sf::Vector2f(windowSize.x, windowSize.y));
+ shadeRect.setFillColor(sf::Color(0, 0, 0, 200));
+ window.draw(shadeRect);
+
+ const sf::Font *FONT = ResourceCache::getFont("fonts/Roboto-Regular.ttf");
+ const float FONT_SIZE = 30 * Settings::getScaling();
+ sf::Text text("Wait until you are added to the channel", *FONT, FONT_SIZE);
+ text.setPosition(floor((float)windowSize.x * 0.5f - text.getLocalBounds().width * 0.5f), floor((float)windowSize.y * 0.5f - FONT->getLineSpacing(FONT_SIZE) * 0.5f));
+ window.draw(text);
+ }
+
//video.draw(window);
window.display();
}