Prevent user from entering incorrect characters in Live Notifications channels list (#3715)

Co-authored-by: Sidd <iProdigy@users.noreply.github.com>
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
kornes 2022-05-08 10:27:25 +00:00 committed by GitHub
parent fd44f30c7d
commit 07dd8c560b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 199 additions and 0 deletions

View file

@ -12,6 +12,7 @@
- Minor: Sorted usernames in /vips message to be case-insensitive. (#3696)
- Minor: Added option to open a user's chat in a new tab from the usercard profile picture context menu. (#3625)
- Minor: Fixed tag parsing for consecutive escaped characters. (#3711)
- Minor: Prevent user from entering incorrect characters in Live Notifications channels list. (#3715)
- Minor: Fixed automod caught message notice appearing twice for mods. (#3717)
- Bugfix: Fixed live notifications for usernames containing uppercase characters. (#3646)
- Bugfix: Fixed live notifications not getting updated for closed streams going offline. (#3678)

View file

@ -305,6 +305,7 @@ SOURCES += \
src/widgets/helper/NotebookButton.cpp \
src/widgets/helper/NotebookTab.cpp \
src/widgets/helper/QColorPicker.cpp \
src/widgets/helper/RegExpItemDelegate.cpp \
src/widgets/helper/ResizingTextEdit.cpp \
src/widgets/helper/ScrollbarHighlight.cpp \
src/widgets/helper/SearchPopup.cpp \
@ -588,6 +589,7 @@ HEADERS += \
src/widgets/helper/NotebookButton.hpp \
src/widgets/helper/NotebookTab.hpp \
src/widgets/helper/QColorPicker.hpp \
src/widgets/helper/RegExpItemDelegate.hpp \
src/widgets/helper/ResizingTextEdit.hpp \
src/widgets/helper/ScrollbarHighlight.hpp \
src/widgets/helper/SearchPopup.hpp \

View file

@ -420,6 +420,8 @@ set(SOURCE_FILES
widgets/helper/NotebookTab.hpp
widgets/helper/QColorPicker.cpp
widgets/helper/QColorPicker.hpp
widgets/helper/RegExpItemDelegate.cpp
widgets/helper/RegExpItemDelegate.hpp
widgets/helper/ResizingTextEdit.cpp
widgets/helper/ResizingTextEdit.hpp
widgets/helper/ScrollbarHighlight.cpp

View file

@ -5,6 +5,12 @@
namespace chatterino {
namespace {
const auto TWITCH_USER_LOGIN_PATTERN = R"(^[a-z0-9]\w{0,24}$)";
} // namespace
void openTwitchUsercard(QString channel, QString username)
{
QDesktopServices::openUrl("https://www.twitch.tv/popout/" + channel +
@ -35,4 +41,20 @@ void stripChannelName(QString &channelName)
}
}
QRegularExpression twitchUserNameRegexp()
{
static QRegularExpression re(
TWITCH_USER_LOGIN_PATTERN,
QRegularExpression::PatternOption::CaseInsensitiveOption);
return re;
}
QRegularExpression twitchUserLoginRegexp()
{
static QRegularExpression re(TWITCH_USER_LOGIN_PATTERN);
return re;
}
} // namespace chatterino

View file

@ -1,5 +1,6 @@
#pragma once
#include <QRegularExpression>
#include <QString>
namespace chatterino {
@ -12,4 +13,16 @@ void stripUserName(QString &userName);
// stripChannelName removes any @ prefix or , suffix to make it more suitable for command use
void stripChannelName(QString &channelName);
// Matches a strict Twitch user login.
// May contain lowercase a-z, 0-9, and underscores
// Must contain between 1 and 25 characters
// Must not start with an underscore
QRegularExpression twitchUserLoginRegexp();
// Matches a loose Twitch user login name.
// May contain lowercase and uppercase a-z, 0-9, and underscores
// Must contain between 1 and 25 characters
// Must not start with an underscore
QRegularExpression twitchUserNameRegexp();
} // namespace chatterino

View file

@ -1,4 +1,5 @@
#include "EditableModelView.hpp"
#include "widgets/helper/RegExpItemDelegate.hpp"
#include <QAbstractItemView>
#include <QAbstractTableModel>
@ -90,6 +91,10 @@ EditableModelView::EditableModelView(QAbstractTableModel *model, bool movable)
// finish button layout
buttons->addStretch(1);
}
void EditableModelView::setValidationRegexp(QRegularExpression regexp)
{
this->tableView_->setItemDelegate(new RegExpItemDelegate(this, regexp));
}
void EditableModelView::setTitles(std::initializer_list<QString> titles)
{

View file

@ -16,6 +16,7 @@ public:
EditableModelView(QAbstractTableModel *model, bool movable = true);
void setTitles(std::initializer_list<QString> titles);
void setValidationRegexp(QRegularExpression regexp);
QTableView *getTableView();
QAbstractTableModel *getModel();

View file

@ -0,0 +1,24 @@
#include "widgets/helper/RegExpItemDelegate.hpp"
#include <QLineEdit>
namespace chatterino {
RegExpItemDelegate::RegExpItemDelegate(QObject *parent,
QRegularExpression regexp)
: QStyledItemDelegate(parent)
, regexp_(regexp)
{
}
QWidget *RegExpItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
auto *editor = new QLineEdit(parent);
editor->setValidator(
new QRegularExpressionValidator(this->regexp_, editor));
return editor;
}
} // namespace chatterino

View file

@ -0,0 +1,22 @@
#pragma once
#include <QRegularExpression>
#include <QStyledItemDelegate>
namespace chatterino {
class RegExpItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
RegExpItemDelegate(QObject *parent, QRegularExpression regexp);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
private:
const QRegularExpression regexp_;
};
} // namespace chatterino

View file

@ -6,6 +6,7 @@
#include "singletons/Settings.hpp"
#include "singletons/Toasts.hpp"
#include "util/LayoutCreator.hpp"
#include "util/Twitch.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QCheckBox>
@ -96,6 +97,7 @@ NotificationPage::NotificationPage()
nullptr, Platform::Twitch))
.getElement();
view->setTitles({"Twitch channels"});
view->setValidationRegexp(twitchUserNameRegexp());
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);

View file

@ -159,3 +159,108 @@ TEST(UtilTwitch, StripChannelName)
<< qUtf8Printable(expectedChannelName);
}
}
TEST(UtilTwitch, UserLoginRegexp)
{
struct TestCase {
QString inputUserLogin;
bool matches;
};
std::vector<TestCase> tests{
{
"pajlada",
true,
},
{
// Login names must not start with an underscore
"_pajlada",
false,
},
{
"@pajlada",
false,
},
{
"pajlada!",
false,
},
{
"pajlada,",
false,
},
{
// Login names must not contain capital letters
"Pajlada",
false,
},
{"k", true},
{"testaccount_420", true},
{"3v", true},
{"ron", true},
{"bits", true},
};
const auto &regexp = twitchUserLoginRegexp();
for (const auto &[inputUserLogin, expectedMatch] : tests)
{
const auto &match = regexp.match(inputUserLogin);
auto actual = regexp.match(inputUserLogin);
EXPECT_EQ(match.hasMatch(), expectedMatch)
<< qUtf8Printable(inputUserLogin) << " did not match as expected";
}
}
TEST(UtilTwitch, UserNameRegexp)
{
struct TestCase {
QString inputUserLogin;
bool matches;
};
std::vector<TestCase> tests{
{"PAJLADA", true},
{
// User names must not start with an underscore
"_pajlada",
false,
},
{
"@pajlada",
false,
},
{
"pajlada!",
false,
},
{
"pajlada,",
false,
},
{
"Pajlada",
true,
},
{"k", true},
{"testaccount_420", true},
{"3v", true},
{"ron", true},
{"bits", true},
{"3V", true},
{"Ron", true},
{"Bits", true},
};
const auto &regexp = twitchUserNameRegexp();
for (const auto &[inputUserLogin, expectedMatch] : tests)
{
const auto &match = regexp.match(inputUserLogin);
auto actual = regexp.match(inputUserLogin);
EXPECT_EQ(match.hasMatch(), expectedMatch)
<< qUtf8Printable(inputUserLogin) << " did not match as expected";
}
}