diff options
Diffstat (limited to 'tabs')
| -rw-r--r-- | tabs/TweetsModel.qml | 88 | ||||
| -rw-r--r-- | tabs/Twitter.qml | 124 | ||||
| -rw-r--r-- | tabs/tweetSearch.js | 83 |
3 files changed, 295 insertions, 0 deletions
diff --git a/tabs/TweetsModel.qml b/tabs/TweetsModel.qml new file mode 100644 index 00000000..19b77ab1 --- /dev/null +++ b/tabs/TweetsModel.qml @@ -0,0 +1,88 @@ +import QtQuick 2.2 +import "tweetSearch.js" as Helper + +Item { + id: wrapper + + // Insert valid consumer key and secret tokens below + // See https://dev.twitter.com/apps +//! [auth tokens] + property string tweetsMaxCount: "20" + property string consumerKey : "" + property string consumerSecret : "" +//! [auth tokens] + property string bearerToken : "" + + property variant model: tweets + property string from : "" + 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/search/tweets.json?from=" + from + + "&count=" + tweetsMaxCount + "&q=" + encodePhrase(phrase)); + 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.statuses) { + var jsonObject = objectArray.statuses[key]; + tweets.append(jsonObject); + } + } + if (wasLoading == true) + wrapper.isLoaded() + } + wasLoading = (status === XMLHttpRequest.LOADING); + } + req.send(); +//! [requesting] + } + + + Component.onCompleted: { + if (consumerKey === "" || consumerSecret == "") { + console.log("setting demo token") + bearerToken = encodeURIComponent(Helper.demoToken()) + tweetsModel.phrase = "" + tweetsModel.from = "@monerocurrency" + reload() + 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; + } + } + authReq.send("grant_type=client_credentials"); + } + +} diff --git a/tabs/Twitter.qml b/tabs/Twitter.qml new file mode 100644 index 00000000..03c9be81 --- /dev/null +++ b/tabs/Twitter.qml @@ -0,0 +1,124 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import "tweetSearch.js" as Helper + +Item { + id: tab + ListModel { + id: testModel + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + ListElement { head: "Monero || #xmr"; foot: "<b>@btcplanet</b> Duis turpis arcu, varius nec rutrum in, adipiscing at enim. Donec quis consequat ipsum," } + } + + 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") + tab.counter = idx.length + } + } + + Timer { + id: timer + interval: 1; running: tab.counter; repeat: true + onTriggered: { + tab.counter--; + var id = tweetsModel.model.get(idx[tab.counter]).id + var item = tweetsModel.model.get(tab.counter) + listView.add({ "statusText": item.text, + "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 }); + console.log(item.created_at) + ids.push(id) + } + } + + ListView { + id: listView + model: ListModel { id: finalModel } + anchors.fill: parent + clip: true + boundsBehavior: ListView.StopAtBounds + + function add(obj) { model.insert(0, obj) } + delegate: Rectangle { + height: 98 + width: listView.width + + Text { + id: headerText + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.topMargin: 16 + elide: Text.ElideRight + font.family: "Arial" + font.pixelSize: 18 + color: "#000000" + text: model.name + } + + Text { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: headerText.bottom + anchors.bottom: parent.bottom + anchors.topMargin: 10 + anchors.bottomMargin: 10 + wrapMode: Text.Wrap + elide: Text.ElideRight + font.family: "Arial" + font.pixelSize: 12 + color: "#535353" + text: model.statusText + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: 1 + color: "#DBDBDB" + } + } + } +} diff --git a/tabs/tweetSearch.js b/tabs/tweetSearch.js new file mode 100644 index 00000000..2d1c083f --- /dev/null +++ b/tabs/tweetSearch.js @@ -0,0 +1,83 @@ +.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) + + '<a href=\"' + linkForEntity(links[i]) + '\">' + + textForEntity(links[i]) + '</a>' + + text.substring(links[i].indices[1]) + } + return text.replace(/\n/g, '<br>'); +} + +function boldLinks(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) + + '<b>' + textForEntity(links[i]) + '</b>' + + text.substring(links[i].indices[1]) + } + return text.replace(/\n/g, '<br>'); +} |
