Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feat/theme-watcher
Browse files Browse the repository at this point in the history
  • Loading branch information
pajlada committed Jul 23, 2023
2 parents ffb3c0a + fca5769 commit f207abd
Show file tree
Hide file tree
Showing 18 changed files with 351 additions and 114 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@
- Minor: Add pin action to usercards and reply threads. (#4692)
- Minor: Stream status requests are now batched. (#4713)
- Minor: Added `/c2-theme-autoreload` command to automatically reload a custom theme. This is useful for when you're developing your own theme. (#4718)
- Bugfix: Increased amount of blocked users loaded from 100 to 1,000. (#4721)
- Bugfix: Fixed generation of crashdumps by the browser-extension process when the browser was closed. (#4667)
- Bugfix: Fix spacing issue with mentions inside RTL text. (#4677)
- Bugfix: Fixed a crash when opening and closing a reply thread and switching the user. (#4675)
- Bugfix: Fixed a crash that could happen when closing splits before their display name was updated. This was especially noticeable after the live controller changes. (#4731)
- Bugfix: Fix visual glitches with smooth scrolling. (#4501)
- Bugfix: Fixed pings firing for the "Your username" highlight when not signed in. (#4698)
- Bugfix: Fixed partially broken filters on Qt 6 builds. (#4702)
- Bugfix: Fixed some network errors having `0` as their HTTP status. (#4704)
- Bugfix: Fixed crash that could occurr when closing the usercard too quickly after blocking or unblocking a user. (#4711)
- Bugfix: Fixed highlights sometimes not working after changing sound device, or switching users in your operating system. (#4729)
- Bugfix: Fixed key bindings not showing in context menus on Mac. (#4722)
- Dev: Added command to set Qt's logging filter/rules at runtime (`/c2-set-logging-rules`). (#4637)
- Dev: Added the ability to see & load custom themes from the Themes directory. No stable promises are made of this feature, changes might be made that breaks custom themes without notice. (#4570)
Expand Down
4 changes: 3 additions & 1 deletion mocks/include/mocks/Helix.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "providers/twitch/api/Helix.hpp"
#include "util/CancellationToken.hpp"

#include <gmock/gmock.h>
#include <QString>
Expand Down Expand Up @@ -107,7 +108,8 @@ class Helix : public IHelix
MOCK_METHOD(void, loadBlocks,
(QString userId,
ResultCallback<std::vector<HelixBlock>> successCallback,
HelixFailureCallback failureCallback),
FailureCallback<QString> failureCallback,
CancellationToken &&token),
(override));

MOCK_METHOD(void, blockUser,
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ set(SOURCE_FILES

util/AttachToConsole.cpp
util/AttachToConsole.hpp
util/CancellationToken.hpp
util/Clipboard.cpp
util/Clipboard.hpp
util/ConcurrentMap.hpp
Expand Down
8 changes: 4 additions & 4 deletions src/controllers/ignores/IgnoreController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ bool isIgnoredMessage(IgnoredMessageParameters &&params)
{
auto sourceUserID = params.twitchUserID;

auto blocks =
getApp()->accounts->twitch.getCurrent()->accessBlockedUserIds();

if (auto it = blocks->find(sourceUserID); it != blocks->end())
bool isBlocked =
getApp()->accounts->twitch.getCurrent()->blockedUserIds().contains(
sourceUserID);
if (isBlocked)
{
switch (static_cast<ShowIgnoredUsersMessages>(
getSettings()->showBlockedUsersMessages.getValue()))
Expand Down
76 changes: 53 additions & 23 deletions src/controllers/sound/SoundController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ constexpr const auto NUM_SOUNDS = 4;
SoundController::SoundController()
: context(std::make_unique<ma_context>())
, resourceManager(std::make_unique<ma_resource_manager>())
, device(std::make_unique<ma_device>())
, engine(std::make_unique<ma_engine>())
{
}
Expand Down Expand Up @@ -66,27 +65,9 @@ void SoundController::initialize(Settings &settings, Paths &paths)
this->defaultPingData = defaultPingFile.readAll();

/// Initialize a sound device
auto deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.pDeviceID = nullptr;
deviceConfig.playback.format = this->resourceManager->config.decodedFormat;
deviceConfig.playback.channels = 0;
deviceConfig.pulse.pStreamNamePlayback = "Chatterino MA";
deviceConfig.sampleRate = this->resourceManager->config.decodedSampleRate;
deviceConfig.dataCallback = ma_engine_data_callback_internal;
deviceConfig.pUserData = this->engine.get();

result =
ma_device_init(this->context.get(), &deviceConfig, this->device.get());
if (result != MA_SUCCESS)
if (!this->recreateDevice())
{
qCWarning(chatterinoSound) << "Error initializing device:" << result;
return;
}

result = ma_device_start(this->device.get());
if (result != MA_SUCCESS)
{
qCWarning(chatterinoSound) << "Error starting device:" << result;
qCWarning(chatterinoSound) << "Failed to create the initial device";
return;
}

Expand Down Expand Up @@ -172,7 +153,11 @@ SoundController::~SoundController()
}

ma_engine_uninit(this->engine.get());
ma_device_uninit(this->device.get());
if (this->device)
{
ma_device_uninit(this->device.get());
this->device.reset();
}
ma_resource_manager_uninit(this->resourceManager.get());
ma_context_uninit(this->context.get());
}
Expand Down Expand Up @@ -204,7 +189,12 @@ void SoundController::play(const QUrl &sound)
{
qCWarning(chatterinoSound)
<< "Failed to start the sound device" << result;
return;

if (!this->recreateDevice())
{
qCWarning(chatterinoSound) << "Failed to recreate device";
return;
}
}

qCInfo(chatterinoSound) << "Successfully restarted the sound device";
Expand Down Expand Up @@ -234,4 +224,44 @@ void SoundController::play(const QUrl &sound)
}
}

bool SoundController::recreateDevice()
{
ma_result result{};

if (this->device)
{
// Release the previous device first
qCDebug(chatterinoSound) << "Uniniting previously created device";
ma_device_uninit(this->device.get());
}

this->device = std::make_unique<ma_device>();

auto deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.pDeviceID = nullptr;
deviceConfig.playback.format = this->resourceManager->config.decodedFormat;
deviceConfig.playback.channels = 0;
deviceConfig.pulse.pStreamNamePlayback = "Chatterino MA";
deviceConfig.sampleRate = this->resourceManager->config.decodedSampleRate;
deviceConfig.dataCallback = ma_engine_data_callback_internal;
deviceConfig.pUserData = this->engine.get();

result =
ma_device_init(this->context.get(), &deviceConfig, this->device.get());
if (result != MA_SUCCESS)
{
qCWarning(chatterinoSound) << "Error initializing device:" << result;
return false;
}

result = ma_device_start(this->device.get());
if (result != MA_SUCCESS)
{
qCWarning(chatterinoSound) << "Error starting device:" << result;
return false;
}

return true;
}

} // namespace chatterino
9 changes: 8 additions & 1 deletion src/controllers/sound/SoundController.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class SoundController : public Singleton
// Used for storing & reusing sounds to be played
std::unique_ptr<ma_resource_manager> resourceManager;
// The sound device we're playing sound into
std::unique_ptr<ma_device> device;
std::unique_ptr<ma_device> device{nullptr};
// The engine is a high-level API for playing sounds from paths in a simple & efficient-enough manner
std::unique_ptr<ma_engine> engine;

Expand All @@ -64,6 +64,13 @@ class SoundController : public Singleton

bool initialized{false};

// Recreates the sound device
// This is used during initialization, and can also be used if the device
// needs to be recreated during playback
//
// Returns false on failure
bool recreateDevice();

friend class Application;
};

Expand Down
71 changes: 36 additions & 35 deletions src/providers/twitch/TwitchAccount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
#include "common/Outcome.hpp"
#include "common/QLogging.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "debug/AssertInGuiThread.hpp"
#include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/irc/IrcMessageBuilder.hpp"
#include "providers/IvrApi.hpp"
#include "providers/twitch/api/Helix.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "providers/twitch/TwitchUser.hpp"
#include "singletons/Emotes.hpp"
#include "util/CancellationToken.hpp"
#include "util/Helpers.hpp"
#include "util/QStringHash.hpp"
#include "util/RapidjsonHelpers.hpp"
Expand Down Expand Up @@ -100,79 +101,79 @@ bool TwitchAccount::isAnon() const

void TwitchAccount::loadBlocks()
{
assertInGuiThread();

auto token = CancellationToken(false);
this->blockToken_ = token;
this->ignores_.clear();
this->ignoresUserIds_.clear();

getHelix()->loadBlocks(
getIApp()->getAccounts()->twitch.getCurrent()->userId_,
[this](std::vector<HelixBlock> blocks) {
auto ignores = this->ignores_.access();
auto userIds = this->ignoresUserIds_.access();
ignores->clear();
userIds->clear();
[this](const std::vector<HelixBlock> &blocks) {
assertInGuiThread();

for (const HelixBlock &block : blocks)
{
TwitchUser blockedUser;
blockedUser.fromHelixBlock(block);
ignores->insert(blockedUser);
userIds->insert(blockedUser.id);
this->ignores_.insert(blockedUser);
this->ignoresUserIds_.insert(blockedUser.id);
}
},
[] {
qCWarning(chatterinoTwitch) << "Fetching blocks failed!";
});
[](auto error) {
qCWarning(chatterinoTwitch).noquote()
<< "Fetching blocks failed:" << error;
},
std::move(token));
}

void TwitchAccount::blockUser(QString userId, const QObject *caller,
void TwitchAccount::blockUser(const QString &userId, const QObject *caller,
std::function<void()> onSuccess,
std::function<void()> onFailure)
{
getHelix()->blockUser(
userId, caller,
[this, userId, onSuccess] {
[this, userId, onSuccess = std::move(onSuccess)] {
assertInGuiThread();

TwitchUser blockedUser;
blockedUser.id = userId;
{
auto ignores = this->ignores_.access();
auto userIds = this->ignoresUserIds_.access();

ignores->insert(blockedUser);
userIds->insert(blockedUser.id);
}
this->ignores_.insert(blockedUser);
this->ignoresUserIds_.insert(blockedUser.id);
onSuccess();
},
std::move(onFailure));
}

void TwitchAccount::unblockUser(QString userId, const QObject *caller,
void TwitchAccount::unblockUser(const QString &userId, const QObject *caller,
std::function<void()> onSuccess,
std::function<void()> onFailure)
{
getHelix()->unblockUser(
userId, caller,
[this, userId, onSuccess] {
[this, userId, onSuccess = std::move(onSuccess)] {
assertInGuiThread();

TwitchUser ignoredUser;
ignoredUser.id = userId;
{
auto ignores = this->ignores_.access();
auto userIds = this->ignoresUserIds_.access();

ignores->erase(ignoredUser);
userIds->erase(ignoredUser.id);
}
this->ignores_.erase(ignoredUser);
this->ignoresUserIds_.erase(ignoredUser.id);
onSuccess();
},
std::move(onFailure));
}

SharedAccessGuard<const std::set<TwitchUser>> TwitchAccount::accessBlocks()
const
const std::unordered_set<TwitchUser> &TwitchAccount::blocks() const
{
return this->ignores_.accessConst();
assertInGuiThread();
return this->ignores_;
}

SharedAccessGuard<const std::set<QString>> TwitchAccount::accessBlockedUserIds()
const
const std::unordered_set<QString> &TwitchAccount::blockedUserIds() const
{
return this->ignoresUserIds_.accessConst();
assertInGuiThread();
return this->ignoresUserIds_;
}

void TwitchAccount::loadEmotes(std::weak_ptr<Channel> weakChannel)
Expand Down
20 changes: 11 additions & 9 deletions src/providers/twitch/TwitchAccount.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "common/UniqueAccess.hpp"
#include "controllers/accounts/Account.hpp"
#include "messages/Emote.hpp"
#include "providers/twitch/TwitchUser.hpp"
#include "util/CancellationToken.hpp"
#include "util/QStringHash.hpp"

#include <QColor>
Expand All @@ -15,12 +17,11 @@

#include <functional>
#include <mutex>
#include <set>
#include <unordered_set>
#include <vector>

namespace chatterino {

struct TwitchUser;
class Channel;
using ChannelPtr = std::shared_ptr<Channel>;

Expand Down Expand Up @@ -72,15 +73,15 @@ class TwitchAccount : public Account
bool isAnon() const;

void loadBlocks();
void blockUser(QString userId, const QObject *caller,
void blockUser(const QString &userId, const QObject *caller,
std::function<void()> onSuccess,
std::function<void()> onFailure);
void unblockUser(QString userId, const QObject *caller,
void unblockUser(const QString &userId, const QObject *caller,
std::function<void()> onSuccess,
std::function<void()> onFailure);

SharedAccessGuard<const std::set<QString>> accessBlockedUserIds() const;
SharedAccessGuard<const std::set<TwitchUser>> accessBlocks() const;
[[nodiscard]] const std::unordered_set<TwitchUser> &blocks() const;
[[nodiscard]] const std::unordered_set<QString> &blockedUserIds() const;

void loadEmotes(std::weak_ptr<Channel> weakChannel = {});
// loadUserstateEmotes loads emote sets that are part of the USERSTATE emote-sets key
Expand All @@ -105,10 +106,11 @@ class TwitchAccount : public Account
const bool isAnon_;
Atomic<QColor> color_;

mutable std::mutex ignoresMutex_;
QStringList userstateEmoteSets_;
UniqueAccess<std::set<TwitchUser>> ignores_;
UniqueAccess<std::set<QString>> ignoresUserIds_;

ScopedCancellationToken blockToken_;
std::unordered_set<TwitchUser> ignores_;
std::unordered_set<QString> ignoresUserIds_;

// std::map<UserId, TwitchAccountEmoteData> emotes;
UniqueAccess<TwitchAccountEmoteData> emotes_;
Expand Down
Loading

0 comments on commit f207abd

Please sign in to comment.