diff --git a/.gitignore b/.gitignore
index 3c6ec6855..3d5ecc2b5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+__pycache__/
+
# C++ objects and libs
*.slo
diff --git a/chatterino.pro b/chatterino.pro
index e281901d7..131696679 100644
--- a/chatterino.pro
+++ b/chatterino.pro
@@ -240,6 +240,7 @@ SOURCES += \
src/messages/Emote.cpp \
src/messages/ImageSet.cpp \
src/providers/bttv/BttvEmotes.cpp \
+ src/providers/LinkResolver.cpp \
src/providers/ffz/FfzEmotes.cpp \
src/autogenerated/ResourcesAutogen.cpp \
src/singletons/Badges.cpp \
@@ -439,6 +440,7 @@ HEADERS += \
src/messages/ImageSet.hpp \
src/common/Outcome.hpp \
src/providers/bttv/BttvEmotes.hpp \
+ src/providers/LinkResolver.hpp \
src/providers/ffz/FfzEmotes.hpp \
src/autogenerated/ResourcesAutogen.hpp \
src/singletons/Badges.hpp \
diff --git a/resources/__pycache__/_generate_resources.cpython-36.pyc b/resources/__pycache__/_generate_resources.cpython-36.pyc
deleted file mode 100644
index dfaf6af51..000000000
Binary files a/resources/__pycache__/_generate_resources.cpython-36.pyc and /dev/null differ
diff --git a/resources/buttons/addSplitDark.png b/resources/buttons/addSplitDark.png
new file mode 100644
index 000000000..c6cd6707a
Binary files /dev/null and b/resources/buttons/addSplitDark.png differ
diff --git a/resources/generate_resources.py b/resources/generate_resources.py
index 4b047219c..5239e64e8 100755
--- a/resources/generate_resources.py
+++ b/resources/generate_resources.py
@@ -5,10 +5,18 @@ from _generate_resources import *
ignored_files = ['qt.conf', 'resources.qrc', 'resources_autogenerated.qrc', 'windows.rc',
'generate_resources.py', '_generate_resources.py']
+
+# to ignore all files in a/b, add a/b to ignored_directories.
+# this will ignore a/b/c/d.txt and a/b/xd.txt
ignored_directories = ['__pycache__']
def isNotIgnored(file):
- return str(file) not in ignored_files
+ # check if file exists in an ignored direcory
+ for ignored_directory in ignored_directories:
+ if file.parent.as_posix().startswith(ignored_directory):
+ return False
+
+ return file.as_posix() not in ignored_files
all_files = list(filter(isNotIgnored, \
filter(Path.is_file, Path('.').glob('**/*'))))
@@ -18,15 +26,15 @@ image_files = list(filter(isNotIgnored, \
with open('./resources_autogenerated.qrc', 'w') as out:
out.write(resources_header)
for file in all_files:
- out.write(f" {str(file)}\n")
+ out.write(f" {file.as_posix()}\n")
out.write(resources_footer)
with open('../src/autogenerated/ResourcesAutogen.cpp', 'w') as out:
out.write(source_header)
for file in sorted(image_files):
- var_name = str(file.with_suffix("")).replace("/",".")
+ var_name = file.with_suffix("").as_posix().replace("/",".")
out.write(f' this->{var_name}')
- out.write(f' = QPixmap(":/{file}");\n')
+ out.write(f' = QPixmap(":/{file.as_posix()}");\n')
out.write(source_footer)
def writeHeader(out, name, element, indent):
@@ -46,12 +54,11 @@ with open('../src/autogenerated/ResourcesAutogen.hpp', 'w') as out:
elements = {}
for file in sorted(image_files):
elements_ref = elements
- directories = str(file).split('/')[:-1]
+ directories = file.as_posix().split('/')[:-1]
filename = file.stem
for directory in directories:
if directory not in elements_ref:
- if directory not in ignored_directories:
- elements_ref[directory] = {}
+ elements_ref[directory] = {}
elements_ref = elements_ref[directory]
elements_ref[filename] = filename
diff --git a/resources/resources_autogenerated.qrc b/resources/resources_autogenerated.qrc
index 6fc9e6f68..d764d54c1 100644
--- a/resources/resources_autogenerated.qrc
+++ b/resources/resources_autogenerated.qrc
@@ -1,64 +1,66 @@
- pajaDank.png
- icon.png
- emojidata.txt
+ chatterino2.icns
contributors.txt
- error.png
emoji.json
+ emojidata.txt
+ error.png
icon.ico
+ icon.png
+ pajaDank.png
tlds.txt
- chatterino2.icns
- qss/settings.qss
- __pycache__/_generate_resources.cpython-36.pyc
- licenses/fmt_bsd2.txt
- licenses/openssl.txt
- licenses/pajlada_settings.txt
- licenses/qt_lgpl-3.0.txt
- licenses/pajlada_signals.txt
- licenses/rapidjson.txt
- licenses/websocketpp.txt
- licenses/boost_boost.txt
- licenses/libcommuni_BSD3.txt
- settings/aboutlogo.png
- settings/behave.svg
- settings/accounts.svg
- settings/about.svg
- settings/notifications.svg
- settings/commands.svg
- settings/theme.svg
- split/up.png
- split/left.png
- split/move.png
- split/right.png
- split/down.png
- buttons/unban.png
- buttons/menuDark.png
- buttons/mod.png
- buttons/emote.svg
- buttons/modModeEnabled2.png
+ avatars/fourtf.png
+ avatars/pajlada.png
+ buttons/addSplitDark.png
buttons/ban.png
- buttons/unmod.png
+ buttons/banRed.png
+ buttons/emote.svg
buttons/emoteDark.svg
- buttons/updateError.png
+ buttons/menuDark.png
+ buttons/menuLight.png
+ buttons/mod.png
buttons/modModeDisabled.png
buttons/modModeDisabled2.png
buttons/modModeEnabled.png
- buttons/menuLight.png
- buttons/update.png
+ buttons/modModeEnabled2.png
buttons/timeout.png
- buttons/banRed.png
+ buttons/unban.png
+ buttons/unmod.png
+ buttons/update.png
+ buttons/updateError.png
+ licenses/boost_boost.txt
+ licenses/emoji-data-source.txt
+ licenses/fmt_bsd2.txt
+ licenses/libcommuni_BSD3.txt
+ licenses/openssl.txt
+ licenses/pajlada_settings.txt
+ licenses/pajlada_signals.txt
+ licenses/qt_lgpl-3.0.txt
+ licenses/rapidjson.txt
+ licenses/websocketpp.txt
+ qss/settings.qss
+ settings/about.svg
+ settings/aboutlogo.png
+ settings/accounts.svg
+ settings/behave.svg
+ settings/commands.svg
+ settings/emote.svg
+ settings/notifications.svg
+ settings/theme.svg
sounds/ping2.wav
- twitch/prime.png
- twitch/verified.png
+ split/down.png
+ split/left.png
+ split/move.png
+ split/right.png
+ split/up.png
twitch/admin.png
+ twitch/broadcaster.png
+ twitch/cheer1.png
+ twitch/globalmod.png
+ twitch/moderator.png
+ twitch/prime.png
+ twitch/staff.png
twitch/subscriber.png
twitch/turbo.png
- twitch/moderator.png
- twitch/globalmod.png
- twitch/cheer1.png
- twitch/broadcaster.png
- twitch/staff.png
- avatars/fourtf.png
- avatars/pajlada.png
+ twitch/verified.png
\ No newline at end of file
diff --git a/src/Application.cpp b/src/Application.cpp
index bce4cd504..2f8c02587 100644
--- a/src/Application.cpp
+++ b/src/Application.cpp
@@ -10,6 +10,7 @@
#include "debug/Log.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/bttv/BttvEmotes.hpp"
+#include "providers/chatterino/ChatterinoBadges.hpp"
#include "providers/ffz/FfzEmotes.hpp"
#include "providers/twitch/PubsubClient.hpp"
#include "providers/twitch/TwitchServer.hpp"
@@ -56,6 +57,7 @@ Application::Application(Settings &_settings, Paths &_paths)
, taggedUsers(&this->emplace())
, moderationActions(&this->emplace())
, twitch2(&this->emplace())
+ , chatterinoBadges(&this->emplace())
, logging(&this->emplace())
{
diff --git a/src/Application.hpp b/src/Application.hpp
index b7df63d9d..90e2781af 100644
--- a/src/Application.hpp
+++ b/src/Application.hpp
@@ -28,7 +28,7 @@ class Settings;
class Fonts;
class Resources2;
class Toasts;
-
+class ChatterinoBadges;
class Application
{
@@ -65,6 +65,7 @@ public:
TaggedUsersController *const taggedUsers{};
ModerationActions *const moderationActions{};
TwitchServer *const twitch2{};
+ ChatterinoBadges *const chatterinoBadges{};
/*[[deprecated]]*/ Logging *const logging{};
diff --git a/src/autogenerated/ResourcesAutogen.cpp b/src/autogenerated/ResourcesAutogen.cpp
index c5035f2e6..9d67608e8 100644
--- a/src/autogenerated/ResourcesAutogen.cpp
+++ b/src/autogenerated/ResourcesAutogen.cpp
@@ -6,6 +6,7 @@ Resources2::Resources2()
{
this->avatars.fourtf = QPixmap(":/avatars/fourtf.png");
this->avatars.pajlada = QPixmap(":/avatars/pajlada.png");
+ this->buttons.addSplitDark = QPixmap(":/buttons/addSplitDark.png");
this->buttons.ban = QPixmap(":/buttons/ban.png");
this->buttons.banRed = QPixmap(":/buttons/banRed.png");
this->buttons.menuDark = QPixmap(":/buttons/menuDark.png");
diff --git a/src/autogenerated/ResourcesAutogen.hpp b/src/autogenerated/ResourcesAutogen.hpp
index c6adb3576..4a6f9fc4b 100644
--- a/src/autogenerated/ResourcesAutogen.hpp
+++ b/src/autogenerated/ResourcesAutogen.hpp
@@ -3,8 +3,7 @@
namespace chatterino {
-class Resources2 : public Singleton
-{
+class Resources2 : public Singleton {
public:
Resources2();
@@ -13,6 +12,7 @@ public:
QPixmap pajlada;
} avatars;
struct {
+ QPixmap addSplitDark;
QPixmap ban;
QPixmap banRed;
QPixmap menuDark;
diff --git a/src/common/Channel.cpp b/src/common/Channel.cpp
index f9ac3ac50..ac1d6ea56 100644
--- a/src/common/Channel.cpp
+++ b/src/common/Channel.cpp
@@ -143,7 +143,8 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
if (s->flags.hasNone({MessageFlag::Timeout, MessageFlag::Untimeout}) &&
s->loginName == message->timeoutUser) {
// FOURTF: disabled for now
- // s->flags.EnableFlag(MessageFlag::Disabled);
+ // PAJLADA: Shitty solution described in Message.hpp
+ s->flags.set(MessageFlag::Disabled);
}
}
diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp
index 237727d0d..4a56913b4 100644
--- a/src/common/SignalVectorModel.hpp
+++ b/src/common/SignalVectorModel.hpp
@@ -124,7 +124,7 @@ public:
rowItem.items[column]->setData(value, role);
if (rowItem.isCustomRow) {
- this->customRowSetData(rowItem.items, column, value, role);
+ this->customRowSetData(rowItem.items, column, value, role, row);
} else {
int vecRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(vecRow, this);
@@ -230,7 +230,8 @@ protected:
}
virtual void customRowSetData(const std::vector &row,
- int column, const QVariant &value, int role)
+ int column, const QVariant &value, int role,
+ int rowIndex)
{
}
diff --git a/src/controllers/highlights/HighlightModel.cpp b/src/controllers/highlights/HighlightModel.cpp
index 827e23a4a..0ba232b37 100644
--- a/src/controllers/highlights/HighlightModel.cpp
+++ b/src/controllers/highlights/HighlightModel.cpp
@@ -36,36 +36,67 @@ void HighlightModel::getRowFromItem(const HighlightPhrase &item,
void HighlightModel::afterInit()
{
- std::vector row = this->createRow();
- setBoolItem(row[0], getSettings()->enableHighlightsSelf.getValue(), true,
+ std::vector usernameRow = this->createRow();
+ setBoolItem(usernameRow[0], getSettings()->enableSelfHighlight.getValue(),
+ true, false);
+ usernameRow[0]->setData("Your username (automatic)", Qt::DisplayRole);
+ setBoolItem(usernameRow[1],
+ getSettings()->enableSelfHighlightTaskbar.getValue(), true,
false);
- row[0]->setData("Your username (automatic)", Qt::DisplayRole);
- setBoolItem(row[1], getSettings()->enableHighlightTaskbar.getValue(), true,
+ setBoolItem(usernameRow[2],
+ getSettings()->enableSelfHighlightSound.getValue(), true,
false);
- setBoolItem(row[2], getSettings()->enableHighlightSound.getValue(), true,
+ usernameRow[3]->setFlags(0);
+ this->insertCustomRow(usernameRow, 0);
+ std::vector whisperRow = this->createRow();
+ setBoolItem(whisperRow[0], getSettings()->enableWhisperHighlight.getValue(),
+ true, false);
+ whisperRow[0]->setData("Whispers", Qt::DisplayRole);
+ setBoolItem(whisperRow[1],
+ getSettings()->enableWhisperHighlightTaskbar.getValue(), true,
false);
- row[3]->setFlags(0);
- this->insertCustomRow(row, 0);
+ setBoolItem(whisperRow[2],
+ getSettings()->enableWhisperHighlightSound.getValue(), true,
+ false);
+ whisperRow[3]->setFlags(0);
+ this->insertCustomRow(whisperRow, 1);
}
void HighlightModel::customRowSetData(const std::vector &row,
int column, const QVariant &value,
- int role)
+ int role, int rowIndex)
{
switch (column) {
case 0: {
if (role == Qt::CheckStateRole) {
- getSettings()->enableHighlightsSelf.setValue(value.toBool());
+ if (rowIndex == 0) {
+ getSettings()->enableSelfHighlight.setValue(value.toBool());
+ } else if (rowIndex == 1) {
+ getSettings()->enableWhisperHighlight.setValue(
+ value.toBool());
+ }
}
} break;
case 1: {
if (role == Qt::CheckStateRole) {
- getSettings()->enableHighlightTaskbar.setValue(value.toBool());
+ if (rowIndex == 0) {
+ getSettings()->enableSelfHighlightTaskbar.setValue(
+ value.toBool());
+ } else if (rowIndex == 1) {
+ getSettings()->enableWhisperHighlightTaskbar.setValue(
+ value.toBool());
+ }
}
} break;
case 2: {
if (role == Qt::CheckStateRole) {
- getSettings()->enableHighlightSound.setValue(value.toBool());
+ if (rowIndex == 0) {
+ getSettings()->enableSelfHighlightSound.setValue(
+ value.toBool());
+ } else if (rowIndex == 1) {
+ getSettings()->enableWhisperHighlightSound.setValue(
+ value.toBool());
+ }
}
} break;
case 3: {
diff --git a/src/controllers/highlights/HighlightModel.hpp b/src/controllers/highlights/HighlightModel.hpp
index b42246167..563706246 100644
--- a/src/controllers/highlights/HighlightModel.hpp
+++ b/src/controllers/highlights/HighlightModel.hpp
@@ -26,8 +26,8 @@ protected:
virtual void afterInit() override;
virtual void customRowSetData(const std::vector &row,
- int column, const QVariant &value,
- int role) override;
+ int column, const QVariant &value, int role,
+ int rowIndex) override;
friend class HighlightController;
};
diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp
index e6dcfbc8b..01edc9526 100644
--- a/src/messages/Image.cpp
+++ b/src/messages/Image.cpp
@@ -310,6 +310,14 @@ void Image::load()
return Success;
});
+ req.onError([that = this, weak = weakOf(this)](auto result) -> bool {
+ auto shared = weak.lock();
+ if (!shared) return false;
+
+ shared->empty_ = true;
+
+ return true;
+ });
req.execute();
}
diff --git a/src/messages/Message.hpp b/src/messages/Message.hpp
index d6a79fcdd..6b50e8399 100644
--- a/src/messages/Message.hpp
+++ b/src/messages/Message.hpp
@@ -34,7 +34,13 @@ struct Message : boost::noncopyable {
Message();
~Message();
- MessageFlags flags;
+ // Making this a mutable means that we can update a messages flags,
+ // while still keeping Message constant. This means that a message's flag
+ // can be updated without the renderer being made aware, which might be bad.
+ // This is a temporary effort until we can figure out what the right
+ // const-correct way to deal with this is.
+ // This might bring race conditions with it
+ mutable MessageFlags flags;
QTime parseTime;
QString id;
QString searchText;
diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp
index b9271d2ed..49b2cadbc 100644
--- a/src/messages/MessageElement.cpp
+++ b/src/messages/MessageElement.cpp
@@ -61,6 +61,12 @@ MessageElementFlags MessageElement::getFlags() const
return this->flags_;
}
+MessageElement *MessageElement::updateLink()
+{
+ this->linkChanged.invoke();
+ return this;
+}
+
// IMAGE
ImageElement::ImageElement(ImagePtr image, MessageElementFlags flags)
: MessageElement(flags)
@@ -152,6 +158,15 @@ void TextElement::addToContainer(MessageLayoutContainer &container,
color, this->style_, container.getScale()))
->setLink(this->getLink());
e->setTrailingSpace(trailingSpace);
+
+ // If URL link was changed,
+ // Should update it in MessageLayoutElement too!
+ if (this->getLink().type == Link::Url) {
+ this->linkChanged.connect(
+ [this, e]() {
+ e->setLink(this->getLink());
+ });
+ }
return e;
};
diff --git a/src/messages/MessageElement.hpp b/src/messages/MessageElement.hpp
index 8d96d1135..e82bee5e0 100644
--- a/src/messages/MessageElement.hpp
+++ b/src/messages/MessageElement.hpp
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
namespace chatterino {
class Channel;
@@ -128,6 +129,7 @@ public:
const Link &getLink() const;
bool hasTrailingSpace() const;
MessageElementFlags getFlags() const;
+ MessageElement *updateLink();
virtual void addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags) = 0;
@@ -135,6 +137,7 @@ public:
protected:
MessageElement(MessageElementFlags flags);
bool trailingSpace = true;
+ pajlada::Signals::NoArgSignal linkChanged;
private:
Link link_;
diff --git a/src/providers/LinkResolver.cpp b/src/providers/LinkResolver.cpp
new file mode 100644
index 000000000..f48834ef3
--- /dev/null
+++ b/src/providers/LinkResolver.cpp
@@ -0,0 +1,48 @@
+#include "providers/LinkResolver.hpp"
+
+#include "common/Common.hpp"
+#include "common/NetworkRequest.hpp"
+#include "messages/Link.hpp"
+#include "singletons/Settings.hpp"
+
+#include
+
+namespace chatterino {
+
+void LinkResolver::getLinkInfo(const QString url,
+ std::function successCallback)
+{
+ QString requestUrl("https://braize.pajlada.com/chatterino/link_resolver/" +
+ QUrl::toPercentEncoding(url, "", "/:"));
+
+ NetworkRequest request(requestUrl);
+ request.setCaller(QThread::currentThread());
+ request.setTimeout(30000);
+ request.onSuccess([successCallback, url](auto result) mutable -> Outcome {
+ auto root = result.parseJson();
+ auto statusCode = root.value("status").toInt();
+ QString response = QString();
+ QString linkString = url;
+ if (statusCode == 200) {
+ response = root.value("tooltip").toString();
+ if (getSettings()->enableUnshortLinks) {
+ linkString = root.value("link").toString();
+ }
+ } else {
+ response = root.value("message").toString();
+ }
+ successCallback(QUrl::fromPercentEncoding(response.toUtf8()), Link(Link::Url, linkString));
+
+ return Success;
+ });
+
+ request.onError([successCallback, url](auto result) {
+ successCallback("No link info found", Link(Link::Url, url));
+
+ return true;
+ });
+
+ request.execute();
+}
+
+} // namespace chatterino
diff --git a/src/providers/LinkResolver.hpp b/src/providers/LinkResolver.hpp
new file mode 100644
index 000000000..919f0c3bf
--- /dev/null
+++ b/src/providers/LinkResolver.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include
+#include
+
+#include "messages/Link.hpp"
+
+namespace chatterino {
+
+class LinkResolver
+{
+public:
+ static void getLinkInfo(const QString url,
+ std::function callback);
+
+private:
+};
+
+} // namespace chatterino
diff --git a/src/providers/chatterino/ChatterinoBadges.cpp b/src/providers/chatterino/ChatterinoBadges.cpp
index 37dc8f6f0..28bb57f3e 100644
--- a/src/providers/chatterino/ChatterinoBadges.cpp
+++ b/src/providers/chatterino/ChatterinoBadges.cpp
@@ -5,8 +5,18 @@
#include
#include
#include "common/NetworkRequest.hpp"
+#include "common/Outcome.hpp"
+#include "messages/Emote.hpp"
+
+#include
+
+#include