diff --git a/casinocoin-qt-windows.pro b/casinocoin-qt-windows.pro index 6ee3b2c..77948ff 100644 --- a/casinocoin-qt-windows.pro +++ b/casinocoin-qt-windows.pro @@ -220,7 +220,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/qtquick_controls/cpp/qmlexchangeslistmodel.h \ src/qt/qtquick_controls/cpp/qmlexchangeslistitem.h \ src/qt/qtquick_controls/cpp/guiexchangeslistview.h \ - src/qt/qtquick_controls/cpp/guiexchangescontrol.h + src/qt/qtquick_controls/cpp/guiexchangescontrol.h \ + src/qt/twitter/twitterwidget.h SOURCES += src/qt/bitcoin.cpp \ src/qt/bitcoingui.cpp \ @@ -321,7 +322,8 @@ SOURCES += src/qt/bitcoin.cpp \ src/qt/qtquick_controls/cpp/qmlexchangeslistmodel.cpp \ src/qt/qtquick_controls/cpp/qmlexchangeslistitem.cpp \ src/qt/qtquick_controls/cpp/guiexchangeslistview.cpp \ - src/qt/qtquick_controls/cpp/guiexchangescontrol.cpp + src/qt/qtquick_controls/cpp/guiexchangescontrol.cpp \ + src/qt/twitter/twitterwidget.cpp RESOURCES += src/qt/bitcoin.qrc @@ -357,7 +359,8 @@ OTHER_FILES += README.md \ src/qt/qtquick_controls/qml/QmlGUIExchangesWindow.qml \ src/qt/qtquick_controls/qml/QmlGUIMenuToolbarWindow.qml \ src/qt/qtquick_controls/qml/QmlGUIMenuToolbarListView.qml \ - src/qt/qtquick_controls/qml/QmlGUIMenuToolbarControl.qml + src/qt/qtquick_controls/qml/QmlGUIMenuToolbarControl.qml \ + src/qt/twitter/*.qml DISTFILES += \ QmlImports.qml diff --git a/casinocoin-qt-windows.pro.user b/casinocoin-qt-windows.pro.user index eed2bdd..86fc534 100644 --- a/casinocoin-qt-windows.pro.user +++ b/casinocoin-qt-windows.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -40,6 +40,7 @@ true false 0 + true true 0 8 @@ -58,14 +59,14 @@ ProjectExplorer.Project.Target.0 - Desktop Qt MinGW-w64 32bit (MSYS2) - Desktop Qt MinGW-w64 32bit (MSYS2) - qt.M2_MinGW-w64_i686_kit - 0 + Qt 5.6.0 (mingw32) + Qt 5.6.0 (mingw32) + {b3cf2641-46bb-4b28-b7a6-2713d9074823} + 1 0 0 - C:/Users/a.jochems/Documents/GitHub/casinocoin-issue-18 + true @@ -73,61 +74,7 @@ QtProjectManager.QMakeBuildStep true - USE_UPNP=0 USE_QRCODE=1 USE_IPV6=1 - false - false - false - - - true - Make - - Qt4ProjectManager.MakeStep - - false - - - - 2 - Build - - ProjectExplorer.BuildSteps.Build - - - - true - Make - - Qt4ProjectManager.MakeStep - - true - clean - - - 1 - Clean - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - Debug - - Qt4ProjectManager.Qt4BuildConfiguration - 0 - true - - - C:/Users/a.jochems/Documents/GitHub/build-casinocoin-qt-windows-Desktop_Qt_MinGW_w64_32bit_MSYS2-Release - - - true - qmake - - QtProjectManager.QMakeBuildStep - false - + "USE_UPNP=0" "USE_QRCODE=1" "USE_IPV6=1" false false false @@ -172,8 +119,8 @@ 0 true - - C:/Users/a.jochems/Documents/GitHub/build-casinocoin-qt-windows-Desktop_Qt_MinGW_w64_32bit_MSYS2-Profile + + C:/Users/a.jochems/Documents/GitHub/casinocoin-development true @@ -181,9 +128,9 @@ QtProjectManager.QMakeBuildStep true - + "USE_UPNP=0" "USE_QRCODE=1" "USE_IPV6=1" false - true + false false @@ -220,13 +167,13 @@ 2 false - Profile + Debug Qt4ProjectManager.Qt4BuildConfiguration - 0 + 2 true - 3 + 2 0 @@ -242,6 +189,7 @@ 1 + false false 1000 @@ -284,13 +232,13 @@ casinocoin-qt-windows - Qt4ProjectManager.Qt4RunConfiguration:C:/Users/a.jochems/Documents/GitHub/casinocoin-issue-18/casinocoin-qt-windows.pro + Qt4ProjectManager.Qt4RunConfiguration:C:/Users/a.jochems/Documents/GitHub/casinocoin-development/casinocoin-qt-windows.pro true casinocoin-qt-windows.pro false - false + C:/Users/a.jochems/Documents/GitHub/casinocoin-development 3768 false true diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index cf1cefd..ceefd75 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -135,5 +135,11 @@ qtquick_controls/qml/QmlGUIExchangesWindow.qml qtquick_controls/qml/QmlGUIExchangesListView.qml qtquick_controls/qml/QmlGUIExchangesControl.qml + twitter/FlipBar.qml + twitter/TweetDelegate.qml + twitter/tweetsearch.js + twitter/TweetsModel.qml + twitter/CasinocoinTwitterFeed.qml + twitter/resources/anonymous.png diff --git a/src/qt/forms/infopage.ui b/src/qt/forms/infopage.ui index cbcea39..4a1925d 100644 --- a/src/qt/forms/infopage.ui +++ b/src/qt/forms/infopage.ui @@ -38,9 +38,9 @@ - - 0 - 0 + + 3 + 1 @@ -58,6 +58,12 @@ + + + 0 + 0 + + 10 @@ -201,33 +207,13 @@ - - - Coin Fiat Value - - - - - - - - 75 - true - - - - - - - - - Coin Value - + @@ -240,7 +226,47 @@ + + + + Coin Fiat Value + + + + + + + + 75 + true + + + + - + + + + + + Estimated Marketcapital + + + + + + + + 75 + true + + + + - + + + + @@ -253,7 +279,7 @@ - + @@ -272,7 +298,7 @@ - + @@ -285,7 +311,7 @@ - + @@ -304,7 +330,7 @@ - + @@ -317,7 +343,7 @@ - + @@ -341,8 +367,64 @@ - + + + + + 2 + 1 + + + + Qt::LeftToRight + + + false + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + color: rgb(166, 27, 31); + + + Twitter + + + + + + + + + + 0 + 1 + + QFrame::StyledPanel diff --git a/src/qt/infopage.cpp b/src/qt/infopage.cpp index a0ad8f7..c4a360d 100644 --- a/src/qt/infopage.cpp +++ b/src/qt/infopage.cpp @@ -8,17 +8,21 @@ #include "main.h" #include "overviewpage.h" #include "qtquick_controls/cpp/guiexchangeswidget.h" +#include "twitter/twitterwidget.h" using namespace std; InfoPage::InfoPage(QWidget *parent) : QDialog(parent), ui(new Ui::InfoPage), - exchangesWidget( 0 ) + exchangesWidget( 0 ), + twitterWidget( 0 ) { ui->setupUi(this); ui->coinInfoBox->setMinimumHeight(250); + ui->twitterFeedBox->setMinimumHeight(250); ui->exchangeInfoBox->setMinimumHeight(250); + createTwitterWidget(); createExchangesWidget(); } @@ -101,6 +105,12 @@ InfoPage::~InfoPage() delete ui; } +void InfoPage::createTwitterWidget() +{ + twitterWidget = new TwitterWidget( this ); + ui->verticalLayoutTwitter->addWidget( twitterWidget->dockQmlToWidget() ); +} + void InfoPage::createExchangesWidget() { exchangesWidget = new GUIExchangesWidget( this ); @@ -108,8 +118,9 @@ void InfoPage::createExchangesWidget() ui->verticalLayoutExchanges->addWidget( exchangesWidget->dockQmlToWidget() ); } -void InfoPage::setCoinValues(QString coinValue, QString coinFiatValue) +void InfoPage::setCoinValues(QString coinValue, QString coinFiatValue, QString marketCapital) { ui->txtCoinValue->setText(coinValue); ui->txtCoinFiatValue->setText(coinFiatValue); + ui->txtEstimatedMarketCapital->setText(marketCapital); } diff --git a/src/qt/infopage.h b/src/qt/infopage.h index 776a5c8..f5910b5 100644 --- a/src/qt/infopage.h +++ b/src/qt/infopage.h @@ -6,6 +6,7 @@ class WalletModel; class ClientModel; class GUIExchangesWidget; +class TwitterWidget; namespace Ui { class InfoPage; @@ -21,7 +22,7 @@ public slots: /** Set number of blocks shown in the UI */ void setNumBlocks(int count, int countOfPeers); /** Set coin values */ - void setCoinValues(const QString coinValue, const QString coinFiatValue); + void setCoinValues(const QString coinValue, const QString coinFiatValue, const QString marketCapital); public: explicit InfoPage(QWidget *parent = 0); @@ -34,8 +35,10 @@ private: WalletModel *walletModel; ClientModel *clientModel; GUIExchangesWidget* exchangesWidget; + TwitterWidget* twitterWidget; - void createExchangesWidget(); + void createTwitterWidget(); + void createExchangesWidget(); double GetNetworkHashRate(int lookup, int height); }; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 987716b..fdbcadd 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -284,10 +284,13 @@ void OverviewPage::updateFiatBalance(int currency) QString conversionCurrency = QString("Price").append(Currencies::name(currency)); QString coinValue = QString::number( coinInformation.find("PriceBTC").value().toDouble(), 'f', 8 ); double currencyValue = coinInformation.find(conversionCurrency).value().toDouble(); + double marketCapValue = coinInformation.find("MarketCapital").value().toDouble(); // create formated fiat value QString formattedFiatValue = Currencies::format(currency, currencyValue, true, 4, false); + // create formatted market capital value + QString formattedMarketCapital = Currencies::format(Currencies::USD, marketCapValue, true, 2, false); // emit signal for change value - emit coinValueChanged(coinValue, formattedFiatValue); + emit coinValueChanged(coinValue, formattedFiatValue, formattedMarketCapital); // calculate and set fiat balance double fiatBalance = currentBalance * currencyValue; QString fiatBalanceString = Currencies::format(currency,fiatBalance,true, 2, true); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index dfa1ac2..cfd6b02 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -40,7 +40,7 @@ public slots: signals: void transactionClicked(const QModelIndex &index); - void coinValueChanged(const QString coinValue, const QString formattedCoinFiatValue); + void coinValueChanged(const QString coinValue, const QString formattedCoinFiatValue, const QString marketCapital); private: Ui::OverviewPage *ui; diff --git a/src/qt/twitter/CasinocoinTwitterFeed.qml b/src/qt/twitter/CasinocoinTwitterFeed.qml new file mode 100644 index 0000000..ff92942 --- /dev/null +++ b/src/qt/twitter/CasinocoinTwitterFeed.qml @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import "tweetsearch.js" as Helper +import QtQuick.Controls 1.4 + +Rectangle { + id: id_TwitterRoot + width: parent ? parent.width : 0 + height: parent ? parent.height : 0 + color: GUI20Skin.colorFrameBackground + + property int inAnimDur: 250 + property int counter: 0 + property alias isLoading: tweetsModel.isLoading + property var idx + property var ids + + Component.onCompleted: ids = new Array() + + function idInModel(id) + { + for (var j = 0; j < ids.length; j++) + if (ids[j] === id) + return 1 + return 0 + } + + TweetsModel { + id: tweetsModel + onIsLoaded: { + console.debug("Reload") + idx = new Array() + for (var i = 0; i < tweetsModel.model.count; i++) { + var id = tweetsModel.model.get(i).id + if (!idInModel(id)) + idx.push(i) + } + console.debug(idx.length + " new tweets") + id_TwitterRoot.counter = idx.length + } + } + + Timer { + id: timer + interval: 500; running: id_TwitterRoot.counter; repeat: true + onTriggered: { + id_TwitterRoot.counter--; + var id = tweetsModel.model.get(idx[id_TwitterRoot.counter]).id; + var item = tweetsModel.model.get(id_TwitterRoot.counter); + if(item.retweeted_status != undefined){ + item = item.retweeted_status; + } + mainListView.add( { "statusText": Helper.insertLinks(item.text, item.entities), + "twitterName": item.user.screen_name, + "name" : item.user.name, + "userImage": item.user.profile_image_url, + "source": item.source, + "id": id, + "uri": Helper.insertLinks(item.user.url, item.user.entities), + "published": item.created_at } ); + ids.push(id) + } + } + + Timer { + id: refreshTwitterFeedTimer + interval: 600000; + running: true; + repeat: true; + onTriggered: tweetsModel.reload() + } + + ScrollView { + id: mainScrollView + anchors.fill: id_TwitterRoot + + ListView { + id: mainListView + anchors.fill: mainScrollView + delegate: TweetDelegate { } + model: ListModel { id: finalModel } + + add: Transition { + NumberAnimation { property: "hm"; from: 0; to: 1.0; duration: 300; easing.type: Easing.OutQuad } + PropertyAction { property: "appear"; value: 250 } + } + + onDragEnded: tweetsModel.reload() + + function clear() { + ids = new Array() + model.clear() + } + + function add(obj) { + model.insert(0, obj) + } + } + } +} diff --git a/src/qt/twitter/FlipBar.qml b/src/qt/twitter/FlipBar.qml new file mode 100644 index 0000000..f1e240c --- /dev/null +++ b/src/qt/twitter/FlipBar.qml @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: container + property int animDuration: 300 + property Item front: Item {} + property Item back: Item {} + property real factor: 0.1 // amount the edges fold in for the 3D effect + property alias delta: effect.delta + property Item cur: frontShown ? front : back + property Item noncur: frontShown ? back : front + + function swap() { + var tmp = front; + front = back; + back = tmp; + resync(); + } + + width: cur.width + height: cur.height + onFrontChanged: resync(); + onBackChanged: resync(); + + function resync() {//TODO: Are the items ever actually visible? + back.parent = container; + front.parent = container; + frontShown ? back.visible = false : front.visible = false; + } + + property bool frontShown: true + + onFrontShownChanged: { + back.visible = !frontShown + front.visible = frontShown + } + + function flipUp(start) { + effect.visible = true; + effect.sourceA = effect.source1 + effect.sourceB = effect.source2 + if (start == undefined) + start = 1.0; + deltaAnim.from = start; + deltaAnim.to = 0.0 + dAnim.start(); + frontShown = false; + } + + function flipDown(start) { + effect.visible = true; + effect.sourceA = effect.source1 + effect.sourceB = effect.source2 + if (start == undefined) + start = 0.0; + deltaAnim.from = start; + deltaAnim.to = 1.0 + dAnim.start(); + frontShown = true; + } + + ShaderEffect { + id: effect + width: cur.width + height: cur.height + property real factor: container.factor * width + property real delta: 1.0 + + mesh: GridMesh { resolution: Qt.size(8,2) } + + SequentialAnimation on delta { + id: dAnim + running: false + NumberAnimation { + id: deltaAnim + duration: animDuration//expose anim + } + } + + property variant sourceA: source1 + property variant sourceB: source1 + property variant source1: ShaderEffectSource { + sourceItem: front + hideSource: effect.visible + } + + property variant source2: ShaderEffectSource { + sourceItem: back + hideSource: effect.visible + } + + fragmentShader: " + uniform lowp float qt_Opacity; + uniform sampler2D sourceA; + uniform sampler2D sourceB; + uniform highp float delta; + varying highp vec2 qt_TexCoord0; + void main() { + highp vec4 tex = vec4(qt_TexCoord0.x, qt_TexCoord0.y * 2.0, qt_TexCoord0.x, (qt_TexCoord0.y-0.5) * 2.0); + highp float shade = clamp(delta*2.0, 0.5, 1.0); + highp vec4 col; + if (qt_TexCoord0.y < 0.5) { + col = texture2D(sourceA, tex.xy) * (shade); + } else { + col = texture2D(sourceB, tex.zw) * (1.5 - shade); + col.w = 1.0; + } + gl_FragColor = col * qt_Opacity; + } + " + property real h: height + vertexShader: " + uniform highp float delta; + uniform highp float factor; + uniform highp float h; + uniform highp mat4 qt_Matrix; + attribute highp vec4 qt_Vertex; + attribute highp vec2 qt_MultiTexCoord0; + varying highp vec2 qt_TexCoord0; + void main() { + highp vec4 pos = qt_Vertex; + if (qt_MultiTexCoord0.y == 0.0) + pos.x += factor * (1. - delta) * (qt_MultiTexCoord0.x * -2.0 + 1.0); + else if (qt_MultiTexCoord0.y == 1.0) + pos.x += factor * (delta) * (qt_MultiTexCoord0.x * -2.0 + 1.0); + else + pos.y = delta * h; + gl_Position = qt_Matrix * pos; + qt_TexCoord0 = qt_MultiTexCoord0; + }" + + } +} diff --git a/src/qt/twitter/TweetDelegate.qml b/src/qt/twitter/TweetDelegate.qml new file mode 100644 index 0000000..6c52649 --- /dev/null +++ b/src/qt/twitter/TweetDelegate.qml @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import "tweetsearch.js" as Helper + +Item { + id: container + property real hm: 1.0 + property int appear: -1 + property real startRotation: 1 + + onAppearChanged: { + container.startRotation = 0.5 + flipBar.animDuration = appear; + delayedAnim.start(); + } + + SequentialAnimation { + id: delayedAnim + PauseAnimation { duration: 50 } + ScriptAction { script: flipBar.flipDown(startRotation); } + } + + width: parent ? parent.width : 0 + height: flipBar.height * hm + + FlipBar { + id: flipBar + + property bool flipped: false + delta: startRotation + + anchors.bottom: parent.bottom + width: container.ListView.view ? container.ListView.view.width : 0 + height: Math.max(72, tweet.y + tweet.height + 10) + + front: Rectangle { + width: container.ListView.view ? container.ListView.view.width : 0 + height: Math.max(72, tweet.y + tweet.height + 10) + + Rectangle { color: GUI20Skin.colorToolbarMainGradientEnd; width: parent.width; height: 1 } + Rectangle { color: GUI20Skin.colorToolbarMainGradientEnd; width: parent.width; height: 1; anchors.bottom: parent.bottom } + + Image { + id: placeHolder + source: "resources/anonymous.png" + x: 10; y: 9 + visible: avatar.status != Image.Ready + } + + Image { + id: avatar + source: model.userImage + anchors.fill: placeHolder + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: { + flipBar.flipUp() + flipBar.flipped = true + } + } + } + + Text { + id: name + text: model.name + anchors { left: avatar.right; leftMargin: 10; top: avatar.top; topMargin: -3 } + font.pixelSize: 12 + font.bold: true + color: GUI20Skin.colorToolbarMainGradientEnd + linkColor: "blue" + } + + Text { + id: tweet + text: model.statusText + anchors { left: avatar.right; leftMargin: 10; top: name.bottom; topMargin: 0; right: parent.right; rightMargin: 10 } + wrapMode: Text.WordWrap + font.pixelSize: 12 + font.bold: false + color: "black" + linkColor: "blue" + onLinkActivated: { + var tag = link.split("https://twitter.com/search?q=%23") + var user = link.split("https://twitter.com/") + if (tag[1] != undefined) { + mainListView.positionViewAtBeginning() + mainListView.clear() + mainListView.autoSearch('tag', tag[1]) + tweetsModel.from = "" + tweetsModel.phrase = "#" + tag[1] + } else if (user[1] != undefined) { + mainListView.positionViewAtBeginning() + mainListView.clear() + mainListView.autoSearch('user', user[1]) + tweetsModel.phrase = "" + tweetsModel.from = user[1] + } else + Qt.openUrlExternally(link) + } + } + } + + back: Rectangle { + width: container.ListView.view ? container.ListView.view.width : 0 + height: Math.max(72, tweet.y + tweet.height + 10) + color: "#be4a25" + + Rectangle { color: "#ff6633"; width: parent.width; height: 1 } + Rectangle { color: "#80341a"; width: parent.width; height: 1; anchors.bottom: parent.bottom } + + Image { + id: avatar2 + source: model.userImage + anchors.right: parent.right + anchors.rightMargin: 10 + y: 9 + MouseArea { + anchors.fill: parent + onClicked: { + flipBar.flipDown() + flipBar.flipped = false + } + } + } + + Text { + id: username + text: model.twitterName + x: 10; anchors { top: avatar2.top; topMargin: -3 } + font.pixelSize: 12 + font.bold: true + color: "black" + linkColor: "blue" + } + + Text { + text: model.source + "
" + Helper.formatDate(model.published) + "
" + model.uri + x: 10; anchors { top: username.bottom; topMargin: 0 } + wrapMode: Text.WordWrap + font.pixelSize: 12 + font.bold: false + color: "#ffc2ad" + linkColor: "blue" + onLinkActivated: Qt.openUrlExternally(link); + } + } + } +} diff --git a/src/qt/twitter/TweetsModel.qml b/src/qt/twitter/TweetsModel.qml new file mode 100644 index 0000000..d4ff925 --- /dev/null +++ b/src/qt/twitter/TweetsModel.qml @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import "tweetsearch.js" as Helper + +Item { + id: wrapper + +//! [auth tokens] + property string consumerKey : "" + property string consumerSecret : "" +//! [auth tokens] + property string bearerToken : "" + + property variant model: tweets + property string from : "CasinocoinNews" + property string phrase : "" + + property int status: XMLHttpRequest.UNSENT + property bool isLoading: status === XMLHttpRequest.LOADING + property bool wasLoading: false + signal isLoaded + + ListModel { id: tweets } + + function encodePhrase(x) { return encodeURIComponent(x); } + + function reload() { + tweets.clear() + + if (from == "" && phrase == "") + return; + +//! [requesting] + var req = new XMLHttpRequest; + req.open("GET", "https://api.twitter.com/1.1/statuses/user_timeline.json?" + + "&count=10&screen_name=" + from); + req.setRequestHeader("Authorization", "Bearer " + bearerToken); + req.onreadystatechange = function() { + status = req.readyState; + if (status === XMLHttpRequest.DONE) { + var objectArray = JSON.parse(req.responseText); + if (objectArray.errors !== undefined) + console.log("Error fetching tweets: " + objectArray.errors[0].message) + else { + for (var key in objectArray) { + var jsonObject = objectArray[key]; + tweets.append(jsonObject); + } + } + if (wasLoading == true) + wrapper.isLoaded() + } + wasLoading = (status === XMLHttpRequest.LOADING); + } + req.send(); +//! [requesting] + } + + onPhraseChanged: reload(); + onFromChanged: reload(); + + Component.onCompleted: { + if (consumerKey === "" || consumerSecret == "") { + bearerToken = encodeURIComponent(Helper.demoToken()) + return; + } + + var authReq = new XMLHttpRequest; + authReq.open("POST", "https://api.twitter.com/oauth2/token"); + authReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); + authReq.setRequestHeader("Authorization", "Basic " + Qt.btoa(consumerKey + ":" + consumerSecret)); + authReq.onreadystatechange = function() { + if (authReq.readyState === XMLHttpRequest.DONE) { + var jsonResponse = JSON.parse(authReq.responseText); + if (jsonResponse.errors !== undefined) + console.log("Authentication error: " + jsonResponse.errors[0].message) + else + { + bearerToken = jsonResponse.access_token; + reload(); + } + } + } + authReq.send("grant_type=client_credentials"); + } + +} diff --git a/src/qt/twitter/resources/anonymous.png b/src/qt/twitter/resources/anonymous.png new file mode 100644 index 0000000..88fba26 Binary files /dev/null and b/src/qt/twitter/resources/anonymous.png differ diff --git a/src/qt/twitter/resources/bird-anim-sprites.png b/src/qt/twitter/resources/bird-anim-sprites.png new file mode 100644 index 0000000..4e8d7e6 Binary files /dev/null and b/src/qt/twitter/resources/bird-anim-sprites.png differ diff --git a/src/qt/twitter/resources/icon-clear.png b/src/qt/twitter/resources/icon-clear.png new file mode 100644 index 0000000..75672f6 Binary files /dev/null and b/src/qt/twitter/resources/icon-clear.png differ diff --git a/src/qt/twitter/resources/icon-loading.png b/src/qt/twitter/resources/icon-loading.png new file mode 100644 index 0000000..8dbff8b Binary files /dev/null and b/src/qt/twitter/resources/icon-loading.png differ diff --git a/src/qt/twitter/resources/icon-refresh.png b/src/qt/twitter/resources/icon-refresh.png new file mode 100644 index 0000000..b639a63 Binary files /dev/null and b/src/qt/twitter/resources/icon-refresh.png differ diff --git a/src/qt/twitter/resources/icon-search.png b/src/qt/twitter/resources/icon-search.png new file mode 100644 index 0000000..e41935a Binary files /dev/null and b/src/qt/twitter/resources/icon-search.png differ diff --git a/src/qt/twitter/tweetsearch.js b/src/qt/twitter/tweetsearch.js new file mode 100644 index 0000000..42a76c9 --- /dev/null +++ b/src/qt/twitter/tweetsearch.js @@ -0,0 +1,62 @@ +.pragma library + +function formatDate(date) +{ + var da = new Date(date) + return da.toDateString() +} + +function demoToken() +{ + var a = new Array(22).join('A') + return a + String.fromCharCode(0x44, 0x69, 0x4a, 0x52, 0x51, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x74, 0x2b, 0x72, 0x6a, 0x6c, 0x2b, 0x71, + 0x6d, 0x7a, 0x30, 0x72, 0x63, 0x79, 0x2b, 0x42, 0x62, + 0x75, 0x58, 0x42, 0x42, 0x73, 0x72, 0x55, 0x48, 0x47, + 0x45, 0x67, 0x3d, 0x71, 0x30, 0x45, 0x4b, 0x32, 0x61, + 0x57, 0x71, 0x51, 0x4d, 0x62, 0x31, 0x35, 0x67, 0x43, + 0x5a, 0x4e, 0x77, 0x5a, 0x6f, 0x39, 0x79, 0x71, 0x61, + 0x65, 0x30, 0x68, 0x70, 0x65, 0x32, 0x46, 0x44, 0x73, + 0x53, 0x39, 0x32, 0x57, 0x41, 0x75, 0x30, 0x67) +} + +function linkForEntity(entity) +{ + return (entity.url ? entity.url : + (entity.screen_name ? 'https://twitter.com/' + entity.screen_name : + 'https://twitter.com/search?q=%23' + entity.text)) +} + +function textForEntity(entity) +{ + return (entity.display_url ? entity.display_url : + (entity.screen_name ? entity.screen_name : entity.text)) +} + +function insertLinks(text, entities) +{ + if (typeof text !== 'string') + return ""; + + if (!entities) + return text; + + // Add all links (urls, usernames and hashtags) to an array and sort them in + // descending order of appearance in text + var links = [] + if (entities.urls) + links = entities.urls.concat(entities.hashtags, entities.user_mentions) + else if (entities.url) + links = entities.url.urls + + links.sort(function(a, b) { return b.indices[0] - a.indices[0] }) + + for (var i = 0; i < links.length; i++) { + var offset = links[i].url ? 0 : 1 + text = text.substring(0, links[i].indices[0] + offset) + + '' + + textForEntity(links[i]) + '' + + text.substring(links[i].indices[1]) + } + return text.replace(/\n/g, '
'); +} diff --git a/src/qt/twitter/twitterwidget.cpp b/src/qt/twitter/twitterwidget.cpp new file mode 100644 index 0000000..e55329b --- /dev/null +++ b/src/qt/twitter/twitterwidget.cpp @@ -0,0 +1,40 @@ +#include "twitterwidget.h" + +#include +#include +#include +#include + +#include "gui20_skin.h" + +TwitterWidget::TwitterWidget(QWidget *parent) + : QWidget(parent) +{ +} + +TwitterWidget::~TwitterWidget() +{ + // member objects are moved to qml engine and it manages their instances +} + +QWidget* TwitterWidget::dockQmlToWidget() +{ + QQuickView* pTwitterWindow = new QQuickView; + QWidget* pPlaceHolder = 0; + if ( pTwitterWindow ) + { + QQmlContext* pContext = pTwitterWindow->rootContext(); + if ( pContext ) + { + pContext->setContextProperty( "GUI20Skin", &GUI20Skin::Instance() ); + } + pTwitterWindow->setSource( QUrl( QStringLiteral( "qrc:/qml/twitter/CasinocoinTwitterFeed.qml" ) ) ); + pPlaceHolder = QWidget::createWindowContainer( pTwitterWindow, this ); + if ( pPlaceHolder ) + { + pPlaceHolder->setMinimumSize( 500, 170 ); + pPlaceHolder->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + } + } + return pPlaceHolder; +} diff --git a/src/qt/twitter/twitterwidget.h b/src/qt/twitter/twitterwidget.h new file mode 100644 index 0000000..9b5f4fa --- /dev/null +++ b/src/qt/twitter/twitterwidget.h @@ -0,0 +1,25 @@ +#ifndef TWITTERWIDGET_H +#define TWITTERWIDGET_H + +#include + +class TwitterWidget : public QWidget +{ + Q_OBJECT + +public: + TwitterWidget( QWidget *parent = 0) ; + ~TwitterWidget(); + + QWidget* dockQmlToWidget(); + +public slots: + +private: + void registerCustomQmlTypes(); + +private slots: + +}; + +#endif // TWITTERWIDGET_H diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index c256dbc..7beb5a8 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -92,7 +92,7 @@ WalletView::WalletView(QWidget *parent, BitcoinGUI *_gui): // Clicking on "Export" allows to export the transaction list connect(exportButton, SIGNAL(clicked()), transactionView, SLOT(exportClicked())); // subscribe to coin value changes - connect(overviewPage, SIGNAL(coinValueChanged(const QString, const QString)), infoPage, SLOT(setCoinValues(const QString, const QString))); + connect(overviewPage, SIGNAL(coinValueChanged(const QString, const QString, const QString)), infoPage, SLOT(setCoinValues(const QString, const QString, const QString))); gotoOverviewPage(); }