Skip to content

Commit

Permalink
Allow themes to report feaures. e.g. reactions
Browse files Browse the repository at this point in the history
  • Loading branch information
Ri0n committed Jul 2, 2024
1 parent 89b5898 commit 4ab9960
Show file tree
Hide file tree
Showing 17 changed files with 175 additions and 69 deletions.
5 changes: 0 additions & 5 deletions src/chatviewcommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ class QWidget;
class ChatViewCommon {
public:
enum UserType { LocalParty, RemoteParty, Participant };
enum class Feature { Reactions = 0x1 };
Q_DECLARE_FLAGS(Features, Feature)

ChatViewCommon() : _nickNumber(0) { }
void setLooks(QWidget *);
Expand Down Expand Up @@ -64,11 +62,8 @@ class ChatViewCommon {
// to be called from UI stuff. return list of reactions to send over network
QSet<QString> onReactionSwitched(const QString &senderNickname, const QString &messageId, const QString &reaction);

Features features();

protected:
QDateTime _lastMsgTime;
Features _featuers;

private:
QList<QColor> &generatePalette();
Expand Down
7 changes: 6 additions & 1 deletion src/chatviewtheme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ ChatViewThemePrivate::ChatViewThemePrivate(ChatViewThemeProvider *provider) : Th

ChatViewThemePrivate::~ChatViewThemePrivate()
{
qDebug("ChatViewThemePrivate::~ChatViewThemePrivate");
// qDebug("ChatViewThemePrivate::~ChatViewThemePrivate");
delete wv;
}

Expand Down Expand Up @@ -447,6 +447,11 @@ void ChatViewJSLoader::setMetaData(const QVariantMap &map)
if (!v.isEmpty()) {
theme->homeUrl = v;
}

vl = map["features"].toStringList();
if (vl.count()) {
theme->features = vl;
}
}

void ChatViewJSLoader::finishThemeLoading()
Expand Down
56 changes: 35 additions & 21 deletions src/chatviewthemeprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,49 +85,63 @@ Theme ChatViewThemeProvider::theme(const QString &id)
}

/**
* @brief Load theme from settings or classic on failure.
* Signal themeChanged when necessary
* @brief Load theme from settings or New Classic on failure.
* Signals themeChanged if different theme was loaded before.
*
* @return false on failure to load any theme
*/
bool ChatViewThemeProvider::loadCurrent()
PsiThemeProvider::LoadRestult ChatViewThemeProvider::loadCurrent()
{
QString loadedId = curTheme.id();
QString themeName = PsiOptions::instance()->getOption(optionString()).toString();
if (!loadedId.isEmpty() && loadedId == themeName) {
return true; // already loaded. nothing todo
return LoadSuccess; // already loaded. nothing todo
}
Theme t(theme(themeName));
if (!t.exists()) {
if (themeName != QLatin1String("psi/classic")) {
auto newClassic = QStringLiteral("psi/new_classic");
curLoadingTheme = theme(themeName); // may trigger destructor of prev curLoadingTheme
if (!curLoadingTheme.exists()) {
if (themeName != newClassic) {
qDebug("Invalid theme id: %s", qPrintable(themeName));
qDebug("fallback to classic chatview theme");
PsiOptions::instance()->setOption(optionString(), QLatin1String("psi/classic"));
return loadCurrent();
qDebug("fallback to new_classic chatview theme");
PsiOptions::instance()->setOption(optionString(), newClassic);
curLoadingTheme = theme(newClassic);
} else {
qDebug("New Classic theme failed to load. No fallback..");
return LoadFailure;
}
qDebug("Classic theme failed to load. No fallback..");
return false;
}

bool startedLoading = t.load([this, t, loadedId](bool success) {
if (!success && t.id() != QLatin1String("psi/classic")) {
qDebug("Failed to load theme \"%s\"", qPrintable(t.id()));
qDebug("fallback to classic chatview theme");
PsiOptions::instance()->setOption(optionString(), QLatin1String("psi/classic"));
loadCurrent();
} else if (success) {
bool startedLoading = curLoadingTheme.load([this, loadedId, newClassic](bool success) {
auto t = curLoadingTheme;
curLoadingTheme = {};
if (success) {
curTheme = t;
if (t.id() != loadedId) {
emit themeChanged();
}
} // else it was already classic
return;
}
if (t.id() != newClassic) {
qDebug("Failed to load theme \"%s\"", qPrintable(t.id()));
qDebug("fallback to classic chatview theme");
PsiOptions::instance()->setOption(optionString(), newClassic);
loadCurrent();
} else {
// nowhere to fallback
emit currentLoadFailed();
}
});

return startedLoading; // does not really matter. may fail later on loading
if (startedLoading) {
return LoadInProgress; // does not really matter. may fail later on loading
}
return LoadFailure;
}

void ChatViewThemeProvider::unloadCurrent() { curTheme = Theme(); }

void ChatViewThemeProvider::cancelCurrentLoading() { curLoadingTheme = {}; /* should call destructor */ }

Theme ChatViewThemeProvider::current() const { return curTheme; }

void ChatViewThemeProvider::setCurrentTheme(const QString &id)
Expand Down
11 changes: 5 additions & 6 deletions src/chatviewthemeprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ class ChatViewThemeProvider : public PsiThemeProvider {
const QStringList themeIds() const;
Theme theme(const QString &id);

bool loadCurrent();
void unloadCurrent();
Theme current() const; // currently loaded theme
LoadRestult loadCurrent();
void unloadCurrent();
void cancelCurrentLoading();
Theme current() const; // currently loaded theme

void setCurrentTheme(const QString &);
virtual int screenshotWidth() const { return 512; } // hack
Expand All @@ -54,11 +55,9 @@ class ChatViewThemeProvider : public PsiThemeProvider {
protected:
virtual const char *optionString() const { return "options.ui.chat.theme"; }

signals:
void themeChanged();

private:
Theme curTheme;
Theme curLoadingTheme; // load-in-progress theme to replace cutTheme in success
};

class GroupChatViewThemeProvider : public ChatViewThemeProvider {
Expand Down
39 changes: 27 additions & 12 deletions src/psiaccount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "fileutil.h"
#include "geolocationdlg.h"
#include "iris/bsocket.h"
#include "psithememanager.h"
#include "xmpp/xmpp-im/xmpp_vcard4.h"
#ifdef GOOGLE_FT
#include "googleftmanager.h"
Expand Down Expand Up @@ -1075,6 +1076,12 @@ PsiAccount::PsiAccount(const UserAccount &acc, PsiContactList *parent, TabManage
// another hack. We rather should have PsiMedia single instance as a member of PsiCon
connect(MediaDeviceWatcher::instance(), &MediaDeviceWatcher::availibityChanged, this, &PsiAccount::updateFeatures);

#ifdef WEBKIT
connect(d->psi->themeManager()->provider("chatview"), &PsiThemeProvider::themeChanged, this,
&PsiAccount::updateFeatures);
connect(d->psi->themeManager()->provider("groupchatview"), &PsiThemeProvider::themeChanged, this,
&PsiAccount::updateFeatures);
#endif
#ifdef FILETRANSFER
d->client->setFileTransferEnabled(true);
#else
Expand Down Expand Up @@ -1547,22 +1554,30 @@ void PsiAccount::updateFeatures()
#endif

#ifdef USE_PEP
features << "http://jabber.org/protocol/mood"
<< "http://jabber.org/protocol/activity";
features << "http://jabber.org/protocol/tune"
<< "http://jabber.org/protocol/geoloc";
features << "urn:xmpp:avatar:data"
<< "urn:xmpp:avatar:metadata";
features << QLatin1String("http://jabber.org/protocol/mood") << QLatin1String("http://jabber.org/protocol/activity")
<< QLatin1String("http://jabber.org/protocol/tune") << QLatin1String("http://jabber.org/protocol/geoloc")
<< QLatin1String("urn:xmpp:avatar:data") << QLatin1String("urn:xmpp:avatar:metadata");
#endif
if (AvCallManager::isSupported()) {
features << "urn:xmpp:jingle:transports:ice-udp:1";
features << "urn:xmpp:jingle:transports:ice:0";
features << "urn:xmpp:jingle:apps:rtp:1";
features << "urn:xmpp:jingle:apps:rtp:audio";
features << "urn:xmpp:jingle:apps:rtp:video";
features << QLatin1String("urn:xmpp:jingle:transports:ice-udp:1");
features << QLatin1String("urn:xmpp:jingle:transports:ice:0");
features << QLatin1String("urn:xmpp:jingle:apps:rtp:1");
features << QLatin1String("urn:xmpp:jingle:apps:rtp:audio");
features << QLatin1String("urn:xmpp:jingle:apps:rtp:video");
}

features << "jabber:x:conference"; // allow direct invites
features << QLatin1String("jabber:x:conference"); // allow direct invites

#ifdef WEBKIT
auto gcTheme = psi()->themeManager()->provider("groupchatview")->current();
auto chatTheme = psi()->themeManager()->provider("chatview")->current();
if (gcTheme && chatTheme) {
auto themeFeatures = gcTheme.features() + chatTheme.features();
if (themeFeatures.contains(QStringLiteral("reactions"))) {
features << QLatin1String("urn:xmpp:reactions:0");
}
}
#endif

// TODO reset hash
d->client->setFeatures(Features(features));
Expand Down
15 changes: 13 additions & 2 deletions src/psicon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -558,11 +558,22 @@ bool PsiCon::init()
d->themeManager->registerProvider(new GroupChatViewThemeProvider(this), true);
#endif

if (!d->themeManager->loadAll()) {
const auto reportThemeError = [this]() {
QMessageBox::critical(nullptr, tr("Error"),
tr("Unable to load theme! Please make sure Psi is properly installed."));
tr("Unable to load \"%1\" theme! Please make sure Psi is properly installed.")
.arg(d->themeManager->failedId()));
};
auto themeLoadResult = d->themeManager->loadAll();
if (themeLoadResult == PsiThemeProvider::LoadFailure) {
reportThemeError();
result = false;
}
if (themeLoadResult == PsiThemeProvider::LoadInProgress) {
connect(d->themeManager, &PsiThemeManager::currentLoadFailed, this, [this, reportThemeError]() {
reportThemeError();
closeProgram();
});
}

if (!d->actionList)
d->actionList = new PsiActionList(this);
Expand Down
60 changes: 54 additions & 6 deletions src/psithememanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class PsiThemeManager::Private {
public:
QMap<QString, PsiThemeProvider *> providers;
QSet<QString> required;
QString failedId;
QString errorString;
PsiThemeProvider::LoadRestult loadResult = PsiThemeProvider::LoadNotStarted;
};

//---------------------------------------------------------
Expand Down Expand Up @@ -61,13 +64,58 @@ PsiThemeProvider *PsiThemeManager::provider(const QString &type) { return d->pro

QList<PsiThemeProvider *> PsiThemeManager::registeredProviders() const { return d->providers.values(); }

bool PsiThemeManager::loadAll()
PsiThemeProvider::LoadRestult PsiThemeManager::loadAll()
{
const auto &types = d->providers.keys();
for (const QString &type : types) {
if (!d->providers[type]->loadCurrent() && d->required.contains(type)) {
return false;
d->loadResult = PsiThemeProvider::LoadInProgress;
QObject *ctx = nullptr;
auto pending = std::shared_ptr<QList<PsiThemeProvider *>> { new QList<PsiThemeProvider *>() };
auto cleanup = [this, pending](QObject *ctx, PsiThemeProvider *provider, bool failure) {
if (failure) {
d->failedId = QLatin1String(provider->type());
for (auto p : *pending) {
p->cancelCurrentLoading();
p->disconnect(ctx);
}
ctx->deleteLater();
d->loadResult = PsiThemeProvider::LoadFailure;
emit currentLoadFailed();
return;
}
pending->removeOne(provider);
provider->disconnect(ctx);
if (pending->isEmpty()) {
ctx->deleteLater();
d->loadResult = PsiThemeProvider::LoadSuccess;
emit currentLoadSuccess();
}
};
for (auto it = d->providers.begin(); it != d->providers.end(); ++it) {
auto provider = it.value();
auto required = d->required.contains(it.key());
auto status = provider->loadCurrent();
if (status == PsiThemeProvider::LoadFailure && required) {
d->failedId = QLatin1String(provider->type());
d->loadResult = PsiThemeProvider::LoadFailure;
return PsiThemeProvider::LoadFailure;
}
if (status == PsiThemeProvider::LoadSuccess) {
continue;
}
// in progress
pending->append(provider);

if (!ctx) {
ctx = new QObject(this);
}
connect(
provider, &PsiThemeProvider::themeChanged, ctx,
[ctx, provider, cleanup]() { cleanup(ctx, provider, false); }, Qt::SingleShotConnection);
connect(
provider, &PsiThemeProvider::currentLoadFailed, ctx,
[ctx, provider, required, cleanup]() { cleanup(ctx, provider, required); }, Qt::SingleShotConnection);
}
return true;
d->loadResult = pending->isEmpty() ? PsiThemeProvider::LoadSuccess : PsiThemeProvider::LoadInProgress;
return d->loadResult;
}

QString PsiThemeManager::failedId() const { return d->failedId; }
18 changes: 12 additions & 6 deletions src/psithememanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@ class PsiThemeManager : public QObject {
PsiThemeManager(QObject *parent);
~PsiThemeManager();

void registerProvider(PsiThemeProvider *provider, bool required = false);
PsiThemeProvider *unregisterProvider(const QString &type);
PsiThemeProvider *provider(const QString &type);
QList<PsiThemeProvider *> registeredProviders() const;
bool loadAll();

void registerProvider(PsiThemeProvider *provider, bool required = false);
PsiThemeProvider *unregisterProvider(const QString &type);
PsiThemeProvider *provider(const QString &type);
QList<PsiThemeProvider *> registeredProviders() const;
PsiThemeProvider::LoadRestult loadAll();
QString failedId() const;

signals:
void currentLoadSuccess();
void currentLoadFailed();

private:
class Private;
Private *d;
};
Expand Down
1 change: 1 addition & 0 deletions src/psithememodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ struct PsiThemeModel::Loader {
ti.authors = theme.authors();
ti.creation = theme.creation();
ti.homeUrl = theme.homeUrl();
ti.features = theme.features();

ti.hasPreview = theme.hasPreview();
ti.isValid = true;
Expand Down
1 change: 1 addition & 0 deletions src/psithememodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct ThemeItemInfo {
QStringList authors;
QString creation;
QString homeUrl;
QStringList features;

bool hasPreview;
bool isValid = false;
Expand Down
10 changes: 8 additions & 2 deletions src/psithemeprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include "theme.h"

#include <QFuture>
#include <functional>

class PsiCon;

Expand All @@ -33,15 +32,18 @@ class PsiThemeProvider : public QObject {
PsiCon *_psi;

public:
enum LoadRestult { LoadNotStarted, LoadSuccess, LoadFailure, LoadInProgress };

PsiThemeProvider(PsiCon *parent);

inline PsiCon *psi() const { return _psi; }

virtual const char *type() const = 0;
virtual Theme theme(const QString &id) = 0; // make new theme
virtual const QStringList themeIds() const = 0;
virtual bool loadCurrent() = 0;
virtual LoadRestult loadCurrent() = 0;
virtual void unloadCurrent() = 0;
virtual void cancelCurrentLoading() = 0;
virtual Theme current() const = 0;
virtual void setCurrentTheme(const QString &) = 0;

Expand All @@ -52,6 +54,10 @@ class PsiThemeProvider : public QObject {
virtual QString optionsDescription() const = 0;

static QString themePath(const QString &name);

signals:
void themeChanged();
void currentLoadFailed(); // loading of the current therme has failed
};

#endif // PSITHEMEPROVIDER_H
Loading

0 comments on commit 4ab9960

Please sign in to comment.