diff --git a/.gitignore b/.gitignore index e31cfb2..e5f8bc2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ - *.user +README.md diff --git a/README.md b/README.md index ca54a60..af9d77a 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,23 @@ -# Tooter [Fork] +# Tooter β ## About -Tooter is Mastodon client for Sailfish OS. It is a free, open-source social network. A decentralized alternative to commercial platforms, it avoids the risks of a single company monopolizing your communication. +Tooter is Mastodon client for [Sailfish OS](https://sailfishos.org). -This fork is being used to further develop and maintain the Tooter app by dysko (https://github.com/dysk0/harbour-tooter). The development branch 'upstream' is being used for merge pull requests to the original repository. Releases by dysko can be found on the Jolla store and on https://openrepos.net/content/dysko/tooter +This fork is being used to further develop and maintain the Tooter app by dysko ([harbour-tooter](https://github.com/dysk0/harbour-tooter)). -Releases from this forked repository (branch 'master') can be found here: https://openrepos.net/content/molan/tooter-v-fork. +* Releases from this repository (Tooter β from release branch *master*) can be found on [OpenRepos.net](https://openrepos.net/content/molan/tooter-v) +* Releases by dysko can be found on the Jolla store and on [OpenRepos.net](https://openrepos.net/content/dysko/tooter) ## Build -Clone / download this repository and import it in your SailfishOS IDE using the harbour-tooter.pro project file. No additional configuration needed. +Clone / download this repository and import it into your SailfishOS IDE using the harbour-tooter.pro project file. No additional configuration needed. ## Repository branches: -- master: default (Beta release version, harbour-tooterb) -- upstream: commits for Tooter release (harbour-tooter) +* master: release branch which includes specifics for harbour-tooterb (Tooter β) +* upstream: main development branch which is used to send changes to the upstream repository (harbour-tooter) ## Contributions -Contributions to this project are very welcome, since there are still many things which can be done for Tooter. -- please fork the upstream branch if you want to contribute to this project. +Contributions to this project are very welcome, since there are still many things which can be done for Tooter. If you already know what you want to add or fix, please make a Pull Request (PR) with your proposal. Your PR should include an explanation or a change log summary. Merging will not be allowed until the PR has been reviewed. +Please fork the [upstream branch](https://github.com/molan-git/harbour-tooter/tree/upstream) if you want to contribute to this project. ## Screenshots diff --git a/harbour-tooterb.desktop b/harbour-tooterb.desktop index 74cc312..6511311 100644 --- a/harbour-tooterb.desktop +++ b/harbour-tooterb.desktop @@ -4,8 +4,3 @@ X-Nemo-Application-Type=silica-qt5 Icon=harbour-tooterb Exec=harbour-tooterb Name=Tooter β -# translation example: -# your app name in German locale (de) -# -# Remember to comment out the following line, if you do not want to use -# a different app name in German locale (de). diff --git a/harbour-tooterb.pro b/harbour-tooterb.pro index ff9a7c3..cf3b679 100644 --- a/harbour-tooterb.pro +++ b/harbour-tooterb.pro @@ -9,15 +9,14 @@ # - icon definition filename in desktop file must be changed # - translation filenames have to be changed -# The name of your application TARGET = harbour-tooterb CONFIG += sailfishapp QT += network dbus sql +QT += multimedia CONFIG += link_pkgconfig -PKGCONFIG += sailfishapp -PKGCONFIG += \ +PKGCONFIG += sailfishapp \ nemonotifications-qt5 DEFINES += "APPVERSION=\\\"$${SPECVERSION}\\\"" @@ -39,45 +38,44 @@ dbus_services.files = config/ba.dysko.harbour.tooterb.service interfaces.path = /usr/share/dbus-1/interfaces/ interfaces.files = config/ba.dysko.harbourb.tooterb.xml -SOURCES += src/harbour-tooterb.cpp -SOURCES += src/imageuploader.cpp -SOURCES += src/filedownloader.cpp -SOURCES += src/notifications.cpp -SOURCES += src/dbusAdaptor.cpp -SOURCES += src/dbus.cpp +SOURCES += src/harbour-tooterb.cpp \ + src/imageuploader.cpp \ + src/filedownloader.cpp \ + src/notifications.cpp \ + src/dbusAdaptor.cpp \ + src/dbus.cpp -HEADERS += src/imageuploader.h -HEADERS += src/filedownloader.h -HEADERS += src/notifications.h -HEADERS += src/dbusAdaptor.h -HEADERS += src/dbus.h +HEADERS += src/imageuploader.h \ + src/filedownloader.h \ + src/notifications.h \ + src/dbusAdaptor.h \ + src/dbus.h DISTFILES += qml/harbour-tooterb.qml \ + qml/images/tooterb-cover.svg \ + qml/pages/ConversationPage.qml \ + qml/pages/ProfilePage.qml \ + qml/pages/SettingsPage.qml \ qml/pages/components/InfoBanner.qml \ + qml/pages/components/MediaFullScreen.qml \ + qml/pages/components/MyMedia.qml \ + qml/pages/components/NavigationPanel.qml \ + qml/pages/components/ProfileImage.qml \ qml/pages/components/VisualContainer.qml \ qml/pages/components/MiniStatus.qml \ qml/pages/components/MiniHeader.qml \ qml/pages/components/ItemUser.qml \ qml/pages/components/MyList.qml \ - qml/pages/components/Navigation.qml \ qml/pages/components/ProfileHeader.qml \ qml/pages/components/MediaBlock.qml \ - qml/pages/components/MyImage.qml \ - qml/pages/components/ImageFullScreen.qml \ qml/cover/CoverPage.qml \ qml/pages/MainPage.qml \ qml/pages/LoginPage.qml \ - qml/pages/Conversation.qml \ - qml/pages/components/Toot.qml \ qml/pages/Browser.qml \ - qml/pages/Profile.qml \ - qml/pages/Settings.qml \ qml/lib/API.js \ - qml/images/notification.svg \ - qml/images/verified.svg \ - qml/images/boosted.svg \ - qml/images/tooterb.svg \ - qml/images/emojiselect.svg \ + qml/images/icon-s-following \ + qml/images/icon-s-bookmark \ + qml/images/icon-m-emoji.svg \ qml/images/icon-m-profile.svg \ qml/images/icon-l-profile.svg \ qml/lib/Mastodon.js \ @@ -97,21 +95,18 @@ SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172 # following CONFIG line CONFIG += sailfishapp_i18n -# German translation is enabled as an example. If you aren't -# planning to localize your app, remember to comment out the -# following TRANSLATIONS line. And also do not forget to -# modify the localized app name in the the .desktop file. -TRANSLATIONS += translations/harbour-tooterb-de.ts -TRANSLATIONS += translations/harbour-tooterb-el.ts -TRANSLATIONS += translations/harbour-tooterb-es.ts -TRANSLATIONS += translations/harbour-tooterb-fi.ts -TRANSLATIONS += translations/harbour-tooterb-fr.ts -TRANSLATIONS += translations/harbour-tooterb-nl.ts -TRANSLATIONS += translations/harbour-tooterb-nl_BE.ts -TRANSLATIONS += translations/harbour-tooterb-oc.ts -TRANSLATIONS += translations/harbour-tooterb-pl.ts -TRANSLATIONS += translations/harbour-tooterb-ru.ts -TRANSLATIONS += translations/harbour-tooterb-sr.ts -TRANSLATIONS += translations/harbour-tooterb-sv.ts -TRANSLATIONS += translations/harbour-tooterb-zh_CN.ts -TRANSLATIONS += translations/harbour-tooterb-it.ts +TRANSLATIONS += translations/harbour-tooterb.ts \ + translations/harbour-tooterb-de.ts \ + translations/harbour-tooterb-el.ts \ + translations/harbour-tooterb-es.ts \ + translations/harbour-tooterb-fr.ts \ + translations/harbour-tooterb-it.ts \ + translations/harbour-tooterb-nl.ts \ + translations/harbour-tooterb-nl_BE.ts \ + translations/harbour-tooterb-oc.ts \ + translations/harbour-tooterb-pl.ts \ + translations/harbour-tooterb-ru.ts \ + translations/harbour-tooterb-sr.ts \ + translations/harbour-tooterb-sv.ts \ + translations/harbour-tooterb-zh_CN.ts + diff --git a/harbour-tooterb.pro.user b/harbour-tooterb.pro.user index a74221b..3519283 100644 --- a/harbour-tooterb.pro.user +++ b/harbour-tooterb.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -63,6 +63,370 @@ ProjectExplorer.Project.Target.0 + + SailfishOS-3.2.1.20-i486 (in Sailfish OS Build Engine) + SailfishOS-3.2.1.20-i486 (in Sailfish OS Build Engine) + SailfishOS-3.2.1.20-i486 + 1 + 1 + 0 + + C:/Users/XPAM/Github/Github-App/build-harbour-tooterb-SailfishOS_3_2_1_20_i486_in_Sailfish_OS_Build_Engine-Debug + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 3 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 2 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + C:/Users/XPAM/Github/Github-App/build-harbour-tooterb-SailfishOS_3_2_1_20_i486_in_Sailfish_OS_Build_Engine-Release + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 3 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 2 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + + C:/Users/XPAM/Github/Github-App/build-harbour-tooterb-SailfishOS_3_2_1_20_i486_in_Sailfish_OS_Build_Engine-Profile + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + true + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 3 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 2 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Profile + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 3 + + + + true + Prepare Target + + QmakeProjectManager.MerPrepareTargetStep + + + true + RPM + + QmakeProjectManager.MerRpmDeployStep + + 2 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy As RPM Package + + QmakeProjectManager.MerRpmDeployConfiguration + + + + + true + RPM + + QmakeProjectManager.MerRpmBuildStep + + + true + RPM Validation + + QmakeProjectManager.MerRpmValidationStep + + 2 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Build RPM Package For Manual Deployment + + QmakeProjectManager.MerMb2RpmBuildConfiguration + + + + + true + Prepare Target + + QmakeProjectManager.MerPrepareTargetStep + + + true + Rsync + + QmakeProjectManager.MerRsyncDeployStep + + 2 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy By Copying Binaries + + QmakeProjectManager.MerRSyncDeployConfiguration + + 3 + + + dwarf + + cpu-cycles + + + 250 + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + C:/Users/XPAM/Github/Github-App/harbour-tooter + false + -1 + 3 + + 1 + + + harbour-tooterb (on Sailfish OS Emulator 3.3.0.16) + QmakeProjectManager.MerRunConfiguration:C:/Users/XPAM/Github/Github-App/harbour-tooter/harbour-tooterb.pro + 1 + + false + + 3768 + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.Target.1 SailfishOS-3.3.0.16-armv7hl (in Sailfish OS Build Engine) SailfishOS-3.3.0.16-armv7hl (in Sailfish OS Build Engine) @@ -426,7 +790,7 @@ - ProjectExplorer.Project.Target.1 + ProjectExplorer.Project.Target.2 SailfishOS-3.3.0.16-i486 (in Sailfish OS Build Engine) SailfishOS-3.3.0.16-i486 (in Sailfish OS Build Engine) @@ -789,9 +1153,373 @@ 1 + + ProjectExplorer.Project.Target.3 + + SailfishOS-3.2.1.20-armv7hl (in Sailfish OS Build Engine) + SailfishOS-3.2.1.20-armv7hl (in Sailfish OS Build Engine) + SailfishOS-3.2.1.20-armv7hl + 1 + 1 + 0 + + C:/Users/XPAM/Github/Github-App/build-harbour-tooterb-SailfishOS_3_2_1_20_armv7hl_in_Sailfish_OS_Build_Engine-Debug + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 3 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 2 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + C:/Users/XPAM/Github/Github-App/build-harbour-tooterb-SailfishOS_3_2_1_20_armv7hl_in_Sailfish_OS_Build_Engine-Release + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 3 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 2 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + + C:/Users/XPAM/Github/Github-App/build-harbour-tooterb-SailfishOS_3_2_1_20_armv7hl_in_Sailfish_OS_Build_Engine-Profile + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + true + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 3 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Start Build Engine + + Mer.MerSdkStartStep + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 2 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Profile + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 3 + + + + true + Prepare Target + + QmakeProjectManager.MerPrepareTargetStep + + + true + RPM + + QmakeProjectManager.MerRpmDeployStep + + 2 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy As RPM Package + + QmakeProjectManager.MerRpmDeployConfiguration + + + + + true + RPM + + QmakeProjectManager.MerRpmBuildStep + + + true + RPM Validation + + QmakeProjectManager.MerRpmValidationStep + + 2 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Build RPM Package For Manual Deployment + + QmakeProjectManager.MerMb2RpmBuildConfiguration + + + + + true + Prepare Target + + QmakeProjectManager.MerPrepareTargetStep + + + true + Rsync + + QmakeProjectManager.MerRsyncDeployStep + + 2 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy By Copying Binaries + + QmakeProjectManager.MerRSyncDeployConfiguration + + 3 + + + dwarf + + cpu-cycles + + + 250 + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + C:/Users/XPAM/Github/Github-App/harbour-tooter + false + -1 + 3 + + 1 + + + harbour-tooterb (on Sailfish OS Emulator 3.3.0.16) + QmakeProjectManager.MerRunConfiguration:C:/Users/XPAM/Github/Github-App/harbour-tooter/harbour-tooterb.pro + 1 + + false + + 3768 + false + true + false + false + true + + + + 1 + + ProjectExplorer.Project.TargetCount - 2 + 4 ProjectExplorer.Project.Updater.FileVersion diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml index 2383490..bc5c38f 100644 --- a/qml/cover/CoverPage.qml +++ b/qml/cover/CoverPage.qml @@ -30,9 +30,9 @@ import QtQuick 2.0 import Sailfish.Silica 1.0 - import "../lib/API.js" as Logic + CoverBackground { onStatusChanged: { switch (status ){ @@ -46,20 +46,21 @@ CoverBackground { break; } } + Image { id: bg + source: "../images/tooter-cover.svg" + horizontalAlignment: Image.AlignLeft + verticalAlignment: Image.AlignBottom + fillMode: Image.PreserveAspectFit anchors { bottom : parent.bottom left: parent.left right: parent.right top: parent.top } - horizontalAlignment: Image.AlignLeft - verticalAlignment: Image.AlignBottom - fillMode: Image.PreserveAspectFit - - source: "../images/tooterb.svg" } + Timer { id: timer interval: 60*1000 @@ -70,33 +71,34 @@ CoverBackground { Image { id: iconNot + source: "image://theme/icon-s-alarm?" + Theme.highlightColor anchors { left: parent.left top: parent.top leftMargin: Theme.paddingLarge topMargin: Theme.paddingLarge } - source: "image://theme/icon-s-alarm?" + Theme.highlightColor } + Label { id: notificationsLbl + text: " " + color: Theme.highlightColor anchors { left: iconNot.right leftMargin: Theme.paddingMedium verticalCenter: iconNot.verticalCenter } - text: " " - color: Theme.highlightColor } Label { + text: "Tooter β" + color: Theme.secondaryColor anchors { right: parent.right rightMargin: Theme.paddingLarge verticalCenter: iconNot.verticalCenter } - text: "Tooter β" - color: Theme.primaryColor } signal activateapp(string person, string notice) @@ -112,8 +114,11 @@ CoverBackground { CoverAction { iconSource: "image://theme/icon-cover-new" onTriggered: { - pageStack.push(Qt.resolvedUrl("./../pages/Conversation.qml"), {}) - appWindow.activate(); + pageStack.push(Qt.resolvedUrl("./../pages/ConversationPage.qml"), { + headerTitle: qsTr("New Toot"), + type: "new" + }) + appWindow.activate() } } } @@ -135,5 +140,5 @@ CoverBackground { notificationsLbl.text = notificationsNum; Logic.conf.notificationLastID = notificationLastID; } -} +} diff --git a/qml/harbour-tooterb.qml b/qml/harbour-tooterb.qml index 7b23d5e..60e8399 100644 --- a/qml/harbour-tooterb.qml +++ b/qml/harbour-tooterb.qml @@ -33,23 +33,26 @@ import Sailfish.Silica 1.0 import "pages" import "./lib/API.js" as Logic -ApplicationWindow -{ +ApplicationWindow { id: appWindow - //initialPage: Component { FirstPage { } } - cover: Qt.resolvedUrl("cover/CoverPage.qml") allowedOrientations: defaultAllowedOrientations + cover: Qt.resolvedUrl("cover/CoverPage.qml") Component.onCompleted: { - var obj = {}; - Logic.mediator.installTo(obj); - obj.subscribe('confLoaded', function(){ + var obj = {} + Logic.mediator.installTo(obj) + obj.subscribe('confLoaded', function() { console.log('confLoaded'); //console.log(JSON.stringify(Logic.conf)) if (!Logic.conf['notificationLastID']) - Logic.conf['notificationLastID'] = 0; + Logic.conf['notificationLastID'] = 0 + if (Logic.conf['instance']) { - Logic.api = new Logic.MastodonAPI({ instance: Logic.conf['instance'], api_user_token: "" }); + Logic.api = Logic.mastodonAPI({ + "instance": Logic.conf['instance'], + "api_user_token": "" + }) } + if (Logic.conf['login']) { //Logic.conf['notificationLastID'] = 0 Logic.api.setConfig("api_user_token", Logic.conf['api_user_token']) @@ -57,17 +60,12 @@ ApplicationWindow Logic.api.get('instance', [], function(data) { console.log(JSON.stringify(data)) pageStack.push(Qt.resolvedUrl("./pages/MainPage.qml"), {}) - }); - - // - // + }) //pageStack.push(Qt.resolvedUrl("./pages/Conversation.qml"), {}) } else { pageStack.push(Qt.resolvedUrl("./pages/LoginPage.qml"), {}) } - - - }); + }) Logic.init() } @@ -75,19 +73,18 @@ ApplicationWindow //Logic.conf.notificationLastID = 0; Logic.saveData() } - Connections - { - target: Dbus - onViewtoot: - { - console.log(key, "dbus onViewtoot") - } - onActivateapp: - { - console.log ("dbus activate app") - pageStack.pop(pageStack.find( function(page){ return (page._depth === 0) })) - activate() - } - } -} + Connections { + target: Dbus + onViewtoot: { + console.log(key, "dbus onViewtoot") + } + onActivateapp: { + console.log ("dbus activate app") + pageStack.pop(pageStack.find( function(page) { + return (page._depth === 0) + })) + activate() + } + } +} diff --git a/qml/images/boosted.svg b/qml/images/boosted.svg deleted file mode 100644 index 468e2da..0000000 --- a/qml/images/boosted.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - diff --git a/qml/images/emojiselect.svg b/qml/images/icon-m-emoji.svg similarity index 100% rename from qml/images/emojiselect.svg rename to qml/images/icon-m-emoji.svg diff --git a/qml/images/icon-s-bookmark.svg b/qml/images/icon-s-bookmark.svg new file mode 100644 index 0000000..f05bf8a --- /dev/null +++ b/qml/images/icon-s-bookmark.svg @@ -0,0 +1,7 @@ + + icon-s-bookmark + + + + + diff --git a/qml/images/icon-s-follow.svg b/qml/images/icon-s-follow.svg new file mode 100644 index 0000000..ebcf5ee --- /dev/null +++ b/qml/images/icon-s-follow.svg @@ -0,0 +1,12 @@ + + Artboard 1 + + + + + + + + + + diff --git a/qml/images/notification.svg b/qml/images/notification.svg deleted file mode 100644 index 56f1b35..0000000 --- a/qml/images/notification.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/qml/images/tooterb.svg b/qml/images/tooter-cover.svg similarity index 100% rename from qml/images/tooterb.svg rename to qml/images/tooter-cover.svg diff --git a/qml/images/verified.svg b/qml/images/verified.svg deleted file mode 100644 index 65d529b..0000000 --- a/qml/images/verified.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - diff --git a/qml/lib/API.js b/qml/lib/API.js index f930c0c..cfc6fa8 100644 --- a/qml/lib/API.js +++ b/qml/lib/API.js @@ -9,6 +9,7 @@ var mediator = (function(){ mediator.channels[channel].push({ context : this, callback : fn }); return this; }; + var publish = function(channel){ if(!mediator.channels[channel]) return false; var args = Array.prototype.slice.call(arguments, 1); @@ -18,6 +19,7 @@ var mediator = (function(){ }; return this; }; + return { channels : {}, publish : publish, @@ -28,6 +30,7 @@ var mediator = (function(){ } }; }()); + var init = function(){ console.log("db.version: "+db.version); if(db.version === '') { @@ -35,7 +38,7 @@ var init = function(){ tx.executeSql('CREATE TABLE IF NOT EXISTS settings (' + ' key TEXT UNIQUE, ' + ' value TEXT ' - +');'); + + ');'); //tx.executeSql('INSERT INTO settings (key, value) VALUES (?, ?)', ["conf", "{}"]); }); db.changeVersion('', '0.1', function(tx) { @@ -88,21 +91,22 @@ var tootParser = function(data){ ret.display_name = data.account.display_name ret.avatar_static = data.account.avatar_static - ret.favourited = data.favourited ? true : false - ret.favourites_count = data.favourites_count ? data.favourites_count : 0 + ret.status_favourites_count = data.favourites_count ? data.favourites_count : 0 ret.reblog = data.reblog ? true : false ret.reblogged = data.reblogged ? true : false - ret.reblogs_count = data.reblogs_count ? data.reblogs_count : false + ret.status_reblogs_count = data.reblogs_count ? data.reblogs_count : false + + ret.bookmarked = data.bookmarked ? true : false ret.muted = data.muted ? true : false ret.sensitive = data.sensitive ? true : false ret.visibility = data.visibility ? data.visibility : false - console.log(ret) } + var test = 1; Qt.include("Mastodon.js") @@ -161,6 +165,7 @@ var notifier = function(item){ key: item.id } break; + case "follow": msg = { urgency: "critical", @@ -182,6 +187,7 @@ var notifier = function(item){ key: item.id } break; + case "mention": msg = { urgency: "critical", @@ -193,6 +199,7 @@ var notifier = function(item){ key: item.id } break; + default: //console.log(JSON.stringify(messageObject.data)) return; diff --git a/qml/lib/Mastodon.js b/qml/lib/Mastodon.js index 8a8e4c6..97b5bcc 100644 --- a/qml/lib/Mastodon.js +++ b/qml/lib/Mastodon.js @@ -3,17 +3,19 @@ // do whatever you want with it // but please don't hurt it (and keep this header) -var MastodonAPI = function(config) { +var mastodonAPI = function(config) { var apiBase = config.instance + "/api/v1/"; return { setConfig: function (key, value) { // modify initial config afterwards config[key] = value; }, + getConfig: function(key) { //get config key return config[key]; }, + get: function (endpoint) { // for GET API calls // can be called with two or three parameters @@ -56,8 +58,8 @@ var MastodonAPI = function(config) { http.setRequestHeader("Connection", "close"); http.onreadystatechange = function() { // Call a function when the state changes. - if (http.readyState == 4) { - if (http.status == 200) { + if (http.readyState === 4) { + if (http.status === 200) { console.log("Successful GET API request to " +apiBase+endpoint); callback(JSON.parse(http.response),http.status) } else { @@ -67,6 +69,7 @@ var MastodonAPI = function(config) { } http.send(); }, + post: function (endpoint) { // for POST API calls var postData, callback; @@ -91,8 +94,8 @@ var MastodonAPI = function(config) { http.setRequestHeader("Connection", "close"); http.onreadystatechange = function() { // Call a function when the state changes. - if (http.readyState == 4) { - if (http.status == 200) { + if (http.readyState === 4) { + if (http.status === 200) { console.log("Successful POST API request to " +apiBase+endpoint); callback(JSON.parse(http.response),http.status) } else { @@ -113,6 +116,7 @@ var MastodonAPI = function(config) { } });*/ }, + delete: function (endpoint, callback) { // for DELETE API calls. $.ajax({ @@ -125,6 +129,7 @@ var MastodonAPI = function(config) { } }); }, + stream: function (streamType, onData) { // Event Stream Support // websocket streaming is undocumented. i had to reverse engineer the fucking web client. @@ -132,7 +137,7 @@ var MastodonAPI = function(config) { // user for your local home TL and notifications // public for your federated TL // public:local for your home TL - // hashtag&tag=fuckdonaldtrump for the stream of #fuckdonaldtrump + // hashtag&tag=mastodonrocks for the stream of #mastodonrocks // callback gets called whenever new data ist recieved // callback { event: (eventtype), payload: {mastodon object as described in the api docs} } // eventtype could be notification (=notification) or update (= new toot in TL) @@ -147,12 +152,10 @@ var MastodonAPI = function(config) { onData(event); }; es.onmessage = listener; - - }, + registerApplication: function (client_name, redirect_uri, scopes, website, callback) { //register a new application - // OAuth Auth flow: // First register the application // 2) get a access code from a user (using the link, generation function below!) @@ -180,8 +183,8 @@ var MastodonAPI = function(config) { http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); http.onreadystatechange = function() { // Call a function when the state changes. - if (http.readyState == 4) { - if (http.status == 200) { + if (http.readyState === 4) { + if (http.status === 200) { console.log("Registered Application: " + http.response); callback(http.response) } else { @@ -191,10 +194,12 @@ var MastodonAPI = function(config) { } http.send(params); }, + generateAuthLink: function (client_id, redirect_uri, responseType, scopes) { return config.instance + "/oauth/authorize?client_id=" + client_id + "&redirect_uri=" + redirect_uri + "&response_type=" + responseType + "&scope=" + scopes.join("+"); }, + getAccessTokenFromAuthCode: function (client_id, client_secret, redirect_uri, code, callback) { /*$.ajax({ url: config.instance + "/oauth/token", @@ -221,8 +226,8 @@ var MastodonAPI = function(config) { http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); http.onreadystatechange = function() { // Call a function when the state changes. - if (http.readyState == 4) { - if (http.status == 200) { + if (http.readyState === 4) { + if (http.status === 200) { console.log("Got Token: " + http.response); callback(http.response) } else { @@ -236,7 +241,7 @@ var MastodonAPI = function(config) { }; // node.js -if (typeof module !== 'undefined') { module.exports = MastodonAPI; }; +if (typeof module !== 'undefined') { module.exports = mastodonAPI; }; String.prototype.replaceAll = function(search, replacement) { var target = this; diff --git a/qml/lib/Worker.js b/qml/lib/Worker.js index 8b53e8c..1373fb0 100644 --- a/qml/lib/Worker.js +++ b/qml/lib/Worker.js @@ -1,5 +1,8 @@ Qt.include("Mastodon.js") + + var loadImages = true; + WorkerScript.onMessage = function(msg) { console.log("Action > " + msg.action) console.log("Model > " + msg.model) @@ -7,27 +10,31 @@ WorkerScript.onMessage = function(msg) { console.log("Conf > " + JSON.stringify(msg.conf)) console.log("Params > " + JSON.stringify(msg.params)) - // order notifications in ASC order + /** order notifications in ASC order */ function orderNotifications(items){ - for (var i = items.length-1; i > 0; i--){ + for (var i = items.length-1; i > 0; i--) { if (items[i].id > 0 ) //msg.conf.notificationLastID) WorkerScript.sendMessage({ 'fireNotification': true, "data": items[i]}) } } - if (!msg.conf || !msg.conf.login){ + /** Logged-in status */ + if (!msg.conf || !msg.conf.login) { console.log("Not loggedin") return; } + + /** Load images */ if (typeof msg.conf['loadImages'] !== "undefined") loadImages = msg.conf['loadImages'] - var API = MastodonAPI({ instance: msg.conf.instance, api_user_token: msg.conf.api_user_token}); + /** POST statuses */ + var API = mastodonAPI({ instance: msg.conf.instance, api_user_token: msg.conf.api_user_token}); if (msg.method === "POST"){ API.post(msg.action, msg.params, function(data) { if (msg.bgAction){ console.log(JSON.stringify(data)) - } else if (msg.action === "statuses"){ + } else if (msg.action === "statuses") { // status posted if(msg.model){ var item = parseToot(data); @@ -59,7 +66,7 @@ WorkerScript.onMessage = function(msg) { } else if(msg.action === "notifications") { // notification - //console.log("Is notification... parsing...") + console.log("Get notification list") console.log(JSON.stringify(data[i])) item = parseNotification(data[i]); items.push(item) @@ -69,7 +76,9 @@ WorkerScript.onMessage = function(msg) { console.log("ancestors") for (var j = 0; j < data[i].length; j ++) { item = parseToot(data[i][j]); - item['id'] = item['status_id'] + item['id'] = item['status_id']; + if (typeof item['attachments'] === "undefined") + item['attachments'] = []; items.push(item) console.log(JSON.stringify(data[i][j])) } @@ -78,7 +87,7 @@ WorkerScript.onMessage = function(msg) { //console.log(JSON.stringify(i)) } else if(msg.action.indexOf("statuses") >-1 && msg.action.indexOf("context") >-1 && i === "descendants") { - // status ancestors toots - conversation + // status descendants toots - conversation console.log("descendants") for (var j = 0; j < data[i].length; j ++) { item = parseToot(data[i][j]); @@ -91,16 +100,18 @@ WorkerScript.onMessage = function(msg) { addDataToModel (msg.model, "append", items); items = []; - } else if (data[i].hasOwnProperty("content")){ - //console.log("Is toot... parsing...") + } else if (data[i].hasOwnProperty("content")){ + //console.log("Get Toot") item = parseToot(data[i]); item['id'] = item['status_id'] items.push(item) + } else { - WorkerScript.sendMessage({ 'action': msg.action, 'success': true, key: i, "data": data[i]}) + WorkerScript.sendMessage({ 'action': msg.action, 'success': true, key: i, "data": data[i] }) } } } + if(msg.model && items.length) addDataToModel(msg.model, msg.mode, items) /*if(msg.action === "notifications") @@ -108,40 +119,53 @@ WorkerScript.onMessage = function(msg) { }); } - - //WorkerScript.sendMessage({ 'notifyNewItems': length - i }) -function addDataToModel (model, mode, items){ +function addDataToModel (model, mode, items) { var length = items.length; console.log("Fetched > " +length) if (mode === "append") { model.append(items) } else if (mode === "prepend") { - for(var i = length-1; i >= 0 ; i--){ + for(var i = length-1; i >= 0 ; i--) { model.insert(0,items[i]) } } - model.sync() } -function parseAccounts(collection, prefix, data){ - var res = collection; +/** Function: Get Account Data */ +function parseAccounts(collection, prefix, data) { + + var res = collection; + // Base attributes res[prefix + 'account_id'] = data["id"] res[prefix + 'account_username'] = data["username"] res[prefix + 'account_acct'] = data["acct"] + res[prefix + 'account_url'] = data["url"] + // Display attributes res[prefix + 'account_display_name'] = data["display_name"] - res[prefix + 'account_discoverable'] = data["discoverable"] - res[prefix + 'account_locked'] = data["locked"] - res[prefix + 'account_created_at'] = data["created_at"] + res[prefix + 'account_note'] = data["note"] res[prefix + 'account_avatar'] = data["avatar"] res[prefix + 'account_header'] = data["header"] + res[prefix + 'account_locked'] = data["locked"] + //res[prefix + 'account_emojis'] = data["emojis"] + res[prefix + 'account_discoverable'] = data["discoverable"] + // Statistical attributes + res[prefix + 'account_created_at'] = data["created_at"] + res[prefix + 'account_statuses_count'] = data["statuses_count"] + res[prefix + 'account_followers_count'] = data["followers_count"] + res[prefix + 'account_following_count'] = data["following_count"] + // Optional attributes + //res[prefix + 'account_fields'] = data["fields"] + res[prefix + 'account_bot'] = data["bot"] + res[prefix + 'account_group'] = data["group"] - // console.log(JSON.stringify(res)) + //console.log(JSON.stringify(res)) return (res); } +/** Function: Get Notification Data */ function parseNotification(data){ //console.log(JSON.stringify(data)) var item = { @@ -150,50 +174,49 @@ function parseNotification(data){ attachments: [] }; switch (item['type']){ + case "mention": if (!data.status) { - break; + break; } - item = parseToot(data.status) - item['typeIcon'] = "image://theme/icon-s-retweet" item['typeIcon'] = "image://theme/icon-s-alarm" - item['type'] = "mention"; + item['type'] = "mention" break; + case "reblog": if (!data.status) { break; } - item = parseToot(data.status) item = parseAccounts(item, "reblog_", data["account"]) item = parseAccounts(item, "", data["status"]["account"]) - item['status_reblog'] = true; - item['type'] = "reblog"; + item['status_reblog'] = true + item['type'] = "reblog" item['typeIcon'] = "image://theme/icon-s-retweet" break; + case "favourite": if (!data.status) { break; } - item = parseToot(data.status) item = parseAccounts(item, "reblog_", data["account"]) item = parseAccounts(item, "", data["status"]["account"]) - item['status_reblog'] = true; + item['status_reblog'] = true + item['type'] = "favourite" item['typeIcon'] = "image://theme/icon-s-favorite" - item['type'] = "favourite"; - //item['retweetScreenName'] = item['reblog_account_username']; break; + case "follow": item['type'] = "follow"; item = parseAccounts(item, "", data["account"]) item = parseAccounts(item, "reblog_", data["account"]) - item['content'] = data['account']['note'] - item['typeIcon'] = "image://theme/icon-s-installed" - item['attachments'] = [] - + //item['content'] = data['account']['note'] + item['typeIcon'] = "../../images/icon-s-follow.svg" + //item['attachments'] = [] break; + default: item['typeIcon'] = "image://theme/icon-s-sailfish" } @@ -204,6 +227,7 @@ function parseNotification(data){ return item; } +/** Function: */ function collect() { var ret = {}; var len = arguments.length; @@ -216,70 +240,81 @@ function collect() { } return ret; } -function getDate(dateStr){ + +/** Function: Get Status date */ +function getDate(dateStr) { var ts = new Date(dateStr); return new Date(ts.getFullYear(), ts.getMonth(), ts.getDate(), 0, 0, 0) } -function parseToot (data){ + +/** Function: Get Status data */ +function parseToot (data) { var i = 0; var item = {}; item['type'] = "toot" item['highlight'] = false item['status_id'] = data["id"] - item['status_uri'] = data["uri"] - item['status_url'] = data["url"] - item['status_in_reply_to_id'] = data["in_reply_to_id"] - item['status_in_reply_to_account_id'] = data["in_reply_to_account_id"] - item['status_reblog'] = data["reblog"] ? true : false - item['status_content'] = data["content"] - item['status_created_at'] = item['created_at'] = new Date(data["created_at"]); - item['section'] = getDate(data["created_at"]); - item['reblogs_count'] = data["reblogs_count"] - item['favourites_count'] = data["favourites_count"] - item['reblogged'] = data["reblogged"] - item['favourited'] = data["favourited"] - item['bookmarked'] = data["bookmarked"] + item['status_created_at'] = item['created_at'] = new Date(data["created_at"]) item['status_sensitive'] = data["sensitive"] item['status_spoiler_text'] = data["spoiler_text"] item['status_visibility'] = data["visibility"] + item['status_language'] = data["language"] + item['status_uri'] = data["uri"] + item['status_url'] = data["url"] + item['status_replies_count'] = data["replies_count"] + item['status_reblogs_count'] = data["reblogs_count"] + item['status_favourites_count'] = data["favourites_count"] + item['status_favourited'] = data["favourited"] + item['status_reblogged'] = data["reblogged"] + item['status_bookmarked'] = data["bookmarked"] + item['status_content'] = data["content"] + item['attachments'] = data['media_attachments'] + item['status_in_reply_to_id'] = data["in_reply_to_id"] + item['status_in_reply_to_account_id'] = data["in_reply_to_account_id"] + item['status_reblog'] = data["reblog"] ? true : false + item['section'] = getDate(data["created_at"]) - if(item['status_reblog']){ + /** If Toot is a Reblog */ + if(item['status_reblog']) { item['type'] = "reblog"; item['typeIcon'] = "image://theme/icon-s-retweet" - item['status_id'] = data["reblog"]["id"]; - item['status_spoiler_text'] = data["reblog"]["spoiler_text"] + item['status_id'] = data["reblog"]["id"] item['status_sensitive'] = data["reblog"]["sensitive"] - item['emojis'] = data["reblog"]["emojis"]; + item['status_spoiler_text'] = data["reblog"]["spoiler_text"] + item['status_replies_count'] = data["reblog"]["replies_count"] + item['status_reblogs_count'] = data["reblog"]["reblogs_count"] + item['status_favourites_count'] = data["reblog"]["favourites_count"] item = parseAccounts(item, "", data['reblog']["account"]) item = parseAccounts(item, "reblog_", data["account"]) } else { item = parseAccounts(item, "", data["account"]) } + + /** Replace HTML content in Toots */ item['content'] = data['content'] - .replaceAll('