fix gmail attachment names encoding + other gmail stuff

This commit is contained in:
Martin Rotter 2023-04-20 08:46:50 +02:00
parent abebbf7e56
commit fb2c439b40
10 changed files with 49 additions and 23 deletions

View File

@ -6,6 +6,7 @@ Added:
* macOS build is now built with macOS 11. * macOS build is now built with macOS 11.
* Linux build is now built with Ubuntu 20.04 which is now oldest supported Linux LTS distribution. * Linux build is now built with Ubuntu 20.04 which is now oldest supported Linux LTS distribution.
* Reworked logic of determining which article states (read/unread/starred/unstarred) have changed when synchronizing states to remote feed servers. This concerns all synchronized plugins like Gmail, Greader, Feedly, etc. and show in theory lead to much better performance when you mark many articles as read or unread. * Reworked logic of determining which article states (read/unread/starred/unstarred) have changed when synchronizing states to remote feed servers. This concerns all synchronized plugins like Gmail, Greader, Feedly, etc. and show in theory lead to much better performance when you mark many articles as read or unread.
* Couple of extra QoL enhancements for Gmail like proper encoding of attachment filenames, turned off displaying of "enclosures" as these are displayed in other way, etc.
Fixed: Fixed:
* Deadlock when fetching feeds in some corner situations is now resolved. (#910) * Deadlock when fetching feeds in some corner situations is now resolved. (#910)

View File

@ -17,7 +17,6 @@
Enclosure::Enclosure(QString url, QString mime) : m_url(std::move(url)), m_mimeType(std::move(mime)) {} Enclosure::Enclosure(QString url, QString mime) : m_url(std::move(url)), m_mimeType(std::move(mime)) {}
QList<Enclosure> Enclosures::decodeEnclosuresFromString(const QString& enclosures_data) { QList<Enclosure> Enclosures::decodeEnclosuresFromString(const QString& enclosures_data) {
QList<Enclosure> enclosures;
auto enc = enclosures_data.split(ENCLOSURES_OUTER_SEPARATOR, auto enc = enclosures_data.split(ENCLOSURES_OUTER_SEPARATOR,
#if QT_VERSION >= 0x050F00 // Qt >= 5.15.0 #if QT_VERSION >= 0x050F00 // Qt >= 5.15.0
Qt::SplitBehaviorFlags::SkipEmptyParts); Qt::SplitBehaviorFlags::SkipEmptyParts);
@ -25,17 +24,20 @@ QList<Enclosure> Enclosures::decodeEnclosuresFromString(const QString& enclosure
QString::SplitBehavior::SkipEmptyParts); QString::SplitBehavior::SkipEmptyParts);
#endif #endif
QList<Enclosure> enclosures;
enclosures.reserve(enc.size());
for (const QString& single_enclosure : qAsConst(enc)) { for (const QString& single_enclosure : qAsConst(enc)) {
Enclosure enclosure; Enclosure enclosure;
if (single_enclosure.contains(ECNLOSURES_INNER_SEPARATOR)) { if (single_enclosure.contains(ECNLOSURES_INNER_SEPARATOR)) {
QStringList mime_url = single_enclosure.split(ECNLOSURES_INNER_SEPARATOR); QStringList mime_url = single_enclosure.split(ECNLOSURES_INNER_SEPARATOR);
enclosure.m_mimeType = QByteArray::fromBase64(mime_url.at(0).toLocal8Bit()); enclosure.m_mimeType = QString::fromUtf8(QByteArray::fromBase64(mime_url.at(0).toLocal8Bit()));
enclosure.m_url = QByteArray::fromBase64(mime_url.at(1).toLocal8Bit()); enclosure.m_url = QString::fromUtf8(QByteArray::fromBase64(mime_url.at(1).toLocal8Bit()));
} }
else { else {
enclosure.m_url = QByteArray::fromBase64(single_enclosure.toLocal8Bit()); enclosure.m_url = QString::fromUtf8(QByteArray::fromBase64(single_enclosure.toLocal8Bit()));
} }
enclosures.append(enclosure); enclosures.append(enclosure);
@ -49,11 +51,11 @@ QString Enclosures::encodeEnclosuresToString(const QList<Enclosure>& enclosures)
for (const Enclosure& enclosure : enclosures) { for (const Enclosure& enclosure : enclosures) {
if (enclosure.m_mimeType.isEmpty()) { if (enclosure.m_mimeType.isEmpty()) {
enclosures_str.append(enclosure.m_url.toLocal8Bit().toBase64()); enclosures_str.append(enclosure.m_url.toUtf8().toBase64());
} }
else { else {
enclosures_str.append(QString(enclosure.m_mimeType.toLocal8Bit().toBase64()) + ECNLOSURES_INNER_SEPARATOR + enclosures_str.append(QString(enclosure.m_mimeType.toUtf8().toBase64()) + ECNLOSURES_INNER_SEPARATOR +
enclosure.m_url.toLocal8Bit().toBase64()); enclosure.m_url.toUtf8().toBase64());
} }
} }

View File

@ -14,7 +14,7 @@
<item> <item>
<widget class="QTabWidget" name="m_tabFeedsMessages"> <widget class="QTabWidget" name="m_tabFeedsMessages">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="m_tabFeeds"> <widget class="QWidget" name="m_tabFeeds">
<attribute name="title"> <attribute name="title">
@ -300,7 +300,7 @@
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="m_cbShowEnclosuresDirectly"> <widget class="QCheckBox" name="m_cbShowEnclosuresDirectly">
<property name="text"> <property name="text">
<string>Display attached pictures directly in article</string> <string>Display attachments directly in article</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -88,6 +88,8 @@ QVariant TextBrowserViewer::loadOneResource(int type, const QUrl& name) {
PreparedHtml TextBrowserViewer::prepareHtmlForMessage(const QList<Message>& messages, RootItem* selected_item) const { PreparedHtml TextBrowserViewer::prepareHtmlForMessage(const QList<Message>& messages, RootItem* selected_item) const {
PreparedHtml html; PreparedHtml html;
bool acc_displays_enclosures =
selected_item == nullptr || selected_item->getParentServiceRoot()->displaysEnclosures();
for (const Message& message : messages) { for (const Message& message : messages) {
bool is_plain = !TextFactory::couldBeHtml(message.m_contents); bool is_plain = !TextFactory::couldBeHtml(message.m_contents);
@ -104,14 +106,17 @@ PreparedHtml TextBrowserViewer::prepareHtmlForMessage(const QList<Message>& mess
html.m_html += QSL("<div>"); html.m_html += QSL("<div>");
// Add links to enclosures. // Add links to enclosures.
if (acc_displays_enclosures) {
for (const Enclosure& enc : message.m_enclosures) { for (const Enclosure& enc : message.m_enclosures) {
html.m_html += QSL("[%2] <a href=\"%1\">%1</a><br/>").arg(enc.m_url, enc.m_mimeType); html.m_html += QSL("[%2] <a href=\"%1\">%1</a><br/>").arg(enc.m_url, enc.m_mimeType);
} }
}
// Display enclosures which are pictures if user has it enabled. // Display enclosures which are pictures if user has it enabled.
auto first_enc_break_added = false; auto first_enc_break_added = false;
if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayEnclosuresInMessage)).toBool()) { if (acc_displays_enclosures &&
qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayEnclosuresInMessage)).toBool()) {
for (const Enclosure& enc : message.m_enclosures) { for (const Enclosure& enc : message.m_enclosures) {
if (enc.m_mimeType.startsWith(QSL("image/"))) { if (enc.m_mimeType.startsWith(QSL("image/"))) {
if (!first_enc_break_added) { if (!first_enc_break_added) {

View File

@ -230,11 +230,13 @@ PreparedHtml SkinFactory::generateHtmlOfArticles(const QList<Message>& messages,
QString enclosure_images; QString enclosure_images;
bool is_plain = !TextFactory::couldBeHtml(message.m_contents); bool is_plain = !TextFactory::couldBeHtml(message.m_contents);
if (root == nullptr || root->getParentServiceRoot()->displaysEnclosures()) {
for (const Enclosure& enclosure : message.m_enclosures) { for (const Enclosure& enclosure : message.m_enclosures) {
QString enc_url = QUrl::fromPercentEncoding(enclosure.m_url.toUtf8()); QString enc_url = QUrl::fromPercentEncoding(enclosure.m_url.toUtf8());
enclosures += skin.m_enclosureMarkup.arg(enc_url, QSL("&#129527;"), enclosure.m_mimeType); enclosures += skin.m_enclosureMarkup.arg(enc_url, QSL("&#129527;"), enclosure.m_mimeType);
if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayEnclosuresInMessage)).toBool()) {
if (enclosure.m_mimeType.startsWith(QSL("image/")) && if (enclosure.m_mimeType.startsWith(QSL("image/")) &&
qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayEnclosuresInMessage)).toBool()) { qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayEnclosuresInMessage)).toBool()) {
// Add thumbnail image. // Add thumbnail image.
@ -244,6 +246,8 @@ PreparedHtml SkinFactory::generateHtmlOfArticles(const QList<Message>& messages,
forced_img_size <= 0 ? QString() : QString::number(forced_img_size)); forced_img_size <= 0 ? QString() : QString::number(forced_img_size));
} }
} }
}
}
QString msg_date = QString msg_date =
qApp->settings()->value(GROUP(Messages), SETTING(Messages::UseCustomDate)).toBool() qApp->settings()->value(GROUP(Messages), SETTING(Messages::UseCustomDate)).toBool()

View File

@ -323,6 +323,10 @@ bool ServiceRoot::wantsBaggedIdsOfExistingMessages() const {
return false; return false;
} }
bool ServiceRoot::displaysEnclosures() const {
return true;
}
void ServiceRoot::aboutToBeginFeedFetching(const QList<Feed*>& feeds, void ServiceRoot::aboutToBeginFeedFetching(const QList<Feed*>& feeds,
const QHash<QString, QHash<BagOfMessages, QStringList>>& stated_messages, const QHash<QString, QHash<BagOfMessages, QStringList>>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) { const QHash<QString, QStringList>& tagged_messages) {

View File

@ -67,6 +67,7 @@ class ServiceRoot : public RootItem {
virtual QVariantHash customDatabaseData() const; virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data); virtual void setCustomDatabaseData(const QVariantHash& data);
virtual bool wantsBaggedIdsOfExistingMessages() const; virtual bool wantsBaggedIdsOfExistingMessages() const;
virtual bool displaysEnclosures() const;
virtual void aboutToBeginFeedFetching(const QList<Feed*>& feeds, virtual void aboutToBeginFeedFetching(const QList<Feed*>& feeds,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>&
stated_messages, stated_messages,

View File

@ -547,6 +547,10 @@ bool GmailNetworkFactory::fillFullMessage(Message& msg, const QJsonObject& json,
msg.m_author = sanitizeEmailAuthor(headers[QSL("From")]); msg.m_author = sanitizeEmailAuthor(headers[QSL("From")]);
msg.m_title = headers[QSL("Subject")]; msg.m_title = headers[QSL("Subject")];
// NOTE: Provide link to web-based GUI for the message.
msg.m_url = QSL("https://mail.google.com/mail/u/0/#all/%1").arg(msg.m_customId);
msg.m_createdFromFeed = true; msg.m_createdFromFeed = true;
msg.m_created = TextFactory::parseDateTime(headers[QSL("Date")]); msg.m_created = TextFactory::parseDateTime(headers[QSL("Date")]);

View File

@ -245,3 +245,7 @@ void GmailServiceRoot::saveAllCachedData(bool ignore_errors) {
} }
} }
} }
bool GmailServiceRoot::displaysEnclosures() const {
return false;
}

View File

@ -31,6 +31,7 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual QString code() const; virtual QString code() const;
virtual QString additionalTooltip() const; virtual QString additionalTooltip() const;
virtual void saveAllCachedData(bool ignore_errors); virtual void saveAllCachedData(bool ignore_errors);
virtual bool displaysEnclosures() const;
virtual QVariantHash customDatabaseData() const; virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data); virtual void setCustomDatabaseData(const QVariantHash& data);
virtual QList<Message> obtainNewMessages(Feed* feed, virtual QList<Message> obtainNewMessages(Feed* feed,