refactored MessageBuilder

This commit is contained in:
fourtf 2018-08-07 01:35:24 +02:00
parent f71ff08e68
commit c26422aec1
29 changed files with 964 additions and 970 deletions

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@
#include "controllers/ignores/IgnoreController.hpp"
#include "controllers/moderationactions/ModerationActions.hpp"
#include "controllers/taggedusers/TaggedUsersController.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/bttv/BttvEmotes.hpp"
#include "providers/ffz/FfzEmotes.hpp"
#include "providers/twitch/PubsubClient.hpp"
@ -130,7 +131,7 @@ void Application::initPubsub()
QString text =
QString("%1 cleared the chat").arg(action.source.name);
auto msg = Message::createSystemMessage(text);
auto msg = makeSystemMessage(text);
postToThread([chan, msg] { chan->addMessage(msg); });
});
@ -154,7 +155,7 @@ void Application::initPubsub()
" seconds)");
}
auto msg = Message::createSystemMessage(text);
auto msg = makeSystemMessage(text);
postToThread([chan, msg] { chan->addMessage(msg); });
});
@ -176,7 +177,7 @@ void Application::initPubsub()
.arg(action.source.name, action.target.name);
}
auto msg = Message::createSystemMessage(text);
auto msg = makeSystemMessage(text);
postToThread([chan, msg] { chan->addMessage(msg); });
});
@ -189,10 +190,12 @@ void Application::initPubsub()
return;
}
auto msg = Message::createTimeoutMessage(action);
MessageBuilder msg(action);
msg->flags |= Message::PubSub;
postToThread([chan, msg] { chan->addOrReplaceTimeout(msg); });
postToThread([chan, msg = msg.release()] {
chan->addOrReplaceTimeout(msg);
});
});
this->twitch.pubsub->signals_.moderation.userUnbanned.connect(
@ -204,7 +207,7 @@ void Application::initPubsub()
return;
}
auto msg = Message::createUntimeoutMessage(action);
auto msg = MessageBuilder(action).release();
postToThread([chan, msg] { chan->addMessage(msg); });
});

View file

@ -47,29 +47,29 @@ public:
friend void test();
Settings *const settings = nullptr;
Paths *const paths = nullptr;
Settings *const settings{};
Paths *const paths{};
Resources2 *const resources;
Theme *const themes = nullptr;
Fonts *const fonts = nullptr;
Emotes *const emotes = nullptr;
WindowManager *const windows = nullptr;
Theme *const themes{};
Fonts *const fonts{};
Emotes *const emotes{};
WindowManager *const windows{};
AccountController *const accounts = nullptr;
CommandController *const commands = nullptr;
HighlightController *const highlights = nullptr;
IgnoreController *const ignores = nullptr;
TaggedUsersController *const taggedUsers = nullptr;
ModerationActions *const moderationActions = nullptr;
TwitchServer *const twitch2 = nullptr;
AccountController *const accounts{};
CommandController *const commands{};
HighlightController *const highlights{};
IgnoreController *const ignores{};
TaggedUsersController *const taggedUsers{};
ModerationActions *const moderationActions{};
TwitchServer *const twitch2{};
[[deprecated]] Logging *const logging = nullptr;
/*[[deprecated]]*/ Logging *const logging{};
/// Provider-specific
struct {
[[deprecated("use twitch2 instead")]] TwitchServer *server = nullptr;
[[deprecated("use twitch2->pubsub instead")]] PubSub *pubsub = nullptr;
/*[[deprecated("use twitch2 instead")]]*/ TwitchServer *server{};
/*[[deprecated("use twitch2->pubsub instead")]]*/ PubSub *pubsub{};
} twitch;
private:

View file

@ -3,6 +3,7 @@
#include "Application.hpp"
#include "debug/Log.hpp"
#include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Logging.hpp"
#include "singletons/WindowManager.hpp"
@ -124,15 +125,15 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
int count = s->count + 1;
MessagePtr replacement(Message::createSystemMessage(
message->searchText + QString(" (") + QString::number(count) +
" times)"));
MessageBuilder replacement(systemMessage,
message->searchText + QString(" (") +
QString::number(count) + " times)");
replacement->timeoutUser = message->timeoutUser;
replacement->count = count;
replacement->flags = message->flags;
this->replaceMessage(s, replacement);
this->replaceMessage(s, replacement.release());
return;
}
@ -143,7 +144,8 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
auto &s = snapshot[i];
if ((s->flags & (Message::Timeout | Message::Untimeout)) == 0 &&
s->loginName == message->timeoutUser) {
s->flags.EnableFlag(Message::Disabled);
// FOURTF: disabled for now
// s->flags.EnableFlag(Message::Disabled);
}
}
@ -165,7 +167,8 @@ void Channel::disableAllMessages()
continue;
}
s->flags.EnableFlag(Message::Disabled);
// FOURTF: disabled for now
// s->flags.EnableFlag(Message::Disabled);
}
}
@ -188,7 +191,7 @@ void Channel::replaceMessage(MessagePtr message, MessagePtr replacement)
}
}
void Channel::addRecentChatter(const std::shared_ptr<Message> &message)
void Channel::addRecentChatter(const MessagePtr &message)
{
// Do nothing by default
}

View file

@ -52,7 +52,7 @@ public:
void addOrReplaceTimeout(MessagePtr message);
void disableAllMessages();
void replaceMessage(MessagePtr message, MessagePtr replacement);
virtual void addRecentChatter(const std::shared_ptr<Message> &message);
virtual void addRecentChatter(const MessagePtr &message);
QStringList modList;

View file

@ -54,6 +54,24 @@ public:
reinterpret_cast<Q &>(this->value) |= static_cast<Q>(flag);
}
void set(T flag)
{
reinterpret_cast<Q &>(this->value) |= static_cast<Q>(flag);
}
void unset(T flag)
{
reinterpret_cast<Q &>(this->value) &= ~static_cast<Q>(flag);
}
void set(T flag, bool value)
{
if (value)
this->set(flag);
else
this->unset(flag);
}
bool HasFlag(Q flag) const
{
return (this->value & flag) == flag;

View file

@ -141,16 +141,17 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
}
b.emplace<TextElement>(rest, MessageElement::Text);
b.getMessage()->flags |= Message::DoNotTriggerNotification;
b.message().flags |= Message::DoNotTriggerNotification;
auto messagexD = b.release();
app->twitch.server->whispersChannel->addMessage(b.getMessage());
app->twitch.server->whispersChannel->addMessage(messagexD);
app->twitch.server->sendMessage("jtv", text);
if (getSettings()->inlineWhispers) {
app->twitch.server->forEachChannel(
[&b](ChannelPtr _channel) {
_channel->addMessage(b.getMessage());
[&messagexD](ChannelPtr _channel) {
_channel->addMessage(messagexD);
});
}
@ -166,7 +167,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
if (commandName == "/debug-args") {
QString msg = QApplication::instance()->arguments().join(' ');
channel->addMessage(Message::createSystemMessage(msg));
channel->addMessage(makeSystemMessage(msg));
return "";
} else if (commandName == "/uptime") {
@ -176,13 +177,13 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
? streamStatus->uptime
: "Channel is not live.";
channel->addMessage(Message::createSystemMessage(messageText));
channel->addMessage(makeSystemMessage(messageText));
return "";
} else if (commandName == "/ignore") {
if (words.size() < 2) {
channel->addMessage(
Message::createSystemMessage("Usage: /ignore [user]"));
makeSystemMessage("Usage: /ignore [user]"));
return "";
}
auto app = getApp();
@ -191,21 +192,21 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
auto target = words.at(1);
if (user->isAnon()) {
channel->addMessage(Message::createSystemMessage(
channel->addMessage(makeSystemMessage(
"You must be logged in to ignore someone"));
return "";
}
user->ignore(target, [channel](auto resultCode,
const QString &message) {
channel->addMessage(Message::createSystemMessage(message));
});
user->ignore(
target, [channel](auto resultCode, const QString &message) {
channel->addMessage(makeSystemMessage(message));
});
return "";
} else if (commandName == "/unignore") {
if (words.size() < 2) {
channel->addMessage(Message::createSystemMessage(
"Usage: /unignore [user]"));
channel->addMessage(
makeSystemMessage("Usage: /unignore [user]"));
return "";
}
auto app = getApp();
@ -214,21 +215,21 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
auto target = words.at(1);
if (user->isAnon()) {
channel->addMessage(Message::createSystemMessage(
channel->addMessage(makeSystemMessage(
"You must be logged in to ignore someone"));
return "";
}
user->unignore(target, [channel](auto resultCode,
const QString &message) {
channel->addMessage(Message::createSystemMessage(message));
});
user->unignore(
target, [channel](auto resultCode, const QString &message) {
channel->addMessage(makeSystemMessage(message));
});
return "";
} else if (commandName == "/follow") {
if (words.size() < 2) {
channel->addMessage(
Message::createSystemMessage("Usage: /follow [user]"));
makeSystemMessage("Usage: /follow [user]"));
return "";
}
auto app = getApp();
@ -237,7 +238,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
auto target = words.at(1);
if (user->isAnon()) {
channel->addMessage(Message::createSystemMessage(
channel->addMessage(makeSystemMessage(
"You must be logged in to follow someone"));
return "";
}
@ -245,12 +246,12 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
TwitchApi::findUserId(
target, [user, channel, target](QString userId) {
if (userId.isEmpty()) {
channel->addMessage(Message::createSystemMessage(
channel->addMessage(makeSystemMessage(
"User " + target + " could not be followed!"));
return;
}
user->followUser(userId, [channel, target]() {
channel->addMessage(Message::createSystemMessage(
channel->addMessage(makeSystemMessage(
"You successfully followed " + target));
});
});
@ -258,8 +259,8 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
return "";
} else if (commandName == "/unfollow") {
if (words.size() < 2) {
channel->addMessage(Message::createSystemMessage(
"Usage: /unfollow [user]"));
channel->addMessage(
makeSystemMessage("Usage: /unfollow [user]"));
return "";
}
auto app = getApp();
@ -268,7 +269,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
auto target = words.at(1);
if (user->isAnon()) {
channel->addMessage(Message::createSystemMessage(
channel->addMessage(makeSystemMessage(
"You must be logged in to follow someone"));
return "";
}
@ -276,12 +277,12 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
TwitchApi::findUserId(
target, [user, channel, target](QString userId) {
if (userId.isEmpty()) {
channel->addMessage(Message::createSystemMessage(
channel->addMessage(makeSystemMessage(
"User " + target + " could not be followed!"));
return;
}
user->unfollowUser(userId, [channel, target]() {
channel->addMessage(Message::createSystemMessage(
channel->addMessage(makeSystemMessage(
"You successfully unfollowed " + target));
});
});
@ -289,8 +290,8 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
return "";
} else if (commandName == "/logs") {
if (words.size() < 2) {
channel->addMessage(Message::createSystemMessage(
"Usage: /logs [user] (channel)"));
channel->addMessage(
makeSystemMessage("Usage: /logs [user] (channel)"));
return "";
}
auto app = getApp();

View file

@ -6,6 +6,7 @@
#include "debug/Log.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/WindowManager.hpp"
#include "util/DebugCount.hpp"
#include "util/PostToThread.hpp"
#include <QBuffer>

View file

@ -1,20 +1,22 @@
#include "messages/Message.hpp"
#include "MessageElement.hpp"
#include "providers/twitch/PubsubActions.hpp"
#include "util/DebugCount.hpp"
#include "util/IrcHelpers.hpp"
using SBHighlight = chatterino::ScrollbarHighlight;
namespace chatterino {
void Message::addElement(MessageElement *element)
Message::Message()
: parseTime(QTime::currentTime())
{
this->elements_.push_back(std::unique_ptr<MessageElement>(element));
DebugCount::increase("messages");
}
const std::vector<std::unique_ptr<MessageElement>> &Message::getElements() const
Message::~Message()
{
return this->elements_;
DebugCount::decrease("messages");
}
SBHighlight Message::getScrollBarHighlight() const
@ -28,195 +30,8 @@ SBHighlight Message::getScrollBarHighlight() const
}
// Static
MessagePtr Message::createSystemMessage(const QString &text)
{
MessagePtr message(new Message);
message->addElement(new TimestampElement(QTime::currentTime()));
message->addElement(
new TextElement(text, MessageElement::Text, MessageColor::System));
message->flags |= MessageFlags::System;
message->flags |= MessageFlags::DoNotTriggerNotification;
message->searchText = text;
return message;
}
MessagePtr Message::createMessage(const QString &text)
{
MessagePtr message(new Message);
message->addElement(new TimestampElement(QTime::currentTime()));
message->addElement(
new TextElement(text, MessageElement::Text, MessageColor::Text));
message->searchText = text;
return message;
}
namespace {
void appendDuration(int count, QChar &&order, QString &outString)
{
outString.append(QString::number(count));
outString.append(order);
}
QString makeDuration(int timeoutSeconds)
{
QString res;
int seconds = timeoutSeconds % 60;
int timeoutMinutes = timeoutSeconds / 60;
int minutes = timeoutMinutes % 60;
int timeoutHours = timeoutMinutes / 60;
int hours = timeoutHours % 24;
int days = timeoutHours / 24;
if (days > 0) {
appendDuration(days, 'd', res);
}
if (hours > 0) {
if (!res.isEmpty()) {
res.append(" ");
}
appendDuration(hours, 'h', res);
}
if (minutes > 0) {
if (!res.isEmpty()) {
res.append(" ");
}
appendDuration(minutes, 'm', res);
}
if (seconds > 0) {
if (!res.isEmpty()) {
res.append(" ");
}
appendDuration(seconds, 's', res);
}
return res;
}
} // namespace
MessagePtr Message::createTimeoutMessage(const QString &username,
const QString &durationInSeconds,
const QString &reason,
bool multipleTimes)
{
QString text;
text.append(username);
if (!durationInSeconds.isEmpty()) {
text.append(" has been timed out");
// TODO: Implement who timed the user out
text.append(" for ");
bool ok = true;
int timeoutSeconds = durationInSeconds.toInt(&ok);
if (ok) {
text.append(makeDuration(timeoutSeconds));
}
} else {
text.append(" has been permanently banned");
}
if (reason.length() > 0) {
text.append(": \"");
text.append(parseTagString(reason));
text.append("\"");
}
text.append(".");
if (multipleTimes) {
text.append(" (multiple times)");
}
MessagePtr message = Message::createSystemMessage(text);
message->flags.EnableFlag(MessageFlags::System);
message->flags.EnableFlag(MessageFlags::Timeout);
message->timeoutUser = username;
return message;
}
MessagePtr Message::createTimeoutMessage(const BanAction &action,
uint32_t count)
{
MessagePtr msg(new Message);
msg->addElement(new TimestampElement(QTime::currentTime()));
msg->flags.EnableFlag(MessageFlags::System);
msg->flags.EnableFlag(MessageFlags::Timeout);
msg->timeoutUser = action.target.name;
msg->count = count;
QString text;
if (action.isBan()) {
if (action.reason.isEmpty()) {
text = QString("%1 banned %2.") //
.arg(action.source.name)
.arg(action.target.name);
} else {
text = QString("%1 banned %2: \"%3\".") //
.arg(action.source.name)
.arg(action.target.name)
.arg(action.reason);
}
} else {
if (action.reason.isEmpty()) {
text = QString("%1 timed out %2 for %3.") //
.arg(action.source.name)
.arg(action.target.name)
.arg(makeDuration(action.duration));
} else {
text = QString("%1 timed out %2 for %3: \"%4\".") //
.arg(action.source.name)
.arg(action.target.name)
.arg(makeDuration(action.duration))
.arg(action.reason);
}
if (count > 1) {
text.append(QString(" (%1 times)").arg(count));
}
}
msg->addElement(
new TextElement(text, MessageElement::Text, MessageColor::System));
msg->searchText = text;
return msg;
}
MessagePtr Message::createUntimeoutMessage(const UnbanAction &action)
{
MessagePtr msg(new Message);
msg->addElement(new TimestampElement(QTime::currentTime()));
msg->flags.EnableFlag(MessageFlags::System);
msg->flags.EnableFlag(MessageFlags::Untimeout);
msg->timeoutUser = action.target.name;
QString text;
if (action.wasBan()) {
text = QString("%1 unbanned %2.") //
.arg(action.source.name)
.arg(action.target.name);
} else {
text = QString("%1 untimedout %2.") //
.arg(action.source.name)
.arg(action.target.name);
}
msg->addElement(
new TextElement(text, MessageElement::Text, MessageColor::System));
msg->searchText = text;
return msg;
}
} // namespace chatterino

View file

@ -6,27 +6,13 @@
#include "widgets/helper/ScrollbarHighlight.hpp"
#include <QTime>
#include <cinttypes>
#include <memory>
#include <vector>
#include "util/DebugCount.hpp"
namespace chatterino {
struct Message {
Message()
: parseTime(QTime::currentTime())
{
DebugCount::increase("messages");
}
~Message()
{
DebugCount::decrease("messages");
}
struct Message : boost::noncopyable {
enum MessageFlags : uint16_t {
None = 0,
System = (1 << 0),
@ -43,6 +29,9 @@ struct Message {
Subscription = (1 << 11),
};
Message();
~Message();
FlagsEnum<MessageFlags> flags;
QTime parseTime;
QString id;
@ -51,33 +40,12 @@ struct Message {
QString displayName;
QString localizedName;
QString timeoutUser;
uint32_t count = 1;
std::vector<std::unique_ptr<MessageElement>> elements;
// Messages should not be added after the message is done initializing.
void addElement(MessageElement *element);
const std::vector<std::unique_ptr<MessageElement>> &getElements() const;
// Scrollbar
ScrollbarHighlight getScrollBarHighlight() const;
private:
std::vector<std::unique_ptr<MessageElement>> elements_;
public:
static std::shared_ptr<Message> createSystemMessage(const QString &text);
static std::shared_ptr<Message> createMessage(const QString &text);
static std::shared_ptr<Message> createTimeoutMessage(
const QString &username, const QString &durationInSeconds,
const QString &reason, bool multipleTimes);
static std::shared_ptr<Message> createTimeoutMessage(
const BanAction &action, uint32_t count = 1);
static std::shared_ptr<Message> createUntimeoutMessage(
const UnbanAction &action);
};
using MessagePtr = std::shared_ptr<Message>;
using MessagePtr = std::shared_ptr<const Message>;
} // namespace chatterino

View file

@ -4,43 +4,171 @@
#include "singletons/Emotes.hpp"
#include "singletons/Resources.hpp"
#include "singletons/Theme.hpp"
#include "util/FormatTime.hpp"
#include "util/IrcHelpers.hpp"
#include <QDateTime>
namespace chatterino {
MessagePtr makeSystemMessage(const QString &text)
{
return MessageBuilder(systemMessage, text).release();
}
MessageBuilder::MessageBuilder()
: message_(new Message)
: message_(std::make_unique<Message>())
{
}
MessagePtr MessageBuilder::getMessage()
MessageBuilder::MessageBuilder(const QString &text)
: MessageBuilder()
{
return this->message_;
this->emplace<TimestampElement>();
this->emplace<TextElement>(text, MessageElement::Text,
MessageColor::System);
this->message().searchText = text;
}
void MessageBuilder::append(MessageElement *element)
MessageBuilder::MessageBuilder(SystemMessageTag, const QString &text)
: MessageBuilder()
{
this->message_->addElement(element);
this->emplace<TimestampElement>();
this->emplace<TextElement>(text, MessageElement::Text,
MessageColor::System);
this->message().flags |= Message::System;
this->message().flags |= Message::DoNotTriggerNotification;
this->message().searchText = text;
}
void MessageBuilder::appendTimestamp()
MessageBuilder::MessageBuilder(TimeoutMessageTag, const QString &username,
const QString &durationInSeconds,
const QString &reason, bool multipleTimes)
: MessageBuilder()
{
this->appendTimestamp(QTime::currentTime());
}
QString text;
void MessageBuilder::setHighlight(bool value)
{
if (value) {
this->message_->flags |= Message::Highlighted;
text.append(username);
if (!durationInSeconds.isEmpty()) {
text.append(" has been timed out");
// TODO: Implement who timed the user out
text.append(" for ");
bool ok = true;
int timeoutSeconds = durationInSeconds.toInt(&ok);
if (ok) {
text.append(formatTime(timeoutSeconds));
}
} else {
this->message_->flags &= ~Message::Highlighted;
text.append(" has been permanently banned");
}
if (reason.length() > 0) {
text.append(": \"");
text.append(parseTagString(reason));
text.append("\"");
}
text.append(".");
if (multipleTimes) {
text.append(" (multiple times)");
}
this->message().flags.EnableFlag(Message::System);
this->message().flags.EnableFlag(Message::Timeout);
this->message().flags.EnableFlag(Message::DoNotTriggerNotification);
this->message().timeoutUser = username;
}
void MessageBuilder::appendTimestamp(const QTime &time)
MessageBuilder::MessageBuilder(const BanAction &action, uint32_t count)
{
this->append(new TimestampElement(time));
this->emplace<TimestampElement>();
this->message().flags.EnableFlag(Message::System);
this->message().flags.EnableFlag(Message::Timeout);
this->message().timeoutUser = action.target.name;
this->message().count = count;
QString text;
if (action.isBan()) {
if (action.reason.isEmpty()) {
text = QString("%1 banned %2.") //
.arg(action.source.name)
.arg(action.target.name);
} else {
text = QString("%1 banned %2: \"%3\".") //
.arg(action.source.name)
.arg(action.target.name)
.arg(action.reason);
}
} else {
if (action.reason.isEmpty()) {
text = QString("%1 timed out %2 for %3.") //
.arg(action.source.name)
.arg(action.target.name)
.arg(formatTime(action.duration));
} else {
text = QString("%1 timed out %2 for %3: \"%4\".") //
.arg(action.source.name)
.arg(action.target.name)
.arg(formatTime(action.duration))
.arg(action.reason);
}
if (count > 1) {
text.append(QString(" (%1 times)").arg(count));
}
}
this->emplace<TextElement>(text, MessageElement::Text,
MessageColor::System);
this->message().searchText = text;
}
MessageBuilder::MessageBuilder(const UnbanAction &action)
{
this->emplace<TimestampElement>();
this->message().flags.EnableFlag(Message::System);
this->message().flags.EnableFlag(Message::Untimeout);
this->message().timeoutUser = action.target.name;
QString text;
if (action.wasBan()) {
text = QString("%1 unbanned %2.") //
.arg(action.source.name)
.arg(action.target.name);
} else {
text = QString("%1 untimedout %2.") //
.arg(action.source.name)
.arg(action.target.name);
}
this->emplace<TextElement>(text, MessageElement::Text,
MessageColor::System);
this->message().searchText = text;
}
Message *MessageBuilder::operator->()
{
return this->message_.get();
}
Message &MessageBuilder::message()
{
return *this->message_;
}
MessagePtr MessageBuilder::release()
{
return MessagePtr(this->message_.release());
}
void MessageBuilder::append(std::unique_ptr<MessageElement> element)
{
this->message().elements.push_back(std::move(element));
}
QString MessageBuilder::matchLink(const QString &string)

View file

@ -3,21 +3,36 @@
#include "messages/Message.hpp"
#include <QRegularExpression>
#include <ctime>
namespace chatterino {
struct MessageBuilder {
struct SystemMessageTag {
};
struct TimeoutMessageTag {
};
const SystemMessageTag systemMessage{};
const TimeoutMessageTag timeoutMessage{};
MessagePtr makeSystemMessage(const QString &text);
class MessageBuilder
{
public:
MessageBuilder();
MessageBuilder(const QString &text);
MessageBuilder(SystemMessageTag, const QString &text);
MessageBuilder(TimeoutMessageTag, const QString &username,
const QString &durationInSeconds, const QString &reason,
bool multipleTimes);
MessageBuilder(const BanAction &action, uint32_t count = 1);
MessageBuilder(const UnbanAction &action);
MessagePtr getMessage();
Message *operator->();
Message &message();
MessagePtr release();
void setHighlight(bool value);
void append(MessageElement *element);
void appendTimestamp();
void appendTimestamp(const QTime &time);
void append(std::unique_ptr<MessageElement> element);
QString matchLink(const QString &string);
template <typename T, typename... Args>
@ -26,13 +41,14 @@ public:
static_assert(std::is_base_of<MessageElement, T>::value,
"T must extend MessageElement");
T *element = new T(std::forward<Args>(args)...);
this->append(element);
return element;
auto unique = std::make_unique<T>(std::forward<Args>(args)...);
auto pointer = unique.get();
this->append(std::move(unique));
return pointer;
}
protected:
MessagePtr message_;
private:
std::unique_ptr<Message> message_;
};
} // namespace chatterino

View file

@ -7,6 +7,7 @@
#include "messages/layouts/MessageLayoutContainer.hpp"
#include "messages/layouts/MessageLayoutElement.hpp"
#include "singletons/Settings.hpp"
#include "util/DebugCount.hpp"
namespace chatterino {

View file

@ -5,6 +5,7 @@
#include "singletons/Emotes.hpp"
#include "singletons/Settings.hpp"
#include "singletons/WindowManager.hpp"
#include "util/DebugCount.hpp"
#include <QApplication>
#include <QDebug>
@ -32,7 +33,7 @@ MessageLayout::~MessageLayout()
DebugCount::decrease("message layout");
}
Message *MessageLayout::getMessage()
const Message *MessageLayout::getMessage()
{
return this->message_.get();
}
@ -104,8 +105,7 @@ void MessageLayout::actuallyLayout(int width, MessageElement::Flags _flags)
this->container_.begin(width, this->scale_, messageFlags);
for (const std::unique_ptr<MessageElement> &element :
this->message_->getElements()) {
for (const auto &element : this->message_->elements) {
element->addToContainer(this->container_, _flags);
}

View file

@ -28,7 +28,7 @@ public:
MessageLayout(MessagePtr message_);
~MessageLayout();
Message *getMessage();
const Message *getMessage();
// Height
int getHeight() const;

View file

@ -3,6 +3,7 @@
#include "common/Common.hpp"
#include "messages/LimitedQueueSnapshot.hpp"
#include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp"
#include <QCoreApplication>
@ -189,8 +190,8 @@ void AbstractIrcServer::onConnected()
{
std::lock_guard<std::mutex> lock(this->channelMutex);
MessagePtr connMsg = Message::createSystemMessage("connected to chat");
MessagePtr reconnMsg = Message::createSystemMessage("reconnected to chat");
auto connected = makeSystemMessage("connected to chat");
auto reconnected = makeSystemMessage("reconnected to chat");
for (std::weak_ptr<Channel> &weak : this->channels.values()) {
std::shared_ptr<Channel> chan = weak.lock();
@ -205,11 +206,12 @@ void AbstractIrcServer::onConnected()
Message::DisconnectedMessage;
if (replaceMessage) {
chan->replaceMessage(snapshot[snapshot.getLength() - 1], reconnMsg);
chan->replaceMessage(snapshot[snapshot.getLength() - 1],
reconnected);
continue;
}
chan->addMessage(connMsg);
chan->addMessage(connected);
}
}
@ -217,8 +219,9 @@ void AbstractIrcServer::onDisconnected()
{
std::lock_guard<std::mutex> lock(this->channelMutex);
MessagePtr msg = Message::createSystemMessage("disconnected from chat");
msg->flags |= Message::DisconnectedMessage;
MessageBuilder b(systemMessage, "disconnected from chat");
b->flags |= Message::DisconnectedMessage;
auto disconnected = b.release();
for (std::weak_ptr<Channel> &weak : this->channels.values()) {
std::shared_ptr<Channel> chan = weak.lock();
@ -226,7 +229,7 @@ void AbstractIrcServer::onDisconnected()
continue;
}
chan->addMessage(msg);
chan->addMessage(disconnected);
}
}

View file

@ -60,13 +60,16 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
TwitchMessageBuilder builder(chan.get(), _message, args, content, isAction);
if (isSub || !builder.isIgnored()) {
MessagePtr msg = builder.build();
if (isSub) {
msg->flags |= Message::Subscription;
msg->flags &= ~Message::Highlighted;
} else {
if (msg->flags & Message::Highlighted) {
builder->flags |= Message::Subscription;
builder->flags &= ~Message::Highlighted;
}
auto highlighted = bool(builder->flags & Message::Highlighted);
auto msg = builder.build();
if (!isSub) {
if (highlighted) {
server.mentionsChannel->addMessage(msg);
getApp()->highlights->addHighlight(msg);
}
@ -151,8 +154,8 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
// check if the chat has been cleared by a moderator
if (message->parameters().length() == 1) {
chan->disableAllMessages();
chan->addMessage(Message::createSystemMessage(
"Chat has been cleared by a moderator."));
chan->addMessage(
makeSystemMessage("Chat has been cleared by a moderator."));
return;
}
@ -170,8 +173,9 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
reason = v.toString();
}
auto timeoutMsg = Message::createTimeoutMessage(username, durationInSeconds,
reason, false);
auto timeoutMsg = MessageBuilder(timeoutMessage, username,
durationInSeconds, reason, false)
.release();
chan->addOrReplaceTimeout(timeoutMsg);
// refresh all
@ -216,17 +220,17 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message)
false);
if (!builder.isIgnored()) {
app->twitch.server->lastUserThatWhisperedMe.set(builder.userName);
MessagePtr _message = builder.build();
if (_message->flags & Message::Highlighted) {
app->twitch.server->mentionsChannel->addMessage(_message);
}
app->twitch.server->lastUserThatWhisperedMe.set(builder.userName);
c->addMessage(_message);
_message->flags |= Message::DoNotTriggerNotification;
// _message->flags |= Message::DoNotTriggerNotification;
if (app->settings->inlineWhispers) {
app->twitch.server->forEachChannel([_message](ChannelPtr channel) {
@ -262,10 +266,11 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
auto it = tags.find("system-msg");
if (it != tags.end()) {
auto newMessage =
Message::createSystemMessage(parseTagString(it.value().toString()));
auto b = MessageBuilder(systemMessage,
parseTagString(it.value().toString()));
newMessage->flags |= Message::Subscription;
b->flags |= Message::Subscription;
auto newMessage = b.release();
QString channelName;
@ -306,7 +311,7 @@ void IrcMessageHandler::handleModeMessage(Communi::IrcMessage *message)
void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
{
auto app = getApp();
MessagePtr msg = Message::createSystemMessage(message->content());
MessagePtr msg = makeSystemMessage(message->content());
QString channelName;
if (!trimChannelName(message->target(), channelName)) {

View file

@ -68,7 +68,7 @@ TwitchChannel::TwitchChannel(const QString &name)
// debugging
#if 0
for (int i = 0; i < 1000; i++) {
this->addMessage(Message::createSystemMessage("asdf"));
this->addMessage(makeSystemMessage("asdf"));
}
#endif
}
@ -104,9 +104,9 @@ void TwitchChannel::sendMessage(const QString &message)
if (!app->accounts->twitch.isLoggedIn()) {
// XXX: It would be nice if we could add a link here somehow that opened
// the "account manager" dialog
this->addMessage(Message::createSystemMessage(
"You need to log in to send messages. You can "
"link your Twitch account in the settings."));
this->addMessage(
makeSystemMessage("You need to log in to send messages. You can "
"link your Twitch account in the settings."));
return;
}
@ -159,7 +159,7 @@ bool TwitchChannel::isBroadcaster() const
return this->getName() == app->accounts->twitch.getCurrent()->getUserName();
}
void TwitchChannel::addRecentChatter(const std::shared_ptr<Message> &message)
void TwitchChannel::addRecentChatter(const MessagePtr &message)
{
assert(!message->loginName.isEmpty());
@ -183,11 +183,11 @@ void TwitchChannel::addJoinedUser(const QString &user)
QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
auto joinedUsers = this->joinedUsers_.access();
auto message = Message::createSystemMessage(
"Users joined: " + joinedUsers->join(", "));
message->flags |= Message::Collapsed;
MessageBuilder builder(systemMessage,
"Users joined: " + joinedUsers->join(", "));
builder->flags |= Message::Collapsed;
joinedUsers->clear();
this->addMessage(message);
this->addMessage(builder.release());
this->joinedUsersMergeQueued_ = false;
});
}
@ -211,10 +211,10 @@ void TwitchChannel::addPartedUser(const QString &user)
QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
auto partedUsers = this->partedUsers_.access();
auto message = Message::createSystemMessage(
"Users parted: " + partedUsers->join(", "));
message->flags |= Message::Collapsed;
this->addMessage(message);
MessageBuilder builder(systemMessage,
"Users parted: " + partedUsers->join(", "));
builder->flags |= Message::Collapsed;
this->addMessage(builder.release());
partedUsers->clear();
this->partedUsersMergeQueued_ = false;

View file

@ -54,7 +54,7 @@ public:
virtual void sendMessage(const QString &message) override;
// Auto completion
void addRecentChatter(const std::shared_ptr<Message> &message) final;
void addRecentChatter(const MessagePtr &message) final;
void addJoinedUser(const QString &user);
void addPartedUser(const QString &user);

View file

@ -98,7 +98,7 @@ MessagePtr TwitchMessageBuilder::build()
// MessageElement::Collapsed);
// }
//#endif
this->message_->flags |= Message::Collapsed;
this->message().flags |= Message::Collapsed;
// PARSING
this->parseMessageID();
@ -175,9 +175,9 @@ MessagePtr TwitchMessageBuilder::build()
this->addWords(splits, twitchEmotes);
this->message_->searchText = this->userName + ": " + this->originalMessage_;
this->message().searchText = this->userName + ": " + this->originalMessage_;
return this->getMessage();
return this->release();
}
void TwitchMessageBuilder::addWords(
@ -376,7 +376,7 @@ void TwitchMessageBuilder::parseUsername()
// this->userName + ")";
// }
this->message_->loginName = this->userName;
this->message().loginName = this->userName;
}
void TwitchMessageBuilder::appendUsername()
@ -384,7 +384,7 @@ void TwitchMessageBuilder::appendUsername()
auto app = getApp();
QString username = this->userName;
this->message_->loginName = username;
this->message().loginName = username;
QString localizedName;
auto iterator = this->tags.find("display-name");
@ -396,12 +396,12 @@ void TwitchMessageBuilder::appendUsername()
Qt::CaseInsensitive) == 0) {
username = displayName;
this->message_->displayName = displayName;
this->message().displayName = displayName;
} else {
localizedName = displayName;
this->message_->displayName = username;
this->message_->localizedName = displayName;
this->message().displayName = username;
this->message().localizedName = displayName;
}
}
@ -573,7 +573,7 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
}
}
this->setHighlight(doHighlight);
this->message().flags.set(Message::Highlighted, doHighlight);
if (!isPastMsg) {
if (playSound &&
@ -586,10 +586,6 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
2500);
}
}
if (doHighlight) {
this->message_->flags |= Message::Highlighted;
}
}
}
@ -662,7 +658,7 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name)
// fourtf: this is ugly
// maybe put the individual badges into a map instead of this
//mess
// mess
void TwitchMessageBuilder::appendTwitchBadges()
{
auto app = getApp();

View file

@ -217,7 +217,7 @@ void TwitchServer::onMessageSendRequested(TwitchChannel *channel,
lastMessage.back() + minMessageOffset > now) {
if (this->lastErrorTimeSpeed_ + 30s < now) {
auto errorMessage =
Message::createSystemMessage("sending messages too fast");
makeSystemMessage("sending messages too fast");
channel->addMessage(errorMessage);
@ -235,7 +235,7 @@ void TwitchServer::onMessageSendRequested(TwitchChannel *channel,
if (lastMessage.size() >= maxMessageCount) {
if (this->lastErrorTimeAmount_ + 30s < now) {
auto errorMessage =
Message::createSystemMessage("sending too many messages");
makeSystemMessage("sending too many messages");
channel->addMessage(errorMessage);

View file

@ -79,7 +79,7 @@ void LoggingChannel::openLogFile()
this->appendLine(this->generateOpeningString(now));
}
void LoggingChannel::addMessage(std::shared_ptr<Message> message)
void LoggingChannel::addMessage(MessagePtr message)
{
QDateTime now = QDateTime::currentDateTime();

View file

@ -19,7 +19,7 @@ class LoggingChannel : boost::noncopyable
public:
~LoggingChannel();
void addMessage(std::shared_ptr<Message> message);
void addMessage(MessagePtr message);
private:
void openLogFile();

46
src/util/FormatTime.cpp Normal file
View file

@ -0,0 +1,46 @@
#include "FormatTime.hpp"
namespace chatterino {
namespace {
void appendDuration(int count, QChar &&order, QString &outString)
{
outString.append(QString::number(count));
outString.append(order);
}
} // namespace
QString formatTime(int totalSeconds)
{
QString res;
int seconds = totalSeconds % 60;
int timeoutMinutes = totalSeconds / 60;
int minutes = timeoutMinutes % 60;
int timeoutHours = timeoutMinutes / 60;
int hours = timeoutHours % 24;
int days = timeoutHours / 24;
if (days > 0) {
appendDuration(days, 'd', res);
}
if (hours > 0) {
if (!res.isEmpty()) {
res.append(" ");
}
appendDuration(hours, 'h', res);
}
if (minutes > 0) {
if (!res.isEmpty()) {
res.append(" ");
}
appendDuration(minutes, 'm', res);
}
if (seconds > 0) {
if (!res.isEmpty()) {
res.append(" ");
}
appendDuration(seconds, 's', res);
}
return res;
}
} // namespace chatterino

10
src/util/FormatTime.hpp Normal file
View file

@ -0,0 +1,10 @@
#pragma once
#include <QString>
namespace chatterino {
// format: 1h 23m 42s
QString formatTime(int totalSeconds);
} // namespace chatterino

View file

@ -63,24 +63,24 @@ void EmotePopup::loadChannel(ChannelPtr _channel)
// TITLE
MessageBuilder builder1;
builder1.append(new TextElement(title, MessageElement::Text));
builder1.emplace<TextElement>(title, MessageElement::Text);
builder1.getMessage()->flags |= Message::Centered;
emoteChannel->addMessage(builder1.getMessage());
builder1->flags |= Message::Centered;
emoteChannel->addMessage(builder1.release());
// EMOTES
MessageBuilder builder2;
builder2.getMessage()->flags |= Message::Centered;
builder2.getMessage()->flags |= Message::DisableCompactEmotes;
builder2->flags |= Message::Centered;
builder2->flags |= Message::DisableCompactEmotes;
for (auto emote : map) {
builder2.append(
(new EmoteElement(emote.second,
MessageElement::Flags::AlwaysShow))
->setLink(Link(Link::InsertText, emote.first.string)));
builder2
.emplace<EmoteElement>(emote.second,
MessageElement::Flags::AlwaysShow)
->setLink(Link(Link::InsertText, emote.first.string));
}
emoteChannel->addMessage(builder2.getMessage());
emoteChannel->addMessage(builder2.release());
};
auto app = getApp();
@ -103,25 +103,25 @@ void EmotePopup::loadChannel(ChannelPtr _channel)
setText = set->text;
}
builder1.append(new TextElement(setText, MessageElement::Text));
builder1.emplace<TextElement>(setText, MessageElement::Text);
builder1.getMessage()->flags |= Message::Centered;
emoteChannel->addMessage(builder1.getMessage());
builder1->flags |= Message::Centered;
emoteChannel->addMessage(builder1.release());
// EMOTES
MessageBuilder builder2;
builder2.getMessage()->flags |= Message::Centered;
builder2.getMessage()->flags |= Message::DisableCompactEmotes;
builder2->flags |= Message::Centered;
builder2->flags |= Message::DisableCompactEmotes;
for (const auto &emote : set->emotes) {
builder2.append(
(new EmoteElement(
app->emotes->twitch.getOrCreateEmote(emote.id, emote.name),
MessageElement::Flags::AlwaysShow))
->setLink(Link(Link::InsertText, emote.name.string)));
builder2
.emplace<EmoteElement>(
app->emotes->twitch.getOrCreateEmote(emote.id, emote.name),
MessageElement::Flags::AlwaysShow)
->setLink(Link(Link::InsertText, emote.name.string));
}
emoteChannel->addMessage(builder2.getMessage());
emoteChannel->addMessage(builder2.release());
}
addEmotes(*app->emotes->bttv.accessGlobalEmotes(),
@ -146,22 +146,23 @@ void EmotePopup::loadEmojis()
// title
MessageBuilder builder1;
builder1.append(new TextElement("emojis", MessageElement::Text));
builder1.getMessage()->flags |= Message::Centered;
emojiChannel->addMessage(builder1.getMessage());
builder1.emplace<TextElement>("emojis", MessageElement::Text);
builder1->flags |= Message::Centered;
emojiChannel->addMessage(builder1.release());
// emojis
MessageBuilder builder;
builder.getMessage()->flags |= Message::Centered;
builder.getMessage()->flags |= Message::DisableCompactEmotes;
builder->flags |= Message::Centered;
builder->flags |= Message::DisableCompactEmotes;
emojis.each([&builder](const auto &key, const auto &value) {
builder.append(
(new EmoteElement(value->emote, MessageElement::Flags::AlwaysShow))
->setLink(Link(Link::Type::InsertText,
":" + value->shortCodes[0] + ":")));
builder
.emplace<EmoteElement>(value->emote,
MessageElement::Flags::AlwaysShow)
->setLink(
Link(Link::Type::InsertText, ":" + value->shortCodes[0] + ":"));
});
emojiChannel->addMessage(builder.getMessage());
emojiChannel->addMessage(builder.release());
this->viewEmojis_->setChannel(emojiChannel);
}

View file

@ -170,15 +170,15 @@ void LogsPopup::getOverrustleLogs()
singleMessage.value("timestamp").toInt())
.time();
MessagePtr message(new Message);
message->addElement(new TimestampElement(timeStamp));
message->addElement(new TextElement(this->userName_,
MessageElement::Username,
MessageColor::System));
message->addElement(
new TextElement(singleMessage.value("text").toString(),
MessageElement::Text, MessageColor::Text));
messages.push_back(message);
MessageBuilder builder;
builder.emplace<TimestampElement>(timeStamp);
builder.emplace<TextElement>(this->userName_,
MessageElement::Username,
MessageColor::System);
builder.emplace<TextElement>(
singleMessage.value("text").toString(),
MessageElement::Text, MessageColor::Text);
messages.push_back(builder.release());
}
}
this->setMessages(messages);

View file

@ -1,6 +1,7 @@
#include "LookPage.hpp"
#include "Application.hpp"
#include "messages/MessageBuilder.hpp"
#include "singletons/WindowManager.hpp"
#include "util/LayoutCreator.hpp"
#include "util/RemoveScrollAreaBackground.hpp"
@ -317,55 +318,31 @@ ChannelPtr LookPage::createPreviewChannel()
{
auto channel = ChannelPtr(new Channel("preview", Channel::Type::Misc));
// clang-format off
{
auto message = MessagePtr(new Message());
message->addElement(new TimestampElement(QTime(8, 13, 42)));
message->addElement(new ImageElement(
Image::fromNonOwningPixmap(&getApp()->resources->twitch.moderator),
MessageElement::BadgeChannelAuthority));
message->addElement(new ImageElement(
Image::fromNonOwningPixmap(&getApp()->resources->twitch.subscriber),
MessageElement::BadgeSubscription));
message->addElement(
new TextElement("username1:", MessageElement::Username,
QColor("#0094FF"), FontStyle::ChatMediumBold));
message->addElement(
new TextElement("This is a preview message", MessageElement::Text));
message->addElement(new ImageElement(
Image::fromNonOwningPixmap(&getApp()->resources->pajaDank),
MessageElement::Flags::AlwaysShow));
message->addElement(
new TextElement("@fourtf", TextElement::BoldUsername,
MessageColor::Text, FontStyle::ChatMediumBold));
message->addElement(
new TextElement("@fourtf", TextElement::NonBoldUsername));
channel->addMessage(message);
MessageBuilder builder;
builder.emplace<TimestampElement>(QTime(8, 13, 42));
builder.emplace<ImageElement>(Image::fromNonOwningPixmap(&getApp()->resources->twitch.moderator), MessageElement::BadgeChannelAuthority);
builder.emplace<ImageElement>(Image::fromNonOwningPixmap(&getApp()->resources->twitch.subscriber), MessageElement::BadgeSubscription);
builder.emplace<TextElement>("username1:", MessageElement::Username, QColor("#0094FF"), FontStyle::ChatMediumBold);
builder.emplace<TextElement>("This is a preview message", MessageElement::Text);
builder.emplace<ImageElement>(Image::fromNonOwningPixmap(&getApp()->resources->pajaDank), MessageElement::Flags::AlwaysShow);
builder.emplace<TextElement>("@fourtf", TextElement::BoldUsername, MessageColor::Text, FontStyle::ChatMediumBold);
builder.emplace<TextElement>("@fourtf", TextElement::NonBoldUsername);
channel->addMessage(builder.release());
}
{
auto message = MessagePtr(new Message());
message->addElement(new TimestampElement(QTime(8, 15, 21)));
message->addElement(
new ImageElement(Image::fromNonOwningPixmap(
&getApp()->resources->twitch.broadcaster),
MessageElement::BadgeChannelAuthority));
message->addElement(
new TextElement("username2:", MessageElement::Username,
QColor("#FF6A00"), FontStyle::ChatMediumBold));
message->addElement(
new TextElement("This is another one", MessageElement::Text));
// message->addElement(new ImageElement(
// Image::fromNonOwningPixmap(&getApp()->resources->ppHop),
// MessageElement::BttvEmote));
message->addElement(
(new TextElement("www.fourtf.com", MessageElement::LowercaseLink,
MessageColor::Link))
->setLink(Link(Link::Url, "https://www.fourtf.com")));
message->addElement(
(new TextElement("wWw.FoUrTf.CoM", MessageElement::OriginalLink,
MessageColor::Link))
->setLink(Link(Link::Url, "https://www.fourtf.com")));
channel->addMessage(message);
MessageBuilder message;
message.emplace<TimestampElement>(QTime(8, 15, 21));
message.emplace<ImageElement>(Image::fromNonOwningPixmap(&getApp()->resources->twitch.broadcaster), MessageElement::BadgeChannelAuthority);
message.emplace<TextElement>("username2:", MessageElement::Username, QColor("#FF6A00"), FontStyle::ChatMediumBold);
message.emplace<TextElement>("This is another one", MessageElement::Text);
// message.emplace<ImageElement>(Image::fromNonOwningPixmap(&getApp()->resources->ppHop), MessageElement::BttvEmote);
message.emplace<TextElement>("www.fourtf.com", MessageElement::LowercaseLink, MessageColor::Link)->setLink(Link(Link::Url, "https://www.fourtf.com"));
message.emplace<TextElement>("wWw.FoUrTf.CoM", MessageElement::OriginalLink, MessageColor::Link)->setLink(Link(Link::Url, "https://www.fourtf.com"));
channel->addMessage(message.release());
}
// clang-format on
return channel;
}

0
weakOf
View file