mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
feat: add setting for showing pronouns in user info popup (#5442)
This uses https://pr.alejo.io/ Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
aae1288112
commit
9375bce555
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
## Unversioned
|
## Unversioned
|
||||||
|
|
||||||
|
- Major: Add option to show pronouns in user card. (#5442)
|
||||||
- Major: Release plugins alpha. (#5288)
|
- Major: Release plugins alpha. (#5288)
|
||||||
- Major: Improve high-DPI support on Windows. (#4868, #5391)
|
- Major: Improve high-DPI support on Windows. (#4868, #5391)
|
||||||
- Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530)
|
- Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530)
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "providers/chatterino/ChatterinoBadges.hpp"
|
#include "providers/chatterino/ChatterinoBadges.hpp"
|
||||||
#include "providers/ffz/FfzBadges.hpp"
|
#include "providers/ffz/FfzBadges.hpp"
|
||||||
#include "providers/ffz/FfzEmotes.hpp"
|
#include "providers/ffz/FfzEmotes.hpp"
|
||||||
|
#include "providers/pronouns/Pronouns.hpp"
|
||||||
#include "providers/recentmessages/Impl.hpp"
|
#include "providers/recentmessages/Impl.hpp"
|
||||||
#include "providers/seventv/SeventvBadges.hpp"
|
#include "providers/seventv/SeventvBadges.hpp"
|
||||||
#include "providers/seventv/SeventvEmotes.hpp"
|
#include "providers/seventv/SeventvEmotes.hpp"
|
||||||
|
@ -110,6 +111,11 @@ public:
|
||||||
return &this->linkResolver;
|
return &this->linkResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pronouns::Pronouns *getPronouns() override
|
||||||
|
{
|
||||||
|
return &this->pronouns;
|
||||||
|
}
|
||||||
|
|
||||||
AccountController accounts;
|
AccountController accounts;
|
||||||
Emotes emotes;
|
Emotes emotes;
|
||||||
mock::UserDataController userData;
|
mock::UserDataController userData;
|
||||||
|
@ -124,6 +130,7 @@ public:
|
||||||
FfzEmotes ffzEmotes;
|
FfzEmotes ffzEmotes;
|
||||||
SeventvEmotes seventvEmotes;
|
SeventvEmotes seventvEmotes;
|
||||||
DisabledStreamerMode streamerMode;
|
DisabledStreamerMode streamerMode;
|
||||||
|
pronouns::Pronouns pronouns;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<QJsonDocument> tryReadJsonFile(const QString &path)
|
std::optional<QJsonDocument> tryReadJsonFile(const QString &path)
|
||||||
|
|
|
@ -264,6 +264,13 @@ public:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pronouns::Pronouns *getPronouns() override
|
||||||
|
{
|
||||||
|
assert(false && "EmptyApplication::getPronouns was called without "
|
||||||
|
"being initialized");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
QTemporaryDir settingsDir;
|
QTemporaryDir settingsDir;
|
||||||
Paths paths_;
|
Paths paths_;
|
||||||
Args args_;
|
Args args_;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "providers/bttv/BttvEmotes.hpp"
|
#include "providers/bttv/BttvEmotes.hpp"
|
||||||
#include "providers/ffz/FfzEmotes.hpp"
|
#include "providers/ffz/FfzEmotes.hpp"
|
||||||
#include "providers/links/LinkResolver.hpp"
|
#include "providers/links/LinkResolver.hpp"
|
||||||
|
#include "providers/pronouns/Pronouns.hpp"
|
||||||
#include "providers/seventv/SeventvAPI.hpp"
|
#include "providers/seventv/SeventvAPI.hpp"
|
||||||
#include "providers/seventv/SeventvEmotes.hpp"
|
#include "providers/seventv/SeventvEmotes.hpp"
|
||||||
#include "providers/twitch/TwitchBadges.hpp"
|
#include "providers/twitch/TwitchBadges.hpp"
|
||||||
|
@ -178,6 +179,7 @@ Application::Application(Settings &_settings, const Paths &paths,
|
||||||
, linkResolver(new LinkResolver)
|
, linkResolver(new LinkResolver)
|
||||||
, streamerMode(new StreamerMode)
|
, streamerMode(new StreamerMode)
|
||||||
, twitchUsers(new TwitchUsers)
|
, twitchUsers(new TwitchUsers)
|
||||||
|
, pronouns(std::make_shared<pronouns::Pronouns>())
|
||||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||||
, plugins(new PluginController(paths))
|
, plugins(new PluginController(paths))
|
||||||
#endif
|
#endif
|
||||||
|
@ -565,6 +567,14 @@ SeventvEventAPI *Application::getSeventvEventAPI()
|
||||||
return this->seventvEventAPI.get();
|
return this->seventvEventAPI.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pronouns::Pronouns *Application::getPronouns()
|
||||||
|
{
|
||||||
|
// pronouns::Pronouns handles its own locks, so we don't need to assert that this is called in the GUI thread
|
||||||
|
assert(this->pronouns);
|
||||||
|
|
||||||
|
return this->pronouns.get();
|
||||||
|
}
|
||||||
|
|
||||||
void Application::save()
|
void Application::save()
|
||||||
{
|
{
|
||||||
this->commands->save();
|
this->commands->save();
|
||||||
|
|
|
@ -54,6 +54,9 @@ class SeventvEventAPI;
|
||||||
class ILinkResolver;
|
class ILinkResolver;
|
||||||
class IStreamerMode;
|
class IStreamerMode;
|
||||||
class ITwitchUsers;
|
class ITwitchUsers;
|
||||||
|
namespace pronouns {
|
||||||
|
class Pronouns;
|
||||||
|
} // namespace pronouns
|
||||||
|
|
||||||
class IApplication
|
class IApplication
|
||||||
{
|
{
|
||||||
|
@ -105,6 +108,7 @@ public:
|
||||||
virtual ILinkResolver *getLinkResolver() = 0;
|
virtual ILinkResolver *getLinkResolver() = 0;
|
||||||
virtual IStreamerMode *getStreamerMode() = 0;
|
virtual IStreamerMode *getStreamerMode() = 0;
|
||||||
virtual ITwitchUsers *getTwitchUsers() = 0;
|
virtual ITwitchUsers *getTwitchUsers() = 0;
|
||||||
|
virtual pronouns::Pronouns *getPronouns() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Application : public IApplication
|
class Application : public IApplication
|
||||||
|
@ -169,6 +173,7 @@ private:
|
||||||
std::unique_ptr<ILinkResolver> linkResolver;
|
std::unique_ptr<ILinkResolver> linkResolver;
|
||||||
std::unique_ptr<IStreamerMode> streamerMode;
|
std::unique_ptr<IStreamerMode> streamerMode;
|
||||||
std::unique_ptr<ITwitchUsers> twitchUsers;
|
std::unique_ptr<ITwitchUsers> twitchUsers;
|
||||||
|
std::shared_ptr<pronouns::Pronouns> pronouns;
|
||||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||||
std::unique_ptr<PluginController> plugins;
|
std::unique_ptr<PluginController> plugins;
|
||||||
#endif
|
#endif
|
||||||
|
@ -215,6 +220,7 @@ public:
|
||||||
FfzEmotes *getFfzEmotes() override;
|
FfzEmotes *getFfzEmotes() override;
|
||||||
SeventvEmotes *getSeventvEmotes() override;
|
SeventvEmotes *getSeventvEmotes() override;
|
||||||
SeventvEventAPI *getSeventvEventAPI() override;
|
SeventvEventAPI *getSeventvEventAPI() override;
|
||||||
|
pronouns::Pronouns *getPronouns() override;
|
||||||
|
|
||||||
ILinkResolver *getLinkResolver() override;
|
ILinkResolver *getLinkResolver() override;
|
||||||
IStreamerMode *getStreamerMode() override;
|
IStreamerMode *getStreamerMode() override;
|
||||||
|
|
|
@ -348,6 +348,13 @@ set(SOURCE_FILES
|
||||||
providers/liveupdates/BasicPubSubManager.hpp
|
providers/liveupdates/BasicPubSubManager.hpp
|
||||||
providers/liveupdates/BasicPubSubWebsocket.hpp
|
providers/liveupdates/BasicPubSubWebsocket.hpp
|
||||||
|
|
||||||
|
providers/pronouns/Pronouns.cpp
|
||||||
|
providers/pronouns/Pronouns.hpp
|
||||||
|
providers/pronouns/UserPronouns.cpp
|
||||||
|
providers/pronouns/UserPronouns.hpp
|
||||||
|
providers/pronouns/alejo/PronounsAlejoApi.cpp
|
||||||
|
providers/pronouns/alejo/PronounsAlejoApi.hpp
|
||||||
|
|
||||||
providers/recentmessages/Api.cpp
|
providers/recentmessages/Api.cpp
|
||||||
providers/recentmessages/Api.hpp
|
providers/recentmessages/Api.hpp
|
||||||
providers/recentmessages/Impl.cpp
|
providers/recentmessages/Impl.cpp
|
||||||
|
|
|
@ -37,6 +37,7 @@ Q_LOGGING_CATEGORY(chatterinoNotification, "chatterino.notification",
|
||||||
logThreshold);
|
logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoImageuploader, "chatterino.imageuploader",
|
Q_LOGGING_CATEGORY(chatterinoImageuploader, "chatterino.imageuploader",
|
||||||
logThreshold);
|
logThreshold);
|
||||||
|
Q_LOGGING_CATEGORY(chatterinoPronouns, "chatterino.pronouns", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoPubSub, "chatterino.pubsub", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoPubSub, "chatterino.pubsub", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoRecentMessages, "chatterino.recentmessages",
|
Q_LOGGING_CATEGORY(chatterinoRecentMessages, "chatterino.recentmessages",
|
||||||
logThreshold);
|
logThreshold);
|
||||||
|
|
|
@ -28,6 +28,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoMessage);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNetwork);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNetwork);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification);
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoPronouns);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoRecentMessages);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoRecentMessages);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoSettings);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoSettings);
|
||||||
|
|
64
src/providers/pronouns/Pronouns.cpp
Normal file
64
src/providers/pronouns/Pronouns.cpp
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#include "providers/pronouns/Pronouns.hpp"
|
||||||
|
|
||||||
|
#include "Application.hpp"
|
||||||
|
#include "common/QLogging.hpp"
|
||||||
|
#include "providers/pronouns/alejo/PronounsAlejoApi.hpp"
|
||||||
|
#include "providers/pronouns/UserPronouns.hpp"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace chatterino::pronouns {
|
||||||
|
|
||||||
|
void Pronouns::fetch(const QString &username,
|
||||||
|
const std::function<void(UserPronouns)> &callbackSuccess,
|
||||||
|
const std::function<void()> &callbackFail)
|
||||||
|
{
|
||||||
|
// Only fetch pronouns if we haven't fetched before.
|
||||||
|
{
|
||||||
|
std::shared_lock lock(this->mutex);
|
||||||
|
|
||||||
|
auto iter = this->saved.find(username);
|
||||||
|
if (iter != this->saved.end())
|
||||||
|
{
|
||||||
|
callbackSuccess(iter->second);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} // unlock mutex
|
||||||
|
|
||||||
|
qCDebug(chatterinoPronouns)
|
||||||
|
<< "Fetching pronouns from alejo.io for " << username;
|
||||||
|
|
||||||
|
alejoApi.fetch(username, [this, callbackSuccess, callbackFail,
|
||||||
|
username](std::optional<UserPronouns> result) {
|
||||||
|
if (result.has_value())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock lock(this->mutex);
|
||||||
|
this->saved[username] = *result;
|
||||||
|
} // unlock mutex
|
||||||
|
qCDebug(chatterinoPronouns)
|
||||||
|
<< "Adding pronouns " << result->format() << " for user "
|
||||||
|
<< username;
|
||||||
|
callbackSuccess(*result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callbackFail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<UserPronouns> Pronouns::getForUsername(const QString &username)
|
||||||
|
{
|
||||||
|
std::shared_lock lock(this->mutex);
|
||||||
|
auto it = this->saved.find(username);
|
||||||
|
if (it != this->saved.end())
|
||||||
|
{
|
||||||
|
return {it->second};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino::pronouns
|
31
src/providers/pronouns/Pronouns.hpp
Normal file
31
src/providers/pronouns/Pronouns.hpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "providers/pronouns/alejo/PronounsAlejoApi.hpp"
|
||||||
|
#include "providers/pronouns/UserPronouns.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace chatterino::pronouns {
|
||||||
|
|
||||||
|
class Pronouns
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pronouns() = default;
|
||||||
|
|
||||||
|
void fetch(const QString &username,
|
||||||
|
const std::function<void(UserPronouns)> &callbackSuccess,
|
||||||
|
const std::function<void()> &callbackFail);
|
||||||
|
|
||||||
|
// Retrieve cached pronouns for user.
|
||||||
|
std::optional<UserPronouns> getForUsername(const QString &username);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// mutex for editing the saved map.
|
||||||
|
std::shared_mutex mutex;
|
||||||
|
// Login name -> Pronouns
|
||||||
|
std::unordered_map<QString, UserPronouns> saved;
|
||||||
|
AlejoApi alejoApi;
|
||||||
|
};
|
||||||
|
} // namespace chatterino::pronouns
|
24
src/providers/pronouns/UserPronouns.cpp
Normal file
24
src/providers/pronouns/UserPronouns.cpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#include "providers/pronouns/UserPronouns.hpp"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace chatterino::pronouns {
|
||||||
|
|
||||||
|
UserPronouns::UserPronouns(QString pronouns)
|
||||||
|
: representation{!pronouns.isEmpty() ? std::move(pronouns) : QString()}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserPronouns::isUnspecified() const
|
||||||
|
{
|
||||||
|
return this->representation.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserPronouns::operator bool() const
|
||||||
|
{
|
||||||
|
return !isUnspecified();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino::pronouns
|
31
src/providers/pronouns/UserPronouns.hpp
Normal file
31
src/providers/pronouns/UserPronouns.hpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace chatterino::pronouns {
|
||||||
|
|
||||||
|
class UserPronouns
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UserPronouns() = default;
|
||||||
|
UserPronouns(QString pronouns);
|
||||||
|
|
||||||
|
QString format() const
|
||||||
|
{
|
||||||
|
if (isUnspecified())
|
||||||
|
{
|
||||||
|
return "unspecified";
|
||||||
|
}
|
||||||
|
return this->representation;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isUnspecified() const;
|
||||||
|
|
||||||
|
/// True, iff the pronouns are not unspecified.
|
||||||
|
operator bool() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString representation;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino::pronouns
|
124
src/providers/pronouns/alejo/PronounsAlejoApi.cpp
Normal file
124
src/providers/pronouns/alejo/PronounsAlejoApi.cpp
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
#include "providers/pronouns/alejo/PronounsAlejoApi.hpp"
|
||||||
|
|
||||||
|
#include "common/network/NetworkRequest.hpp"
|
||||||
|
#include "common/network/NetworkResult.hpp"
|
||||||
|
#include "common/QLogging.hpp"
|
||||||
|
#include "providers/pronouns/UserPronouns.hpp"
|
||||||
|
|
||||||
|
namespace chatterino::pronouns {
|
||||||
|
|
||||||
|
UserPronouns AlejoApi::parse(const QJsonObject &object)
|
||||||
|
{
|
||||||
|
if (!this->pronounsFromId.has_value())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pronoun = object["pronoun_id"];
|
||||||
|
|
||||||
|
if (!pronoun.isString())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pronounStr = pronoun.toString();
|
||||||
|
std::shared_lock lock(this->mutex);
|
||||||
|
auto iter = this->pronounsFromId->find(pronounStr);
|
||||||
|
if (iter != this->pronounsFromId->end())
|
||||||
|
{
|
||||||
|
return {iter->second};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AlejoApi::AlejoApi()
|
||||||
|
{
|
||||||
|
std::shared_lock lock(this->mutex);
|
||||||
|
if (this->pronounsFromId)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(chatterinoPronouns) << "Fetching available pronouns for alejo.io";
|
||||||
|
NetworkRequest(AlejoApi::API_URL + AlejoApi::API_PRONOUNS)
|
||||||
|
.concurrent()
|
||||||
|
.onSuccess([this](const auto &result) {
|
||||||
|
auto object = result.parseJson();
|
||||||
|
if (object.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock lock(this->mutex);
|
||||||
|
this->pronounsFromId = {std::unordered_map<QString, QString>()};
|
||||||
|
for (auto const &pronounId : object.keys())
|
||||||
|
{
|
||||||
|
if (!object[pronounId].isObject())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto pronounObj = object[pronounId].toObject();
|
||||||
|
|
||||||
|
if (!pronounObj["subject"].isString())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString pronouns = pronounObj["subject"].toString();
|
||||||
|
|
||||||
|
auto singular = pronounObj["singular"];
|
||||||
|
if (singular.isBool() && !singular.toBool() &&
|
||||||
|
pronounObj["object"].isString())
|
||||||
|
{
|
||||||
|
pronouns += "/" + pronounObj["object"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pronounsFromId->insert_or_assign(pronounId,
|
||||||
|
pronouns.toLower());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlejoApi::fetch(const QString &username,
|
||||||
|
std::function<void(std::optional<UserPronouns>)> onDone)
|
||||||
|
{
|
||||||
|
bool havePronounList{true};
|
||||||
|
{
|
||||||
|
std::shared_lock lock(this->mutex);
|
||||||
|
havePronounList = this->pronounsFromId.has_value();
|
||||||
|
} // unlock mutex
|
||||||
|
|
||||||
|
if (!havePronounList)
|
||||||
|
{
|
||||||
|
// Pronoun list not available yet, just fail and try again next time.
|
||||||
|
onDone({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequest(AlejoApi::API_URL + AlejoApi::API_USERS + "/" + username)
|
||||||
|
.concurrent()
|
||||||
|
.onSuccess([this, username, onDone](const auto &result) {
|
||||||
|
auto object = result.parseJson();
|
||||||
|
auto parsed = this->parse(object);
|
||||||
|
onDone({parsed});
|
||||||
|
})
|
||||||
|
.onError([onDone, username](auto result) {
|
||||||
|
auto status = result.status();
|
||||||
|
if (status.has_value() && status == 404)
|
||||||
|
{
|
||||||
|
// Alejo returns 404 if the user has no pronouns set.
|
||||||
|
// Return unspecified.
|
||||||
|
onDone({UserPronouns()});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qCWarning(chatterinoPronouns)
|
||||||
|
<< "alejo.io returned " << status.value_or(-1)
|
||||||
|
<< " when fetching pronouns for " << username;
|
||||||
|
onDone({});
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino::pronouns
|
35
src/providers/pronouns/alejo/PronounsAlejoApi.hpp
Normal file
35
src/providers/pronouns/alejo/PronounsAlejoApi.hpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "providers/pronouns/UserPronouns.hpp"
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
|
namespace chatterino::pronouns {
|
||||||
|
|
||||||
|
class AlejoApi
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AlejoApi();
|
||||||
|
/** Fetches pronouns from the alejo.io API for a username and calls onDone when done.
|
||||||
|
onDone can be invoked from any thread. The argument is std::nullopt if and only if the request failed.
|
||||||
|
*/
|
||||||
|
void fetch(const QString &username,
|
||||||
|
std::function<void(std::optional<UserPronouns>)> onDone);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_mutex mutex;
|
||||||
|
/** A map from alejo.io ids to human readable representation like theythem -> they/them, other -> other. */
|
||||||
|
std::optional<std::unordered_map<QString, QString>> pronounsFromId =
|
||||||
|
std::nullopt;
|
||||||
|
UserPronouns parse(const QJsonObject &object);
|
||||||
|
inline static const QString API_URL = "https://api.pronouns.alejo.io/v1";
|
||||||
|
inline static const QString API_USERS = "/users";
|
||||||
|
inline static const QString API_PRONOUNS = "/pronouns";
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino::pronouns
|
|
@ -555,6 +555,7 @@ public:
|
||||||
BoolSetting informOnTabVisibilityToggle = {"/misc/askOnTabVisibilityToggle",
|
BoolSetting informOnTabVisibilityToggle = {"/misc/askOnTabVisibilityToggle",
|
||||||
true};
|
true};
|
||||||
BoolSetting lockNotebookLayout = {"/misc/lockNotebookLayout", false};
|
BoolSetting lockNotebookLayout = {"/misc/lockNotebookLayout", false};
|
||||||
|
BoolSetting showPronouns = {"/misc/showPronouns", false};
|
||||||
|
|
||||||
/// UI
|
/// UI
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "messages/Message.hpp"
|
#include "messages/Message.hpp"
|
||||||
#include "messages/MessageBuilder.hpp"
|
#include "messages/MessageBuilder.hpp"
|
||||||
#include "providers/IvrApi.hpp"
|
#include "providers/IvrApi.hpp"
|
||||||
|
#include "providers/pronouns/Pronouns.hpp"
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
#include "providers/twitch/ChannelPointReward.hpp"
|
#include "providers/twitch/ChannelPointReward.hpp"
|
||||||
#include "providers/twitch/TwitchAccount.hpp"
|
#include "providers/twitch/TwitchAccount.hpp"
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
#include "util/Clipboard.hpp"
|
#include "util/Clipboard.hpp"
|
||||||
#include "util/Helpers.hpp"
|
#include "util/Helpers.hpp"
|
||||||
#include "util/LayoutCreator.hpp"
|
#include "util/LayoutCreator.hpp"
|
||||||
|
#include "util/PostToThread.hpp"
|
||||||
#include "widgets/helper/ChannelView.hpp"
|
#include "widgets/helper/ChannelView.hpp"
|
||||||
#include "widgets/helper/EffectLabel.hpp"
|
#include "widgets/helper/EffectLabel.hpp"
|
||||||
#include "widgets/helper/InvisibleSizeGrip.hpp"
|
#include "widgets/helper/InvisibleSizeGrip.hpp"
|
||||||
|
@ -47,6 +49,9 @@ constexpr QStringView TEXT_CREATED = u"Created: %1";
|
||||||
constexpr QStringView TEXT_TITLE = u"%1's Usercard - #%2";
|
constexpr QStringView TEXT_TITLE = u"%1's Usercard - #%2";
|
||||||
constexpr QStringView TEXT_USER_ID = u"ID: ";
|
constexpr QStringView TEXT_USER_ID = u"ID: ";
|
||||||
constexpr QStringView TEXT_UNAVAILABLE = u"(not available)";
|
constexpr QStringView TEXT_UNAVAILABLE = u"(not available)";
|
||||||
|
constexpr QStringView TEXT_PRONOUNS = u"Pronouns: %1";
|
||||||
|
constexpr QStringView TEXT_UNSPECIFIED = u"(unspecified)";
|
||||||
|
constexpr QStringView TEXT_LOADING = u"(loading...)";
|
||||||
|
|
||||||
using namespace chatterino;
|
using namespace chatterino;
|
||||||
|
|
||||||
|
@ -369,6 +374,11 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, Split *split)
|
||||||
}
|
}
|
||||||
|
|
||||||
// items on the left
|
// items on the left
|
||||||
|
if (getSettings()->showPronouns)
|
||||||
|
{
|
||||||
|
vbox.emplace<Label>(TEXT_PRONOUNS.arg(TEXT_LOADING))
|
||||||
|
.assign(&this->ui_.pronounsLabel);
|
||||||
|
}
|
||||||
vbox.emplace<Label>(TEXT_FOLLOWERS.arg(""))
|
vbox.emplace<Label>(TEXT_FOLLOWERS.arg(""))
|
||||||
.assign(&this->ui_.followerCountLabel);
|
.assign(&this->ui_.followerCountLabel);
|
||||||
vbox.emplace<Label>(TEXT_CREATED.arg(""))
|
vbox.emplace<Label>(TEXT_CREATED.arg(""))
|
||||||
|
@ -948,6 +958,43 @@ void UserInfoPopup::updateUserData()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[] {});
|
[] {});
|
||||||
|
|
||||||
|
// get pronouns
|
||||||
|
if (getSettings()->showPronouns)
|
||||||
|
{
|
||||||
|
getApp()->getPronouns()->fetch(
|
||||||
|
user.login,
|
||||||
|
[this, hack](const auto pronouns) {
|
||||||
|
runInGuiThread([this, hack,
|
||||||
|
pronouns = std::move(pronouns)]() {
|
||||||
|
if (!hack.lock() || this->ui_.pronounsLabel == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!pronouns.isUnspecified())
|
||||||
|
{
|
||||||
|
this->ui_.pronounsLabel->setText(
|
||||||
|
TEXT_PRONOUNS.arg(pronouns.format()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->ui_.pronounsLabel->setText(
|
||||||
|
TEXT_PRONOUNS.arg(TEXT_UNSPECIFIED));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[this, hack]() {
|
||||||
|
runInGuiThread([this, hack]() {
|
||||||
|
qCWarning(chatterinoTwitch) << "Error getting pronouns";
|
||||||
|
if (!hack.lock())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->ui_.pronounsLabel->setText(
|
||||||
|
TEXT_PRONOUNS.arg(TEXT_UNSPECIFIED));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!this->userId_.isEmpty())
|
if (!this->userId_.isEmpty())
|
||||||
|
|
|
@ -73,6 +73,7 @@ private:
|
||||||
|
|
||||||
Label *nameLabel = nullptr;
|
Label *nameLabel = nullptr;
|
||||||
Label *localizedNameLabel = nullptr;
|
Label *localizedNameLabel = nullptr;
|
||||||
|
Label *pronounsLabel = nullptr;
|
||||||
Label *followerCountLabel = nullptr;
|
Label *followerCountLabel = nullptr;
|
||||||
Label *createdDateLabel = nullptr;
|
Label *createdDateLabel = nullptr;
|
||||||
Label *userIDLabel = nullptr;
|
Label *userIDLabel = nullptr;
|
||||||
|
|
|
@ -1029,6 +1029,10 @@ void GeneralPage::initLayout(GeneralPageView &layout)
|
||||||
false,
|
false,
|
||||||
"Make all clickable links lowercase to deter "
|
"Make all clickable links lowercase to deter "
|
||||||
"phishing attempts.");
|
"phishing attempts.");
|
||||||
|
layout.addCheckbox(
|
||||||
|
"Show user's pronouns in user card", s.showPronouns, false,
|
||||||
|
"Shows users' pronouns in their user card. "
|
||||||
|
"Pronouns are retrieved from alejo.io when the user card is opened.");
|
||||||
layout.addCheckbox("Bold @usernames", s.boldUsernames, false,
|
layout.addCheckbox("Bold @usernames", s.boldUsernames, false,
|
||||||
"Bold @mentions to make them more noticable.");
|
"Bold @mentions to make them more noticable.");
|
||||||
layout.addCheckbox("Color @usernames", s.colorUsernames, false,
|
layout.addCheckbox("Color @usernames", s.colorUsernames, false,
|
||||||
|
|
Loading…
Reference in a new issue