mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
feat: add shared chat badge
This commit is contained in:
parent
dab97b3235
commit
f4036b269b
|
@ -71,6 +71,17 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void remove(const key_t &key)
|
||||
{
|
||||
auto it = _cache_items_map.find(key);
|
||||
if (it == _cache_items_map.end())
|
||||
{
|
||||
throw std::range_error("There is no such key in cache");
|
||||
}
|
||||
_cache_items_list.erase(it->second);
|
||||
_cache_items_map.erase(it);
|
||||
}
|
||||
|
||||
const value_t &get(const key_t &key)
|
||||
{
|
||||
auto it = _cache_items_map.find(key);
|
||||
|
|
BIN
resources/twitch/sharedChat.png
Normal file
BIN
resources/twitch/sharedChat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2 KiB |
|
@ -384,6 +384,17 @@ EmotePtr makeAutoModBadge()
|
|||
Url{"https://dashboard.twitch.tv/settings/moderation/automod"}});
|
||||
}
|
||||
|
||||
EmotePtr makeSharedChatBadge(const QString &sourceName)
|
||||
{
|
||||
return std::make_shared<Emote>(
|
||||
Emote{"SharedChat_" + sourceName,
|
||||
ImageSet{Image::fromResourcePixmap(
|
||||
getResources().twitch.sharedChat, 0.25)},
|
||||
Tooltip{"Shared Message" +
|
||||
(sourceName.isEmpty() ? "" : " from " + sourceName)},
|
||||
Url{"https://link.twitch.tv/SharedChatViewer"}});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino {
|
||||
|
@ -1156,6 +1167,14 @@ MessagePtr MessageBuilder::build()
|
|||
this->emplace<TwitchModerationElement>();
|
||||
}
|
||||
|
||||
if (this->sourceName.has_value())
|
||||
{
|
||||
this->emplace<BadgeElement>(
|
||||
makeSharedChatBadge(this->sourceName.value()),
|
||||
MessageElementFlag::BadgeSharedChannel)
|
||||
->setLink({Link::UserInfo, sourceName.value()});
|
||||
}
|
||||
|
||||
this->appendTwitchBadges();
|
||||
|
||||
this->appendChatterinoBadges();
|
||||
|
@ -2242,16 +2261,23 @@ void MessageBuilder::parseRoomID()
|
|||
{
|
||||
this->message().flags.set(MessageFlag::SharedMessage);
|
||||
|
||||
auto sourceChan =
|
||||
getApp()->getTwitch()->getChannelOrEmptyByID(sourceRoom);
|
||||
auto *twitch = getApp()->getTwitch();
|
||||
auto sourceChan = twitch->getChannelOrEmptyByID(sourceRoom);
|
||||
if (sourceChan && !sourceChan->isEmpty())
|
||||
{
|
||||
this->sourceName = sourceChan->getName();
|
||||
this->sourceChannel =
|
||||
dynamic_cast<TwitchChannel *>(sourceChan.get());
|
||||
|
||||
// avoid duplicate pings
|
||||
this->message().flags.set(
|
||||
MessageFlag::DoNotTriggerNotification);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->sourceName =
|
||||
twitch->getOrPopulateChannelCache(sourceRoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,6 +157,7 @@ public:
|
|||
TwitchChannel *twitchChannel = nullptr;
|
||||
/// The Twitch Channel the message was sent in, according to the Shared Chat feature
|
||||
TwitchChannel *sourceChannel = nullptr;
|
||||
std::optional<QString> sourceName = std::nullopt;
|
||||
|
||||
Message *operator->();
|
||||
Message &message();
|
||||
|
|
|
@ -66,6 +66,10 @@ enum class MessageElementFlag : int64_t {
|
|||
BitsStatic = (1LL << 11),
|
||||
BitsAnimated = (1LL << 12),
|
||||
|
||||
// Slot 0: Twitch
|
||||
// - Shared Channel indicator badge
|
||||
BadgeSharedChannel = (1LL << 37),
|
||||
|
||||
// Slot 1: Twitch
|
||||
// - Staff badge
|
||||
// - Admin badge
|
||||
|
@ -119,7 +123,7 @@ enum class MessageElementFlag : int64_t {
|
|||
|
||||
Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority |
|
||||
BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV |
|
||||
BadgeFfz,
|
||||
BadgeFfz | BadgeSharedChannel,
|
||||
|
||||
ChannelName = (1LL << 20),
|
||||
|
||||
|
|
|
@ -153,6 +153,7 @@ TwitchIrcServer::TwitchIrcServer()
|
|||
, liveChannel(new Channel("/live", Channel::Type::TwitchLive))
|
||||
, automodChannel(new Channel("/automod", Channel::Type::TwitchAutomod))
|
||||
, watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching)
|
||||
, channelNamesById_(512)
|
||||
{
|
||||
// Initialize the connections
|
||||
// XXX: don't create write connection if there is no separate write connection.
|
||||
|
@ -1128,6 +1129,38 @@ std::shared_ptr<Channel> TwitchIrcServer::getChannelOrEmptyByID(
|
|||
return Channel::getEmpty();
|
||||
}
|
||||
|
||||
std::optional<QString> TwitchIrcServer::getOrPopulateChannelCache(
|
||||
const QString &channelId)
|
||||
{
|
||||
{
|
||||
const auto cache = this->channelNamesById_.access();
|
||||
if (cache->exists(channelId))
|
||||
{
|
||||
return cache->get(channelId);
|
||||
}
|
||||
|
||||
// prevent multiple helix requests for single user
|
||||
cache->put(channelId, "");
|
||||
}
|
||||
|
||||
getHelix()->getUserById(
|
||||
channelId,
|
||||
[this](const HelixUser &user) {
|
||||
const auto cache = this->channelNamesById_.access();
|
||||
cache->put(user.id, user.login);
|
||||
},
|
||||
[this, &channelId] {
|
||||
const auto cache = this->channelNamesById_.access();
|
||||
if (cache->exists(channelId) && cache->get(channelId).isEmpty())
|
||||
{
|
||||
// invalidate cache so another helix request can be attempted
|
||||
cache->remove(channelId);
|
||||
}
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QString TwitchIrcServer::cleanChannelName(const QString &dirtyChannelName)
|
||||
{
|
||||
if (dirtyChannelName.startsWith('#'))
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
#include "common/Atomic.hpp"
|
||||
#include "common/Channel.hpp"
|
||||
#include "common/Common.hpp"
|
||||
#include "common/UniqueAccess.hpp"
|
||||
#include "providers/irc/IrcConnection2.hpp"
|
||||
#include "util/RatelimitBucket.hpp"
|
||||
|
||||
#include <IrcMessage>
|
||||
#include <lrucache/lrucache.hpp>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
#include <pajlada/signals/signalholder.hpp>
|
||||
|
||||
|
@ -43,6 +45,9 @@ public:
|
|||
virtual ChannelPtr getOrAddChannel(const QString &dirtyChannelName) = 0;
|
||||
virtual ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) = 0;
|
||||
|
||||
virtual std::optional<QString> getOrPopulateChannelCache(
|
||||
const QString &channelId) = 0;
|
||||
|
||||
virtual void addFakeMessage(const QString &data) = 0;
|
||||
|
||||
virtual void addGlobalSystemMessage(const QString &messageText) = 0;
|
||||
|
@ -95,6 +100,9 @@ public:
|
|||
std::shared_ptr<Channel> getChannelOrEmptyByID(
|
||||
const QString &channelID) override;
|
||||
|
||||
std::optional<QString> getOrPopulateChannelCache(
|
||||
const QString &channelId) override;
|
||||
|
||||
void reloadAllBTTVChannelEmotes();
|
||||
void reloadAllFFZChannelEmotes();
|
||||
void reloadAllSevenTVChannelEmotes();
|
||||
|
@ -190,6 +198,9 @@ private:
|
|||
// https://dev.twitch.tv/docs/irc/guide#rate-limits
|
||||
QObjectPtr<RatelimitBucket> joinBucket_;
|
||||
|
||||
// cached channel id => name for resolving Shared Chat members
|
||||
UniqueAccess<cache::lru_cache<QString, QString>> channelNamesById_;
|
||||
|
||||
QTimer reconnectTimer_;
|
||||
int falloffCounter_ = 1;
|
||||
|
||||
|
|
|
@ -195,6 +195,7 @@ void WindowManager::updateWordTypeMask()
|
|||
flags.set(settings->animateEmotes ? MEF::BitsAnimated : MEF::BitsStatic);
|
||||
|
||||
// badges
|
||||
flags.set(MEF::BadgeSharedChannel);
|
||||
flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority
|
||||
: MEF::None);
|
||||
flags.set(settings->showBadgesPredictions ? MEF::BadgePredictions
|
||||
|
|
Loading…
Reference in a new issue