result)
+void SaoriView::reload()
{
- qDebug() << result;
+ auto saoriaccount = SaoriApplication::saori()->findAccount(m_account);
+ if (saoriaccount == nullptr) return;
+ if (m_entries.count() == 0) m_maxid = 0;
+ else {
+ m_maxid = m_entries.first()->id();
+ }
+ QUrlQuery query;
+ query.addQueryItem("since_id",QString::number(m_maxid));
+ saoriaccount->getTimelineData(m_viewname,query);
+}
+
+const QString SaoriView::instanceInfoParser(const QString instance)
+{
+ auto i = SaoriApplication::findInstance(QUrl(instance));
+ if (!i) return QString();
+ QString result;
+ result = QString(htmlDiv("instance_title","%1") +
+ htmlDiv("instance_uri","%2") +
+ htmlDiv("instance_description","%3") +
+ htmlDiv("instance_email","email : %4") +
+ htmlDiv("instance_contact_account","%5") +
+ htmlDiv("instance_version","Version : %6")
+ ).arg(i->instanceInfo("title").toString(),
+ i->instanceInfo("uri").toString(),
+ i->instanceInfo("description").toString(),
+ i->instanceInfo("email").toString(),
+ accountParser(i->instanceInfo("contact_account").toObject()),
+ i->instanceInfo("version").toString());
+ return result;
+}
+
+const QString SaoriView::statusParser(const QJsonObject json)
+{
+ QString result;
+ if (json.isEmpty()) return QString();
+
+ QDateTime dt = QDateTime::fromString(json["created_at"].toString(),"yyyy-MM-ddTHH:mm:ss.zzzZ");
+ dt.setTimeSpec(Qt::UTC);
+
+ result = (!json["reblog"].isNull()) ?
+ (statusParser(json["reblog"].toObject()) +
+ htmlDiv("reblogger",htmlHr() +
+ htmlImg("mavatar",json["account"].toObject()["avatar"].toString()) +
+ tr("boosted by: %1").arg(json["account"].toObject()["display_name"].toString()))
+ ) :
+ (htmlDiv("status",accountParser(json["account"].toObject()) +
+ htmlDiv("created_at",tr("created at :") +
+ dt.toLocalTime().toString()) +
+ htmlDiv("content",contentParser(json["content"].toString())))
+ );
+ if (json["reblog"].isNull()) {
+ QString media;
+ for (auto j:json["media_attachments"].toArray()) {
+ media += mediaParser(j.toObject());
+ }
+ result += htmlDiv("media",media);
+ }
+ return result;
+}
+
+const QString SaoriView::accountParser(const QJsonObject json)
+{
+ QString result;
+ if (json.isEmpty()) return QString();
+ result = htmlDiv("account",
+ htmlSpan("avatar",
+ htmlHr() +
+ htmlImg("avatar",json["avatar"].toString())) +
+ htmlSpan("display_name",
+ json["display_name"].toString()) +
+ " " +
+ htmlSpan("acct","@" + json["acct"].toString()) +
+ htmlDiv("user_info",
+ tr(" following: ") +
+ QString::number(json["following_count"].toInt()) +
+ tr(" following: ") +
+ QString::number(json["followers_count"].toInt()))
+ );
+ return result;
+}
+
+const QString SaoriView::mediaParser(const QJsonObject json)
+{
+ QString result;
+ result = htmlSpan("media_preview",
+ json["type"].toString() == "image" ?
+ htmlAnc("media:" + json["url"].toString(),htmlImg("media",json["preview_url"].toString())) :
+ htmlImg("media",json["preview_url"].toString()));
+ return result;
+}
+
+const QString SaoriView::contentParser(const QString content)
+{
+ QString result;
+ auto l = content.split("
");
+ for (auto s:l) {
+ result += "
";
+ int i = 0;
+ for (auto c:s) {
+ if (c != ' ') break;
+ result += " ";
+ }
+ result += s.right(s.count() - i);
+ }
+ result = result.right(result.count() - 6);
+
+ l = result.split("");
+ result = "";
+ for (auto s:l) {
+ result += "
";
+ int i = 0;
+ for (auto c:s) {
+ if (c != ' ') break;
+ result += " ";
+ }
+ result += s.right(s.count() - i);
+ }
+ result = result.right(result.count() - 3);
+
+ return result;
+}
+
+const QString SaoriView::notificationParser(const QJsonObject json)
+{
+ QString result;
+ if (json.isEmpty()) return QString();
+
+ QDateTime dt = QDateTime::fromString(json["created_at"].toString(),"yyyy-MM-ddTHH:mm:ss.zzzZ");
+ dt.setTimeSpec(Qt::UTC);
+
+ QStringList type;
+ type << "mention" << "reblog" << "favourite" << "follow";
+ QString title;
+ switch (type.indexOf(json["type"].toString())) {
+ case 0:
+ title += htmlImg("mavatar",":/icons/ionicons/chatbubbles.svg");
+ title += tr("%1 mentioned your status.").arg(json["account"].toObject()["display_name"].toString());
+ break;
+ case 1:
+ title += htmlImg("mavatar",":/icons/ionicons/share.svg");
+ title += tr("%1 boosted your status.").arg(json["account"].toObject()["display_name"].toString());
+ break;
+ case 2:
+ title += htmlImg("mavatar",":/icons/ionicons/heart.svg");
+ title += tr("%1 favourited your status.").arg(json["account"].toObject()["display_name"].toString());
+ break;
+ case 3:
+ title += htmlImg("mavatar",":/icons/ionicons/person-add.svg");
+ title += tr("%1 followed you.").arg(json["account"].toObject()["display_name"].toString());
+ break;
+ default:
+ break;
+ }
+
+ result = htmlDiv("notification",
+ htmlDiv("notification_type",title + htmlHr()) +
+ htmlDiv("created_at",tr("created at :") + dt.toLocalTime().toString()) +
+ statusParser(json["status"].toObject()) +
+ htmlDiv("reblogger",htmlHr() +
+ htmlImg("mavatar",json["account"].toObject()["avatar"].toString()) +
+ tr("%1 (@%2)").arg(json["account"].toObject()["display_name"].toString(),
+ json["account"].toObject()["acct"].toString())));
+ return result;
+}
+
+const QString SaoriView::htmlDiv(const QString divclass, const QString text)
+{
+ return QString("
\n%2\n
\n").arg(divclass,text);
+}
+
+const QString SaoriView::htmlSpan(const QString spanclass, const QString text)
+{
+ return QString("%2").arg(spanclass,text);
+}
+
+const QString SaoriView::htmlImg(const QString type, const QString url)
+{
+ return QString("").arg(type,url);
+}
+
+const QString SaoriView::htmlAnc(const QString link, const QString text)
+{
+ return QString("%2").arg(link,text);
+}
+
+const QString SaoriView::htmlHr()
+{
+ return QString("
");
+}
+
+void SaoriView::recived(const QString timeline,const QByteArray data)
+{
+ if (timeline != m_viewname) return;
+ QJsonArray json = QJsonDocument::fromJson(data).array();
+ for (auto j:json) {
+ int i = 0;
+ SaoriViewEntry *entry = nullptr;
+ for(;m_entries.count() > i;i ++) {
+ if (m_entries.at(i)->id() == j.toObject()["id"].toString().toLongLong()) {
+ entry = m_entries.at(i);
+ break;
+ }
+ if (m_entries.at(i)->id() < j.toObject()["id"].toString().toLongLong()) break;
+ }
+ if (entry == nullptr) {
+ entry = new SaoriViewEntry(j.toObject()["id"].toString().toLongLong(),ui->scrollAreaWidgetContents);
+ connect(entry,&SaoriViewEntry::anchorClicked,this,&SaoriView::linkClicked);
+ m_entries.insert(i,entry);
+ qobject_cast(ui->scrollAreaWidgetContents->layout())->insertWidget(i,entry);
+ if (m_viewname == "notifications") entry->setContent(notificationParser(j.toObject()));
+ else entry->setContent(statusParser(j.toObject()));
+ }
+ }
+ if (m_entries.count()) {
+ if (m_entries.first()->id() > m_maxid) reload();
+ }
+}
+
+void SaoriView::linkClicked(const QUrl &url)
+{
+ if (url.toString().left(6) == "media:") {
+ QUrl u(url.toString().mid(6));
+ emit openMediaView(u);
+ }
+}
+
+void SaoriView::on_pushButton_newest_clicked()
+{
+ ui->scrollArea->verticalScrollBar()->setValue(0);
+}
+
+void SaoriView::on_pushButton_reload_clicked()
+{
+ reload();
}
diff --git a/saoriview.h b/saoriview.h
index 2f793be..60540f0 100644
--- a/saoriview.h
+++ b/saoriview.h
@@ -28,7 +28,10 @@
#define SAORIVIEW_H
#include
-#include
+#include
+#include
+#include
+#include
namespace Ui {
class SaoriView;
@@ -46,13 +49,39 @@
protected:
QString m_viewname;
QString m_account;
+ QList m_entries;
+ qlonglong m_maxid;
+
static QList m_viewList;
+protected:
+ void reload();
+ const QString instanceInfoParser(const QString instance);
+ const QString statusParser(const QJsonObject json);
+ const QString accountParser(const QJsonObject json);
+ const QString mediaParser(const QJsonObject json);
+ const QString contentParser(const QString content);
+ const QString notificationParser(const QJsonObject json);
+
+ static const QString htmlDiv(const QString divclass,const QString text);
+ static const QString htmlSpan(const QString spanclass,const QString text);
+ static const QString htmlImg(const QString type,const QString url);
+ static const QString htmlAnc(const QString link,const QString text);
+ static const QString htmlHr();
+
+
public slots:
- void recived(QMap result);
+ void recived(const QString timeline,const QByteArray data);
+ void linkClicked(const QUrl &url);
private:
Ui::SaoriView *ui;
+
+signals:
+ void openMediaView(const QUrl);
+private slots:
+ void on_pushButton_newest_clicked();
+ void on_pushButton_reload_clicked();
};
#endif // SAORIVIEW_H
diff --git a/saoriview.ui b/saoriview.ui
index e4b2987..e8fc732 100644
--- a/saoriview.ui
+++ b/saoriview.ui
@@ -16,18 +16,42 @@
-
+
+
+ 0
+ 0
+
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ Qt::ScrollBarAsNeeded
+
+
+ QAbstractScrollArea::AdjustIgnored
+
true
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
0
0
- 307
+ 293
316
+
+
+ 0
+ 0
+
+
@@ -38,7 +62,7 @@
0
-
-
+
Pin
@@ -52,7 +76,7 @@
-
-
+
Notify
@@ -66,7 +90,7 @@
-
-
+
Auto reload
@@ -80,7 +104,7 @@
-
-
+
Newest
@@ -91,7 +115,7 @@
-
-
+
Reload
@@ -115,7 +139,7 @@
-
-
+
0
diff --git a/saoriviewentry.cpp b/saoriviewentry.cpp
new file mode 100644
index 0000000..64265d4
--- /dev/null
+++ b/saoriviewentry.cpp
@@ -0,0 +1,142 @@
+/***
+
+The MIT License
+
+Copyright (c) 2018 Teppei Tamra (TAM)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+***/
+
+#include "saoriviewentry.h"
+#include "saoriapplication.h"
+#include "saoricache.h"
+#include
+#include
+#include
+
+SaoriViewEntry::SaoriViewEntry(qlonglong id, QWidget *parent) :
+ QTextBrowser(parent)
+{
+ setReadOnly(true);
+ setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
+ setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum);
+ setOpenLinks(false);
+ setOpenExternalLinks(false);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_id = id;
+ connect(SaoriApplication::saori()->cache(),&SaoriCache::downloaded,this,&SaoriViewEntry::downloaded);
+}
+
+void SaoriViewEntry::setContent(const QString content)
+{
+ m_original = content;
+ document()->clear();
+ QStringList imgs;
+ for (int i = 0;(i = m_original.indexOf("cache()->fileCache(QUrl(i.mid(p + 1)));
+ } else {
+ imageurl = i.mid(p + 1);
+ }
+ m_urlmap[i] = imageurl;
+ //qDebug() << imageurl;
+ QImage img(imageurl);
+ imageResizer(i.left(p),img);
+ document()->addResource(QTextDocument::ImageResource,QUrl("img:" + imageurl),QVariant(img));
+ }
+ setText(designedText());
+ // QTextBrowserのサイズを確定させるトリック。
+ QResizeEvent e(size(),size());
+ resizeEvent(&e);
+}
+
+qlonglong SaoriViewEntry::id()
+{
+ return m_id;
+}
+
+void SaoriViewEntry::resizeEvent(QResizeEvent *e)
+{
+ QTextBrowser::resizeEvent(e);
+ document()->setTextWidth(qreal(e->size().width()));
+ setMinimumHeight(document()->size().height() + 5);
+}
+
+const QString SaoriViewEntry::designedText()
+{
+ QString result;
+ result += "";
+ result += "";
+ result += imageReplacer();
+ result += "";
+ return result;
+}
+
+const QString SaoriViewEntry::imageReplacer()
+{
+ QString result = m_original;
+ for (auto k:m_urlmap.keys()) {
+ result.replace(QString("cache()->fileCache(url);
+ setContent(m_original);
+ for (auto i = m_urlmap.begin();i != m_urlmap.end();i ++) {
+ if (i.value().left(2) == ":/") {
+ if (i.key().left(2) != ":/") {
+ return;
+ }
+ }
+ }
+ disconnect(SaoriApplication::saori()->cache(),&SaoriCache::downloaded,this,&SaoriViewEntry::downloaded);
+}
diff --git a/saoriviewentry.h b/saoriviewentry.h
new file mode 100644
index 0000000..737e5a2
--- /dev/null
+++ b/saoriviewentry.h
@@ -0,0 +1,59 @@
+/***
+
+The MIT License
+
+Copyright (c) 2018 Teppei Tamra (TAM)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+***/
+
+#ifndef SAORIVIEWENTRY_H
+#define SAORIVIEWENTRY_H
+
+#include
+#include
+#include
+
+class SaoriViewEntry : public QTextBrowser
+{
+ Q_OBJECT
+
+public:
+ SaoriViewEntry(qlonglong id,QWidget *parent = nullptr);
+ void setContent(const QString content);
+ qlonglong id();
+
+protected:
+ virtual void resizeEvent(QResizeEvent *e);
+ const QString designedText();
+ const QString imageReplacer();
+ void imageResizer(const QString type,QImage &image);
+
+protected:
+ qlonglong m_id;
+ QString m_original;
+ QMap m_urlmap;
+
+protected slots:
+ void downloaded(const QUrl url);
+
+};
+
+#endif // SAORIVIEWENTRY_H
diff --git a/saoriwindow.cpp b/saoriwindow.cpp
index df44ea7..16ece61 100644
--- a/saoriwindow.cpp
+++ b/saoriwindow.cpp
@@ -25,6 +25,7 @@
***/
#include
+#include
#include "saoriwindow.h"
#include "ui_saoriwindow.h"
#include "saoriapplication.h"
@@ -45,19 +46,77 @@
delete ui;
}
+QPair SaoriWindow::getTimelineTitle(const QString timeline)
+{
+ // trick for translation.
+ QStringList trtl;
+ QString tlText,icon;
+ trtl << "home" << "local" << "public" << "notifications" << "instance";
+ switch (trtl.indexOf(timeline)) {
+ case 0:
+ tlText = tr("home");
+ icon = ":/icons/ionicons/chatbubbles.svg";
+ break;
+ case 1:
+ tlText = tr("local");
+ icon = ":/icons/ionicons/chatbubble.svg";
+ break;
+ case 2:
+ tlText = tr("public");
+ icon = ":/icons/ionicons/earth.svg";
+ break;
+ case 3:
+ tlText = tr("notifications");
+ icon = ":/icons/ionicons/alert.svg";
+ break;
+ case 4:
+ tlText = tr("instance");
+ icon = "";
+ break;
+ default:
+ tlText = timeline;
+ break;
+ }
+
+ return QPair(tlText,icon);
+
+}
+
void SaoriWindow::openView(const QStringList viewName)
{
if (auto view = SaoriView::findView(viewName.at(1),viewName.at(0))) {
ui->mdiArea->setActiveSubWindow(qobject_cast(view->parent()));
return;
}
- QString title = viewName.at(1) + ":" + viewName.at(0);
+ QString title = getTimelineTitle(viewName.at(1)).first + ":" + viewName.at(0);
auto view = new SaoriView(viewName.at(1),viewName.at(0));
auto sub = ui->mdiArea->addSubWindow(view);
+ connect(view,&SaoriView::openMediaView,this,&SaoriWindow::openMediaView);
sub->setWindowTitle(title);
+ sub->setWindowIcon(QIcon(getTimelineTitle(viewName.at(1)).second));
sub->show();
}
+void SaoriWindow::openMediaView(const QUrl url)
+{
+ // TODO 専用のwidget作った方がいいでしょう。
+ auto view = new QLabel();
+ auto sub = ui->mdiArea->addSubWindow(view);
+ sub->setWindowTitle(url.toString());
+ QString image = SaoriApplication::saori()->cache()->fileCache(url);
+ if (image.mid(0,2) == ":/")
+ connect(SaoriApplication::saori()->cache(),&SaoriCache::downloaded,this,[=](const QUrl i){
+ if (i.toString() == url.toString()) {
+ view->setPixmap(SaoriApplication::saori()->cache()->fileCache(url));
+ sub->resize(view->pixmap()->size());
+ }
+ });
+ view->setPixmap(image);
+ view->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
+ sub->show();
+ sub->resize(view->pixmap()->size());
+}
+
void SaoriWindow::openAccountDialog()
@@ -67,6 +126,7 @@
dialog->m_account->setParent(QApplication::instance());
SaoriApplication::getAccountList()->append(dialog->m_account);
}
+ updateTimelineList();
delete dialog;
}
@@ -80,35 +140,11 @@
item->setIcon(0,QIcon(":/icons/ionicons/person.svg"));
accountItem->addChild(item);
for (auto tl:SaoriApplication::getAccountList()->at(i)->timelineList()) {
- // trick for translation.
- QStringList trtl;
- QString tlText,icon;
- trtl << "home" << "local" << "public" << "notifications";
- switch (trtl.indexOf(tl)) {
- case 0:
- tlText = tr("home");
- icon = ":/icons/ionicons/chatbubbles.svg";
- break;
- case 1:
- tlText = tr("local");
- icon = ":/icons/ionicons/chatbubble.svg";
- break;
- case 2:
- tlText = tr("public");
- icon = ":/icons/ionicons/earth.svg";
- break;
- case 3:
- tlText = tr("notifications");
- icon = ":/icons/ionicons/alert.svg";
- break;
- default:
- tlText = tl;
- break;
- }
- auto tlItem = new QTreeWidgetItem(item,QStringList() << tlText
+ auto title = getTimelineTitle(tl);
+ auto tlItem = new QTreeWidgetItem(item,QStringList() << title.first
<< SaoriApplication::getAccountList()->at(i)->name()
<< tl);
- if (!icon.isEmpty()) tlItem->setIcon(0,QIcon(icon));
+ if (!title.second.isEmpty()) tlItem->setIcon(0,QIcon(title.second));
item->addChild(tlItem);
}
}
@@ -116,7 +152,7 @@
for (int i = 0;i < SaoriApplication::getInstanceList()->count();i ++) {
auto *item = new QTreeWidgetItem(instanceItem,QStringList() << SaoriApplication::getInstanceList()->at(i)->instance().toString());
instanceItem->addChild(item);
- item->addChild(new QTreeWidgetItem(item,QStringList() << tr("Infomation")));
+ //item->addChild(new QTreeWidgetItem(item,QStringList() << tr("Infomation")));
}
}
@@ -124,7 +160,27 @@
{
auto account = SaoriApplication::findAccount(item->text(1));
if (account) {
- qDebug() << account->instance()->timelineUrl(item->text(2));
openView(QStringList() << item->text(1) << item->text(2));
+ return;
}
+ auto instance = SaoriApplication::findInstance(QUrl(item->text(0)));
+ if (instance) {
+ openView(QStringList() << instance->instance().toString() << "instance");
+ }
+}
+
+void SaoriWindow::on_actionTabbedView_mode_toggled(bool arg1)
+{
+ if (ui->mdiArea->viewMode() == QMdiArea::TabbedView) {
+ if (arg1 == false) {
+ ui->mdiArea->setViewMode(QMdiArea::SubWindowView);
+ ui->actionTiled->setEnabled(true);
+ }
+ } else {
+ if (arg1 == true) {
+ ui->mdiArea->setViewMode(QMdiArea::TabbedView);
+ ui->actionTiled->setEnabled(false);
+ }
+ }
+ return;
}
diff --git a/saoriwindow.h b/saoriwindow.h
index 58587ea..da84032 100644
--- a/saoriwindow.h
+++ b/saoriwindow.h
@@ -29,6 +29,8 @@
#include
#include
+#include
+#include
namespace Ui {
class SaoriWindow;
@@ -41,10 +43,14 @@
public:
explicit SaoriWindow(QWidget *parent = 0);
~SaoriWindow();
+ static QPair getTimelineTitle(const QString timeline);
protected:
void openView(const QStringList viewName);
+public slots:
+ void openMediaView(const QUrl url);
+
protected slots:
void openAccountDialog();
void updateTimelineList();
@@ -54,6 +60,8 @@
private slots:
void on_timelineTree_itemDoubleClicked(QTreeWidgetItem *item, int);
+ void on_actionTabbedView_mode_toggled(bool arg1);
+
private:
Ui::SaoriWindow *ui;
};
diff --git a/saoriwindow.ui b/saoriwindow.ui
index dff0541..ec66142 100644
--- a/saoriwindow.ui
+++ b/saoriwindow.ui
@@ -13,6 +13,10 @@
Saori
+
+
+ :/icons/saori.svg:/icons/saori.svg
+
false
@@ -136,7 +140,7 @@
-
-
+
-
@@ -209,7 +213,9 @@
-
+
+
+
actionShow_Hide_Timeline_List