Merge pull request #101 from poetaster/master

Meta for chum deployment.
This commit is contained in:
molan-git 2023-01-17 14:13:26 +01:00 committed by GitHub
commit 4c2a37db0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 2642 additions and 1741 deletions

76
.github/workflows/build.yaml vendored Normal file
View file

@ -0,0 +1,76 @@
name: Build RPMs
on:
push:
tags:
- "1.*"
env:
OS_VERSION: 4.4.0.68
jobs:
build:
runs-on: ubuntu-latest
name: Build App
strategy:
matrix:
arch: ['armv7hl', 'aarch64', 'i486']
steps:
- uses: actions/checkout@v2
- name: Prepare
run: docker pull coderus/sailfishos-platform-sdk:$OS_VERSION && mkdir output
- name: Build ${{ matrix.arch }}
run: docker run --rm --privileged -v $PWD:/share coderus/sailfishos-platform-sdk:$OS_VERSION /bin/bash -c "
mkdir -p build ;
cd build ;
cp -r /share/* . ;
mb2 -t SailfishOS-$OS_VERSION-${{ matrix.arch }} build ;
sudo cp -r RPMS/*.rpm /share/output"
- name: Upload RPM (${{ matrix.arch }})
uses: actions/upload-artifact@v2
with:
name: rpm-${{ matrix.arch }}
path: output
release:
name: Release
if: startsWith(github.ref, 'refs/tags/1.1')
needs:
- build
runs-on: ubuntu-latest
steps:
- name: Download armv7hl
uses: actions/download-artifact@v2
with:
name: rpm-armv7hl
continue-on-error: true
- name: Download aarch64
uses: actions/download-artifact@v2
with:
name: rpm-aarch64
continue-on-error: true
- name: Download i486
uses: actions/download-artifact@v2
with:
name: rpm-i486
continue-on-error: true
- name: Extract Version Name
id: extract_name
uses: actions/github-script@v4
with:
result-encoding: string
script: |
return context.payload.ref.replace(/refs\/tags\//, '');
- name: Create a Release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: ${{ steps.extract_name.outputs.result }}
draft: false
prerelease: false
body: This release was autogenerated.
files: '*.rpm'

View file

@ -68,6 +68,7 @@ DISTFILES += qml/harbour-tooterb.qml \
qml/pages/components/MyList.qml \ qml/pages/components/MyList.qml \
qml/pages/components/ProfileHeader.qml \ qml/pages/components/ProfileHeader.qml \
qml/pages/components/MediaBlock.qml \ qml/pages/components/MediaBlock.qml \
qml/pages/components/MediaItem.qml \
qml/cover/CoverPage.qml \ qml/cover/CoverPage.qml \
qml/pages/MainPage.qml \ qml/pages/MainPage.qml \
qml/pages/LoginPage.qml \ qml/pages/LoginPage.qml \
@ -87,7 +88,6 @@ DISTFILES += qml/harbour-tooterb.qml \
rpm/harbour-tooterb.changes \ rpm/harbour-tooterb.changes \
rpm/harbour-tooterb.changes.run.in \ rpm/harbour-tooterb.changes.run.in \
rpm/harbour-tooterb.spec \ rpm/harbour-tooterb.spec \
rpm/harbour-tooterb.yaml \
translations/*.ts \ translations/*.ts \
harbour-tooterb.desktop harbour-tooterb.desktop

File diff suppressed because it is too large Load diff

View file

@ -67,6 +67,8 @@ ApplicationWindow {
} }
}) })
Logic.init() Logic.init()
} }
Component.onDestruction: { Component.onDestruction: {

View file

@ -72,7 +72,7 @@ function saveData() {
} }
} }
} }
console.log("ENF OF SAVING") console.log("END OF SAVING")
}); });
} }
@ -116,6 +116,8 @@ var modelTLpublic = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt
var modelTLlocal = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject'); var modelTLlocal = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject');
var modelTLnotifications = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject'); var modelTLnotifications = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject');
var modelTLsearch = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject'); var modelTLsearch = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject');
var modelTLbookmarks = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject');
var notificationsList = [] var notificationsList = []
var notificationGenerator = function(item){ var notificationGenerator = function(item){

View file

@ -16,6 +16,58 @@ var mastodonAPI = function(config) {
return config[key]; return config[key];
}, },
/*
* function to retrieve the Link header
* using HEAD, so bookmarks has head followed by GET
*/
getLink: function (endpoint) {
// variables
var queryData, callback,
queryStringAppend = "?";
// check with which arguments we're supplied
if (typeof arguments[1] === "function") {
queryData = {};
callback = arguments[1];
} else {
queryData = arguments[1];
callback = arguments[2];
}
// build queryData Object into a URL Query String
for (var i in queryData) {
if (queryData.hasOwnProperty(i)) {
if (typeof queryData[i] === "string") {
queryStringAppend += queryData[i] + "&";
} else if (typeof queryData[i] === "object") {
queryStringAppend += queryData[i].name + "="+ queryData[i].data + "&";
}
}
}
var http = new XMLHttpRequest()
var url = apiBase + endpoint;
console.log("HEAD" + apiBase + endpoint + queryStringAppend)
http.open("HEAD", apiBase + endpoint + queryStringAppend, true);
// Send the proper header information along with the request
http.setRequestHeader("Authorization", "Bearer " + config.api_user_token);
http.setRequestHeader("Content-Type", "application/json");
http.setRequestHeader("Connection", "close");
http.onreadystatechange = function() {
if (http.readyState === 4) {
if (http.status === 200) {
callback( http.getResponseHeader("Link") , http.status)
console.log("Successful HEAD API request to " +apiBase+endpoint);
} else {
console.log("error: " + http.status)
}
}
}
http.send();
},
get: function (endpoint) { get: function (endpoint) {
// for GET API calls // for GET API calls
// can be called with two or three parameters // can be called with two or three parameters
@ -46,10 +98,11 @@ var mastodonAPI = function(config) {
} }
} }
} }
//queryStringAppend += "limit=20"
// ajax function // ajax function
var http = new XMLHttpRequest() var http = new XMLHttpRequest()
var url = apiBase + endpoint; var url = apiBase + endpoint;
console.log(queryStringAppend) console.log(apiBase + endpoint + queryStringAppend)
http.open("GET", apiBase + endpoint + queryStringAppend, true); http.open("GET", apiBase + endpoint + queryStringAppend, true);
// Send the proper header information along with the request // Send the proper header information along with the request
@ -60,8 +113,8 @@ var mastodonAPI = function(config) {
http.onreadystatechange = function() { // Call a function when the state changes. http.onreadystatechange = function() { // Call a function when the state changes.
if (http.readyState === 4) { if (http.readyState === 4) {
if (http.status === 200) { if (http.status === 200) {
console.log("Successful GET API request to " +apiBase+endpoint);
callback(JSON.parse(http.response),http.status) callback(JSON.parse(http.response),http.status)
console.log("Successful GET API request to " +apiBase+endpoint);
} else { } else {
console.log("error: " + http.status) console.log("error: " + http.status)
} }
@ -219,7 +272,7 @@ var mastodonAPI = function(config) {
var http = new XMLHttpRequest() var http = new XMLHttpRequest()
var url = config.instance + "/oauth/token"; var url = config.instance + "/oauth/token";
var params = 'client_id=' + client_id + '&client_secret=' + client_secret + '&redirect_uri=' + redirect_uri + '&grant_type=authorization_code&code=' + code; var params = 'client_id=' + client_id + '&client_secret=' + client_secret + '&redirect_uri=' + redirect_uri + '&grant_type=authorization_code&code=' + code;
console.log(params) // console.log(params)
http.open("POST", url, true); http.open("POST", url, true);
// Send the proper header information along with the request // Send the proper header information along with the request

View file

@ -1,14 +1,40 @@
Qt.include("Mastodon.js") Qt.include("Mastodon.js")
var debug = true;
var loadImages = true; var loadImages = true;
// used to dedupe on append/insert
var knownIds = [];
var max_id ;
var since_id;
WorkerScript.onMessage = function(msg) { WorkerScript.onMessage = function(msg) {
/*
console.log("Action > " + msg.action) console.log("Action > " + msg.action)
console.log("Model > " + msg.model) console.log("Model > " + msg.model)
console.log("Mode > " + msg.mode) console.log("Mode > " + msg.mode)
console.log("Conf > " + JSON.stringify(msg.conf)) console.log("Conf > " + JSON.stringify(msg.conf))
console.log("Params > " + JSON.stringify(msg.params)) console.log("Params > " + JSON.stringify(msg.params))
*/
// this is not elegant. it's max_id and ids from MyList
if (msg.params[1]) {
if ( msg.params[0]["name"] === "max_id" ) {
max_id = msg.params[0]["data"]
} else {
since_id = msg.params[0]["data"]
}
// we don't want to pass it onto the backend
if ( msg.params[1]["name"] === "ids" ) {
knownIds = msg.params[1]["data"]
msg.params.pop()
}
if ( msg.params[2]["name"] === "ids" ) {
knownIds = msg.params[2]["data"]
msg.params.pop()
}
}
/** order notifications in ASC order */ /** order notifications in ASC order */
function orderNotifications(items){ function orderNotifications(items){
@ -20,7 +46,7 @@ WorkerScript.onMessage = function(msg) {
/** Logged-in status */ /** Logged-in status */
if (!msg.conf || !msg.conf.login) { if (!msg.conf || !msg.conf.login) {
console.log("Not loggedin") //console.log("Not loggedin")
return; return;
} }
@ -28,12 +54,30 @@ WorkerScript.onMessage = function(msg) {
if (typeof msg.conf['loadImages'] !== "undefined") if (typeof msg.conf['loadImages'] !== "undefined")
loadImages = msg.conf['loadImages'] loadImages = msg.conf['loadImages']
/** POST statuses */
/* init API statuses */
var API = mastodonAPI({ instance: msg.conf.instance, api_user_token: msg.conf.api_user_token}); var API = mastodonAPI({ instance: msg.conf.instance, api_user_token: msg.conf.api_user_token});
/*
* HEAD call for some actions
* we have to retrieve the Link header
* this falls through and continues for GET
*/
if (msg.action === "bookmarks"){
API.getLink(msg.action, msg.params, function(data) {
if (debug) console.log(JSON.stringify(data))
WorkerScript.sendMessage({ 'LinkHeader': data })
});
}
/** POST statuses */
if (msg.method === "POST"){ if (msg.method === "POST"){
API.post(msg.action, msg.params, function(data) { API.post(msg.action, msg.params, function(data) {
if (msg.bgAction){ if (msg.bgAction){
console.log(JSON.stringify(data)) //console.log(JSON.stringify(data))
} else if (msg.action === "statuses") { } else if (msg.action === "statuses") {
// status posted // status posted
if(msg.model){ if(msg.model){
@ -45,7 +89,7 @@ WorkerScript.onMessage = function(msg) {
} else { } else {
for (var i in data) { for (var i in data) {
if (data.hasOwnProperty(i)) { if (data.hasOwnProperty(i)) {
console.log(JSON.stringify(data[i])) //console.log(JSON.stringify(data[i]))
WorkerScript.sendMessage({ 'action': msg.action, 'success': true, key: i, "data": data[i]}) WorkerScript.sendMessage({ 'action': msg.action, 'success': true, key: i, "data": data[i]})
} }
} }
@ -55,21 +99,26 @@ WorkerScript.onMessage = function(msg) {
} }
API.get(msg.action, msg.params, function(data) { API.get(msg.action, msg.params, function(data) {
var items = []; var items = [];
//console.log(data)
for (var i in data) { for (var i in data) {
var item; var item;
if (data.hasOwnProperty(i)) { if (data.hasOwnProperty(i)) {
if(msg.action === "accounts/search") { if(msg.action === "accounts/search") {
item = parseAccounts([], "", data[i]); item = parseAccounts([], "", data[i]);
console.log(JSON.stringify(data[i])) //console.log(JSON.stringify(data[i]))
console.log("has own data")
items.push(item) items.push(item)
} else if(msg.action === "notifications") { } else if(msg.action === "notifications") {
// notification // notification
console.log("Get notification list") //console.log("Get notification list")
console.log(JSON.stringify(data[i])) //console.log(JSON.stringify(data[i]))
item = parseNotification(data[i]); item = parseNotification(data[i]);
items.push(item) items.push(item);
} else if(msg.action.indexOf("statuses") >-1 && msg.action.indexOf("context") >-1 && i === "ancestors") { } else if(msg.action.indexOf("statuses") >-1 && msg.action.indexOf("context") >-1 && i === "ancestors") {
// status ancestors toots - conversation // status ancestors toots - conversation
@ -79,8 +128,9 @@ WorkerScript.onMessage = function(msg) {
item['id'] = item['status_id']; item['id'] = item['status_id'];
if (typeof item['attachments'] === "undefined") if (typeof item['attachments'] === "undefined")
item['attachments'] = []; item['attachments'] = [];
items.push(item) // don't permit doubles
console.log(JSON.stringify(data[i][j])) items.push(item);
//console.log(JSON.stringify(data[i][j]))
} }
addDataToModel (msg.model, "prepend", items); addDataToModel (msg.model, "prepend", items);
items = []; items = [];
@ -94,8 +144,8 @@ WorkerScript.onMessage = function(msg) {
item['id'] = item['status_id']; item['id'] = item['status_id'];
if (typeof item['attachments'] === "undefined") if (typeof item['attachments'] === "undefined")
item['attachments'] = []; item['attachments'] = [];
items.push(item) items.push(item);
console.log(JSON.stringify(data[i][j])) //console.log(JSON.stringify(data[i][j]))
} }
addDataToModel (msg.model, "append", items); addDataToModel (msg.model, "append", items);
items = []; items = [];
@ -104,7 +154,8 @@ WorkerScript.onMessage = function(msg) {
//console.log("Get Toot") //console.log("Get Toot")
item = parseToot(data[i]); item = parseToot(data[i]);
item['id'] = item['status_id'] item['id'] = item['status_id']
items.push(item) items.push(item);
} else { } 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] })
@ -112,29 +163,66 @@ WorkerScript.onMessage = function(msg) {
} }
} }
if(msg.model && items.length) if(msg.model && items.length) {
addDataToModel(msg.model, msg.mode, items) addDataToModel(msg.model, msg.mode, items)
} else {
// for some reason, home chokes.
console.log( "items.length = " + items.length)
}
/*if(msg.action === "notifications") /*if(msg.action === "notifications")
orderNotifications(items)*/ orderNotifications(items)*/
console.log("Get em all?")
WorkerScript.sendMessage({ 'updatedAll': true})
}); });
} }
//WorkerScript.sendMessage({ 'notifyNewItems': length - i }) //WorkerScript.sendMessage({ 'notifyNewItems': length - i })
function addDataToModel (model, mode, items) { function addDataToModel (model, mode, items) {
var length = items.length; var length = items.length;
console.log("Fetched > " +length) var i
if (debug) console.log("Fetched > " +length + " in " + mode)
if (debug) console.log("ids > " + knownIds.length )
if (mode === "append") { if (mode === "append") {
model.append(items) for(i = 0; i <= length-1; i++) {
if ( knownIds.indexOf( items[i]["id"]) === -1) {
model.append(items[i])
} else {
console.log("nope: " + items[i]["id"] )
}
}
// search does not use ids
if ( knownIds.length < 1 ) model.append(items)
} else if (mode === "prepend") { } else if (mode === "prepend") {
for(var i = length-1; i >= 0 ; i--) { for(i = length-1; i >= 0 ; i--) {
model.insert(0,items[i]) model.insert(0,items[i])
/*if ( knownIds.indexOf( items[i]["id"]) === -1) {
model.insert(0,items[i])
}*/
} }
} }
model.sync() model.sync()
} }
/** Function: Get Account Data */ function findDuplicate(arr,val) {
for(var i=0; i < arr.length; i++){
if( arr.indexOf(val) === -1 ) {
return true;
}
}
return false;
}
/* Function: Get Account Data */
function parseAccounts(collection, prefix, data) { function parseAccounts(collection, prefix, data) {
var res = collection; var res = collection;

View file

@ -8,6 +8,7 @@ import "./components/"
Page { Page {
id: conversationPage id: conversationPage
property bool debug: false
property ListModel suggestedModel property ListModel suggestedModel
property ListModel mdl property ListModel mdl
property int tootMaxChar: 500 property int tootMaxChar: 500
@ -25,9 +26,11 @@ Page {
property string status_link: property string status_link:
if (status_url === "") { if (status_url === "") {
var test = status_uri.split("/") var test = status_uri.split("/")
if (debug) {
console.log(status_uri) console.log(status_uri)
console.log(JSON.stringify(test)) console.log(JSON.stringify(test))
console.log(JSON.stringify(test.length)) console.log(JSON.stringify(test.length))
}
if (test.length === 8 && (test[7] === "activity")) { if (test.length === 8 && (test[7] === "activity")) {
var urialt = status_uri.replace("activity", "") var urialt = status_uri.replace("activity", "")
status_link = urialt status_link = urialt
@ -37,7 +40,7 @@ Page {
allowedOrientations: Orientation.All allowedOrientations: Orientation.All
onSuggestedUserChanged: { onSuggestedUserChanged: {
console.log(suggestedUser) //console.log(suggestedUser)
suggestedModel = Qt.createQmlObject( 'import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject' ) suggestedModel = Qt.createQmlObject( 'import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject' )
predictionList.visible = false predictionList.visible = false
if (suggestedUser.length > 0) { if (suggestedUser.length > 0) {
@ -67,7 +70,9 @@ Page {
WorkerScript { WorkerScript {
id: worker id: worker
source: "../lib/Worker.js" source: "../lib/Worker.js"
onMessage: { console.log(JSON.stringify(messageObject)) } onMessage: {
//console.log(JSON.stringify(messageObject))
}
} }
SilicaListView { SilicaListView {
@ -98,7 +103,7 @@ Page {
if (mdl) if (mdl)
for (var i = 0; i < mdl.count; i++) { for (var i = 0; i < mdl.count; i++) {
if (mdl.get(i).status_id === status_id) { if (mdl.get(i).status_id === status_id) {
console.log(mdl.get(i).status_id) //console.log(mdl.get(i).status_id)
positionViewAtIndex(i, ListView.Center) positionViewAtIndex(i, ListView.Center)
} }
} }
@ -266,7 +271,7 @@ Page {
textOperations.select( textOperations.select(
textOperations.selectionStart ? textOperations.selectionStart - 1 : 0, textOperations.selectionStart ? textOperations.selectionStart - 1 : 0,
textOperations.selectionEnd) textOperations.selectionEnd)
console.log(toot.text.length) //console.log(toot.text.length)
suggestedUser = "" suggestedUser = ""
if (textOperations.selectedText.charAt(0) === "@") { if (textOperations.selectedText.charAt(0) === "@") {
suggestedUser = textOperations.selectedText.trim().substring(1) suggestedUser = textOperations.selectedText.trim().substring(1)
@ -292,7 +297,9 @@ Page {
right: parent.right right: parent.right
rightMargin: Theme.paddingSmall rightMargin: Theme.paddingSmall
} }
onSelectionChanged: { console.log(selection) } onSelectionChanged: {
//console.log(selection)
}
onClicked: pageStack.push(emojiDialog) onClicked: pageStack.push(emojiDialog)
} }
@ -321,7 +328,7 @@ Page {
} }
onClicked: { onClicked: {
var idx = index var idx = index
console.log(idx) //console.log(idx)
//mediaModel.remove(idx) //mediaModel.remove(idx)
remorse.execute(myDelegate, "", function () { remorse.execute(myDelegate, "", function () {
mediaModel.remove(idx) mediaModel.remove(idx)
@ -381,7 +388,9 @@ Page {
var imagePicker = pageStack.push("Sailfish.Pickers.ImagePickerPage", { "allowedOrientations": Orientation.All }) var imagePicker = pageStack.push("Sailfish.Pickers.ImagePickerPage", { "allowedOrientations": Orientation.All })
imagePicker.selectedContentChanged.connect(function () { imagePicker.selectedContentChanged.connect(function () {
var imagePath = imagePicker.selectedContent var imagePath = imagePicker.selectedContent
console.log(imagePath)
// console.log(imagePath)
imageUploader.setUploadUrl(Logic.conf.instance + "/api/v1/media") imageUploader.setUploadUrl(Logic.conf.instance + "/api/v1/media")
imageUploader.setFile(imagePath) imageUploader.setFile(imagePath)
imageUploader.setAuthorizationHeader(Logic.conf.api_user_token) imageUploader.setAuthorizationHeader(Logic.conf.api_user_token)
@ -393,19 +402,19 @@ Page {
ImageUploader { ImageUploader {
id: imageUploader id: imageUploader
onProgressChanged: { onProgressChanged: {
console.log("progress " + progress) // console.log("progress " + progress)
uploadProgress.width = parent.width * progress uploadProgress.width = parent.width * progress
} }
onSuccess: { onSuccess: {
uploadProgress.width = 0 uploadProgress.width = 0
console.log(replyData) //console.log(replyData)
mediaModel.append(JSON.parse(replyData)) mediaModel.append(JSON.parse(replyData))
} }
onFailure: { onFailure: {
uploadProgress.width = 0 uploadProgress.width = 0
btnAddImage.enabled = true btnAddImage.enabled = true
console.log(status) //console.log(status)
console.log(statusText) //console.log(statusText)
} }
} }
@ -447,7 +456,7 @@ Page {
var visibility = ["public", "unlisted", "private", "direct"] var visibility = ["public", "unlisted", "private", "direct"]
var media_ids = [] var media_ids = []
for (var k = 0; k < mediaModel.count; k++) { for (var k = 0; k < mediaModel.count; k++) {
console.log(mediaModel.get(k).id) // console.log(mediaModel.get(k).id)
media_ids.push(mediaModel.get(k).id) media_ids.push(mediaModel.get(k).id)
} }
var msg = { var msg = {
@ -511,7 +520,7 @@ Page {
privacy.currentIndex = setIndex privacy.currentIndex = setIndex
} }
console.log(JSON.stringify()) // console.log(JSON.stringify())
worker.sendMessage({ worker.sendMessage({
"action": 'statuses/' + mdl.get(0).status_id + '/context', "action": 'statuses/' + mdl.get(0).status_id + '/context',

View file

@ -7,6 +7,7 @@ import "../lib/API.js" as Logic
Page { Page {
property bool debug: false
// Python connections and signals, callable from QML side // Python connections and signals, callable from QML side
// This is not ideal but keeps the page from erroring out on redirect // This is not ideal but keeps the page from erroring out on redirect
@ -17,13 +18,13 @@ Page {
importModule('server', function () {}); importModule('server', function () {});
setHandler('finished', function(newvalue) { setHandler('finished', function(newvalue) {
console.debug(newvalue) if(debug) console.debug(newvalue)
}); });
startDownload(); startDownload();
} }
function startDownload() { function startDownload() {
call('server.downloader.serve', function() {}); call('server.downloader.serve', function() {});
console.debug("called") if (debug) console.debug("called")
} }
} }
@ -64,10 +65,10 @@ Page {
Logic.api.registerApplication("Tooter", Logic.api.registerApplication("Tooter",
'http://localhost:8000/index.html', // redirect uri, we will need this later on 'http://localhost:8000/index.html', // redirect uri, we will need this later on
["read", "write", "follow"], //scopes ["read", "write", "follow"], //scopes
"http://grave-design.com/harbour-tooter", //website on the login screen "https://github.com/poetaster/harbour-tooter#readme", //website on the login screen
function(data) { function(data) {
console.log(data) if (debug) console.log(data)
var conf = JSON.parse(data) var conf = JSON.parse(data)
conf.instance = instance.text; conf.instance = instance.text;
conf.login = false; conf.login = false;
@ -78,8 +79,8 @@ Page {
conf['mastodon_client_redirect_uri'] = data['mastodon_client_redirect_uri']; conf['mastodon_client_redirect_uri'] = data['mastodon_client_redirect_uri'];
delete Logic.conf;*/ delete Logic.conf;*/
Logic.conf = conf; Logic.conf = conf;
console.log(JSON.stringify(conf)) if(debug) console.log(JSON.stringify(conf))
console.log(JSON.stringify(Logic.conf)) if(debug) console.log(JSON.stringify(Logic.conf))
// we got our application // we got our application
// our user to it! // our user to it!
@ -88,7 +89,7 @@ Page {
"code", // oauth method "code", // oauth method
["read", "write", "follow"] //scopes ["read", "write", "follow"] //scopes
); );
console.log(url) if(debug) console.log(url)
webView.url = url webView.url = url
webView.visible = true webView.visible = true
} }
@ -128,32 +129,27 @@ Page {
} }
onRecvAsyncMessage: { onRecvAsyncMessage: {
console.log('async changed: ' + url) if(debug) console.log('async changed: ' + url)
console.debug(message) if(debug) console.debug(message)
switch (message) { switch (message) {
case "embed:contentOrientationChanged": case "embed:contentOrientationChanged":
break break
case "webview:action": case "webview:action":
if ( data.topic != lon ) {
//webview.runJavaScript("return latlon('" + lat + "','" + lon + "')");
//if (debug) console.debug(data.topic)
//if (debug) console.debug(data.also)
//if (debug) console.debug(data.src)
}
break break
} }
} }
visible: false visible: false
//opacity: 0 //opacity: 0
anchors { anchors.fill: parent
/*{
top: parent.top top: parent.top
left: parent.left left: parent.left
right: parent.right right: parent.right
bottom: parent.bottom bottom: parent.bottom
} }*/
onLoadingChanged: { onLoadingChanged: {
console.log('loading changed: ' + url) if(debug) console.log('loading changed: ' + url)
if ( if (
(url+"").substr(0, 38) === 'http://localhost:8000/index.html?code=' || (url+"").substr(0, 38) === 'http://localhost:8000/index.html?code=' ||
(url+"").substr(0, 39) === 'https://localhost:8000/index.html?code=' (url+"").substr(0, 39) === 'https://localhost:8000/index.html?code='
@ -165,7 +161,7 @@ Page {
var authCode = vars["code"]; var authCode = vars["code"];
console.log(authCode) if(debug) console.log(authCode)
Logic.api.getAccessTokenFromAuthCode( Logic.api.getAccessTokenFromAuthCode(
Logic.conf["client_id"], Logic.conf["client_id"],
@ -174,10 +170,10 @@ Page {
authCode, authCode,
function(data) { function(data) {
// AAAND DATA CONTAINS OUR TOKEN! // AAAND DATA CONTAINS OUR TOKEN!
console.log(data) if(debug) console.log(data)
data = JSON.parse(data) data = JSON.parse(data)
console.log(JSON.stringify(data)) if(debug) console.log(JSON.stringify(data))
console.log(JSON.stringify(data.access_token)) if(debug) console.log(JSON.stringify(data.access_token))
Logic.conf["api_user_token"] = data.access_token Logic.conf["api_user_token"] = data.access_token
Logic.conf["login"] = true; Logic.conf["login"] = true;
Logic.api.setConfig("api_user_token", Logic.conf["api_user_token"]) Logic.api.setConfig("api_user_token", Logic.conf["api_user_token"])

View file

@ -6,7 +6,7 @@ import "./components/"
Page { Page {
id: mainPage id: mainPage
property bool debug: false
property bool isFirstPage: true property bool isFirstPage: true
property bool isTablet: true //Screen.sizeCategory >= Screen.Large property bool isTablet: true //Screen.sizeCategory >= Screen.Large
@ -24,7 +24,8 @@ Page {
id: navigation id: navigation
isPortrait: !mainPage.isPortrait isPortrait: !mainPage.isPortrait
onSlideshowShow: { onSlideshowShow: {
console.log(vIndex) if (debug) console.log(vIndex)
slideshow.positionViewAtIndex(vIndex, ListView.SnapToItem) slideshow.positionViewAtIndex(vIndex, ListView.SnapToItem)
} }
} }
@ -74,6 +75,15 @@ Page {
height: parent.itemHeight height: parent.itemHeight
onOpenDrawer: isPortrait ? infoPanel.open = setDrawer : infoPanel.open = true onOpenDrawer: isPortrait ? infoPanel.open = setDrawer : infoPanel.open = true
} }
MyList {
id: tlBookmarks
title: qsTr("Bookmarks")
type: "bookmarks"
mdl: Logic.modelTLbookmarks
width: isPortrait ? parent.itemWidth : parent.itemWidth - Theme.itemSizeLarge
height: parent.itemHeight
onOpenDrawer: isPortrait ? infoPanel.open = setDrawer : infoPanel.open = true
}
Item { Item {
id: tlSearch id: tlSearch
@ -84,7 +94,7 @@ Page {
width: isPortrait ? parent.itemWidth : parent.itemWidth - Theme.itemSizeLarge width: isPortrait ? parent.itemWidth : parent.itemWidth - Theme.itemSizeLarge
height: parent.itemHeight height: parent.itemHeight
onSearchChanged: { onSearchChanged: {
console.log(search) if (debug) console.log(search)
loader.sourceComponent = loading loader.sourceComponent = loading
if (search.charAt(0) === "@") { if (search.charAt(0) === "@") {
loader.sourceComponent = userListComponent loader.sourceComponent = userListComponent
@ -114,7 +124,7 @@ Page {
EnterKey.onClicked: { EnterKey.onClicked: {
tlSearch.search = text.toLowerCase().trim() tlSearch.search = text.toLowerCase().trim()
focus = false focus = false
console.log(text) if (debug) console.log(text)
} }
} }
} }
@ -148,6 +158,18 @@ Page {
delegate: VisualContainer delegate: VisualContainer
Component.onCompleted: { Component.onCompleted: {
view.type = "timelines/tag/"+tlSearch.search.substring(1) view.type = "timelines/tag/"+tlSearch.search.substring(1)
var ids = []
view.params = []
view.params.push({name: 'limit', data: "20" });
/* we push the ids via params which we remove in the WorkerScript
* see: MyList:loadData() and should move */
if (mdl.count) {
for(var i = 0 ; i < mdl.count ; i++) {
ids.push(mdl.get(i).id)
//if (debug) console.log(model.get(i).id)
}
view.params.push({name: 'ids', data: ids });
}
view.loadData("append") view.loadData("append")
} }
} }
@ -219,8 +241,20 @@ Page {
delegate: VisualContainer delegate: VisualContainer
Component.onCompleted: { Component.onCompleted: {
var ids = []
view3.params = []
view3.params.push({name: 'limit', data: "20" });
/* we push the ids via params which we remove in the WorkerScript
* see: MyList:loadData() and should move */
if (mdl.count) {
for(var i = 0 ; i < mdl.count ; i++) {
ids.push(mdl.get(i).id)
//if (debug) console.log(model.get(i).id)
}
view3.params.push({name: 'ids', data: ids });
}
view3.type = "timelines/tag/"+tlSearch.search view3.type = "timelines/tag/"+tlSearch.search
view3.loadData("append") view3.loadData("prepend")
} }
} }
} }
@ -270,17 +304,21 @@ Page {
function onLinkActivated(href) { function onLinkActivated(href) {
var test = href.split("/") var test = href.split("/")
debug = true
if (debug) {
console.log(href) console.log(href)
console.log(JSON.stringify(test)) console.log(JSON.stringify(test))
console.log(JSON.stringify(test.length)) console.log(JSON.stringify(test.length))
}
if (test.length === 5 && (test[3] === "tags" || test[3] === "tag") ) { if (test.length === 5 && (test[3] === "tags" || test[3] === "tag") ) {
tlSearch.search = "#"+decodeURIComponent(test[4]) tlSearch.search = "#"+decodeURIComponent(test[4])
slideshow.positionViewAtIndex(4, ListView.SnapToItem) slideshow.positionViewAtIndex(5, ListView.SnapToItem)
navigation.navigateTo('search') navigation.navigateTo('search')
if (debug) console.log("search tag")
} else if (test.length === 4 && test[3][0] === "@" ) { } else if (test.length === 4 && test[3][0] === "@" ) {
tlSearch.search = decodeURIComponent("@"+test[3].substring(1)+"@"+test[2]) tlSearch.search = decodeURIComponent("@"+test[3].substring(1)+"@"+test[2])
slideshow.positionViewAtIndex(4, ListView.SnapToItem) slideshow.positionViewAtIndex(5, ListView.SnapToItem)
navigation.navigateTo('search') navigation.navigateTo('search')
} else { } else {
@ -289,6 +327,6 @@ Page {
} }
Component.onCompleted: { Component.onCompleted: {
console.log("aaa") //console.log("aaa")
} }
} }

View file

@ -29,12 +29,13 @@ Page {
property bool muting: false property bool muting: false
property bool domain_blocking: false property bool domain_blocking: false
property date created_at property date created_at
property bool debug: false
WorkerScript { WorkerScript {
id: worker id: worker
source: "../lib/Worker.js" source: "../lib/Worker.js"
onMessage: { onMessage: {
console.log(JSON.stringify(messageObject)) if (debug) console.log(JSON.stringify(messageObject))
if(messageObject.action.indexOf("accounts/search") > -1 ){ if(messageObject.action.indexOf("accounts/search") > -1 ){
user_id = messageObject.data.id user_id = messageObject.data.id
followers_count = messageObject.data.followers_count followers_count = messageObject.data.followers_count
@ -54,7 +55,7 @@ Page {
} }
if(messageObject.action === "accounts/relationships/"){ if(messageObject.action === "accounts/relationships/"){
console.log(JSON.stringify(messageObject)) if (debug) console.log(JSON.stringify(messageObject))
following = messageObject.data.following following = messageObject.data.following
requested = messageObject.data.requested requested = messageObject.data.requested
followed_by = messageObject.data.followed_by followed_by = messageObject.data.followed_by
@ -190,9 +191,9 @@ Page {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
onLinkActivated: { onLinkActivated: {
var test = link.split("/") var test = link.split("/")
console.log(link) if (debug) console.log(link)
console.log(JSON.stringify(test)) if (debug) console.log(JSON.stringify(test))
console.log(JSON.stringify(test.length)) if (debug) console.log(JSON.stringify(test.length))
if (test.length === 5 && (test[3] === "tags" || test[3] === "tag") ) { if (test.length === 5 && (test[3] === "tags" || test[3] === "tag") ) {
pageStack.pop(pageStack.find(function(page) { pageStack.pop(pageStack.find(function(page) {
var check = page.isFirstPage === true; var check = page.isFirstPage === true;

View file

@ -153,6 +153,12 @@ Page {
mastodon: "molan@fosstodon.org" mastodon: "molan@fosstodon.org"
mail: "" mail: ""
} }
ListElement {
name: "poetaster"
desc: qsTr("Development")
mastodon: "postaster@mastodon.gamedev.place"
mail: "blueprint@poetaster.de"
}
ListElement { ListElement {
name: "Miodrag Nikolić" name: "Miodrag Nikolić"

View file

@ -10,14 +10,18 @@ Item {
property double wRatio : 16/9 property double wRatio : 16/9
property double hRatio : 9/16 property double hRatio : 9/16
property bool debug: false
width: width width: width
height: height height: height
Component.onCompleted: { Component.onCompleted: {
if(debug) console.log("MB: " + JSON.stringify(model.get(0)))
if (model && model.count && model.get(0).type === "video") { if (model && model.count && model.get(0).type === "video") {
//console.log("Mediablock")
//console.log(JSON.stringify(model.get(0).type))
while (model.count>1) { while (model.count>1) {
model.remove(model.count-1) model.remove(model.count-1)
} }
//console.log(JSON.stringify(model.get(0)))
} }
var count = 0 var count = 0
if (model && model.count) if (model && model.count)
@ -89,6 +93,8 @@ Item {
type = model.get(0).type type = model.get(0).type
previewURL = model.get(0).preview_url previewURL = model.get(0).preview_url
mediaURL = model.get(0).url mediaURL = model.get(0).url
url = model.get(0).url
if(debug) console.log( model.get(0).url )
height = Theme.itemSizeLarge height = Theme.itemSizeLarge
return true return true
} else { } else {
@ -108,6 +114,8 @@ Item {
type = model.get(1).type type = model.get(1).type
previewURL = model.get(1).preview_url previewURL = model.get(1).preview_url
mediaURL = model.get(1).url mediaURL = model.get(1).url
url = model.get(0).url
if(debug) console.log( model.get(1).url )
height = Theme.itemSizeLarge height = Theme.itemSizeLarge
return true return true
} else { } else {
@ -127,6 +135,7 @@ Item {
type = model.get(2).type type = model.get(2).type
previewURL = model.get(2).preview_url previewURL = model.get(2).preview_url
mediaURL = model.get(2).url mediaURL = model.get(2).url
url = model.get(0).url
height = Theme.itemSizeLarge height = Theme.itemSizeLarge
return true return true
} else { } else {
@ -146,6 +155,7 @@ Item {
type = model.get(3).type type = model.get(3).type
previewURL = model.get(3).preview_url previewURL = model.get(3).preview_url
mediaURL = model.get(3).url mediaURL = model.get(3).url
url = model.get(0).url
height = Theme.itemSizeLarge height = Theme.itemSizeLarge
return true return true
} else { } else {

View file

@ -9,15 +9,26 @@ FullscreenContentPage {
property string type: "" property string type: ""
property string previewURL: "" property string previewURL: ""
property string mediaURL: "" property string mediaURL: ""
property string url: ""
property bool debug: false
allowedOrientations: Orientation.All allowedOrientations: Orientation.All
Component.onCompleted: function() { Component.onCompleted: function() {
if (debug) {
console.log(type) console.log(type)
console.log(previewURL) console.log(previewURL)
console.log(mediaURL) console.log(mediaURL)
}
if (type != 'gifv' && type != 'video') { if (type != 'gifv' && type != 'video') {
imagePreview.source = mediaURL imagePreview.source = mediaURL
imageFlickable.visible = true imageFlickable.visible = true
} else if( type == 'audio'){
video.source = url
videoFlickable.visible = true
playerIcon.visible = true
playerProgress.visible = true
video.play()
hideTimer.start()
} else { } else {
video.source = mediaURL video.source = mediaURL
video.fillMode = VideoOutput.PreserveAspectFit video.fillMode = VideoOutput.PreserveAspectFit
@ -50,18 +61,18 @@ FullscreenContentPage {
videoError.visible = true videoError.visible = true
} }
onStatusChanged: { onStatusChanged: {
console.log(status) if(debug) console.log(status)
switch (status) { switch (status) {
case MediaPlayer.Loading: case MediaPlayer.Loading:
console.log("loading") if(debug) console.log("loading")
return; return;
case MediaPlayer.EndOfMedia: case MediaPlayer.EndOfMedia:
console.log("EndOfMedia") if (debug) console.log("EndOfMedia")
return; return;
} }
} }
onPlaybackStateChanged: { onPlaybackStateChanged: {
console.log(playbackState) if (debug) console.log(playbackState)
switch (playbackState) { switch (playbackState) {
case MediaPlayer.PlayingState: case MediaPlayer.PlayingState:
playerIcon.icon.source = "image://theme/icon-m-pause" playerIcon.icon.source = "image://theme/icon-m-pause"

View file

@ -0,0 +1,323 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
import QtMultimedia 5.6
ListItem {
id: item
property string url
property string mediaUrl
property string mimeType: 'audio/mp3'
property int length
property bool _isAudio: mimeType.substring(0, 6) === "audio/"
property bool _isImage: mimeType.substring(0, 6) === "image/"
function _toTime(s)
{
if (s < 0)
{
return "-";
}
s /= 1000;
var seconds = Math.floor(s) % 60;
s /= 60;
var minutes = Math.floor(s) % 60;
s /= 60;
var hours = Math.floor(s);
if (seconds < 10)
{
seconds = "0" + seconds;
}
if (minutes < 10)
{
minutes = "0" + minutes;
}
if (hours > 0)
{
return hours + ":" + minutes + ":" + seconds;
}
else
{
return minutes + ":" + seconds;
}
}
/* Returns the filename of the given URL.
*/
function _urlFilename(url) {
var idx = url.lastIndexOf("=");
if (idx !== -1) {
return url.substring(idx + 1);
}
idx = url.lastIndexOf("/");
if (idx === url.length - 1) {
idx = url.substring(0, idx).lastIndexOf("/");
}
if (idx !== -1) {
return url.substring(idx + 1);
}
return url;
}
/* Returns the icon source for the given media.
*/
function _mediaIcon(url, type) {
if (type.substring(0, 6) === "image/") {
return url;
} else if (type.substring(0, 6) === "video/") {
return "image://theme/icon-l-play";
} else {
return "image://theme/icon-m-other";
}
}
/* Returns a user-friendly media type name for the given MIME type.
*/
function _mediaTypeName(type) {
if (type.substring(0, 6) === "image/") {
return qsTr("Image");
} else if (type.substring(0, 6) === "video/") {
return qsTr("Video");
} else if (type === "application/pdf") {
return qsTr("PDF document");
} else {
return type;
}
}
onClicked: {
console.log('MediaItem')
console.log(url)
console.log(mediaUrl)
if (_isAudio)
{
if (audioProxy.playing)
{
audioProxy.pause();
}
else
{
audioProxy.play();
}
}
else if (_isImage)
{
var props = {
"url": item.url,
"name": _urlFilename(item.url)
}
pageStack.push(Qt.resolvedUrl("ImagePage.qml"), props);
}
else
{
Qt.openUrlExternally(item.url);
}
}
QtObject {
id: audioProxy
property bool _active: audioPlayer.source == source
property bool playing: _active ? audioPlayer.playing
: false
property bool paused: _active ? audioPlayer.paused
: false
property real duration: _active ? audioPlayer.duration
: -1
property real position: _active ? audioPlayer.position
: 0
property string source: _isAudio ? item.url : ""
property Timer _seeker: Timer {
interval: 50
onTriggered: {
if (audioProxy._active)
{
if (! audioPlayer.playing)
{
console.log("Stream is not ready. Deferring seek operation.")
_seeker.start();
}
else
{
audioPlayer.seek(Math.max(0, database.audioBookmark(audioProxy.source) - 3000));
}
}
}
}
function play()
{
if (_active)
{
audioPlayer.play();
}
else
{
// save bookmark before switching to another podcast
if (audioPlayer.playing)
{
database.setAudioBookmark(audioPlayer.source,
audioPlayer.position);
}
audioPlayer.stop();
audioPlayer.source = source;
audioPlayer.play();
_seeker.start();
}
}
function pause()
{
if (_active)
{
//database.setAudioBookmark(source, audioPlayer.position);
audioPlayer.pause();
}
}
function seek(value)
{
if (_active) audioPlayer.seek(value);
}
onPositionChanged: {
if (_active)
{
if (! slider.down)
{
slider.value = position;
}
}
}
onDurationChanged: {
if (_active)
{
slider.maximumValue = duration;
}
}
}
Audio {
id: audioPlayer
property bool playing: playbackState === Audio.PlayingState
property bool paused: playbackState === Audio.PausedState
autoLoad: false
autoPlay: false
}
Image {
id: mediaIcon
anchors.left: parent.left
anchors.leftMargin: Theme.paddingLarge
anchors.rightMargin: Theme.paddingMedium
width: height
height: parent.height
asynchronous: true
smooth: true
fillMode: Image.PreserveAspectCrop
sourceSize.width: width * 2
sourceSize.height: height * 2
source: ! _isAudio ? _mediaIcon(item.url, item.mimeType)
: audioProxy.playing ? "image://theme/icon-l-pause"
: "image://theme/icon-l-play"
clip: true
BusyIndicator {
running: parent.status === Image.Loading
anchors.centerIn: parent
size: BusyIndicatorSize.Medium
}
}
Label {
id: mediaNameLabel
anchors.left: mediaIcon.right
anchors.right: parent.right
anchors.leftMargin: Theme.paddingLarge
anchors.rightMargin: Theme.paddingLarge
truncationMode: TruncationMode.Fade
font.pixelSize: Theme.fontSizeSmall
color: Theme.primaryColor
text: _urlFilename(item.url)
}
Label {
id: label1
anchors.top: mediaNameLabel.bottom
anchors.left: mediaNameLabel.left
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.secondaryColor
text: ! slider.visible ? _mediaTypeName(item.mimeType)
: audioProxy.playing ? _toTime(slider.sliderValue)
: _toTime(database.audioBookmark(audioProxy.source))
}
Label {
id: label2
anchors.top: mediaNameLabel.bottom
anchors.right: parent.right
anchors.rightMargin: Theme.paddingLarge
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.secondaryColor
text: slider.visible ? _toTime(audioProxy.duration)
: item.length >= 0 ? Format.formatFileSize(item.length)
: ""
}
Slider {
id: slider
visible: _isAudio
enabled: audioProxy.playing || audioProxy.paused
anchors.left: label1.right
anchors.right: label2.left
anchors.verticalCenter: label1.verticalCenter
leftMargin: Theme.paddingSmall
rightMargin: Theme.paddingSmall
height: Theme.itemSizeSmall / 3
handleVisible: false
minimumValue: 0
onDownChanged: {
if (! down)
{
audioProxy.seek(sliderValue);
if (! audioProxy.playing)
{
audioProxy.play();
}
}
}
}//Slider
IconButton {
id: mediaDlBtn
icon.source: "image://theme/icon-m-cloud-download"
anchors {
right: parent.right
rightMargin: Theme.horizontalPageMargin
bottom: parent.bottom
bottomMargin: Theme.horizontalPageMargin
}
onClicked: {
var filename = url.split("/")
FileDownloader.downloadFile(url, filename[filename.length-1])
}
}
}

View file

@ -7,6 +7,7 @@ import "."
SilicaListView { SilicaListView {
id: myList id: myList
property bool debug: false
property string type property string type
property string title property string title
property string description property string description
@ -17,15 +18,21 @@ SilicaListView {
property bool loadStarted: false property bool loadStarted: false
property int scrollOffset property int scrollOffset
property string action: "" property string action: ""
// should consider better names or
// using min_ & max_id
property string linkprev: ""
property string linknext: ""
property variant vars property variant vars
property variant conf property variant conf
property bool notifier: false property bool notifier: false
property bool deduping: false
property variant uniqueIds: []
model: mdl model: mdl
signal notify (string what, int num) signal notify (string what, int num)
onNotify: { onNotify: {
console.log(what + " - " + num) if(debug) console.log(what + " - " + num)
} }
signal openDrawer (bool setDrawer) signal openDrawer (bool setDrawer)
onOpenDrawer: { onOpenDrawer: {
@ -33,7 +40,7 @@ SilicaListView {
} }
signal send (string notice) signal send (string notice)
onSend: { onSend: {
console.log("LIST send signal emitted with notice: " + notice) if (debug) console.log("LIST send signal emitted with notice: " + notice)
} }
header: PageHeader { header: PageHeader {
@ -75,7 +82,6 @@ SilicaListView {
pageStack.push(Qt.resolvedUrl("../SettingsPage.qml"), {}) pageStack.push(Qt.resolvedUrl("../SettingsPage.qml"), {})
} }
} }
MenuItem { MenuItem {
text: qsTr("New Toot") text: qsTr("New Toot")
visible: !profilePage visible: !profilePage
@ -115,7 +121,10 @@ SilicaListView {
} }
onCountChanged: { onCountChanged: {
loadStarted = false if (debug) console.log("count changed on: " + title)
//deDouble()
//loadStarted = false
/*contentY = scrollOffset /*contentY = scrollOffset
console.log("CountChanged!")*/ console.log("CountChanged!")*/
} }
@ -130,7 +139,7 @@ SilicaListView {
anchors.bottomMargin: Theme.paddingLarge anchors.bottomMargin: Theme.paddingLarge
visible: false visible: false
onClicked: { onClicked: {
loadData("append") if (!loadStarted && !deduping) loadData("append")
} }
} }
@ -150,9 +159,9 @@ SilicaListView {
openDrawer(contentY - scrollOffset > 0 ? false : true ) openDrawer(contentY - scrollOffset > 0 ? false : true )
scrollOffset = contentY scrollOffset = contentY
} }
if(contentY+height > footerItem.y && !loadStarted && autoLoadMore) { if(contentY+height > footerItem.y && !deduping && !loadStarted && autoLoadMore) {
loadData("append")
loadStarted = true loadStarted = true
loadData("append")
} }
} }
@ -163,37 +172,192 @@ SilicaListView {
source: "../../lib/Worker.js" source: "../../lib/Worker.js"
onMessage: { onMessage: {
if (messageObject.error){ if (messageObject.error){
console.log(JSON.stringify(messageObject)) if (debug) console.log(JSON.stringify(messageObject))
} else {
if (debug) console.log(JSON.stringify(messageObject))
// loadStarted = false
} }
if (messageObject.fireNotification && notifier){ if (messageObject.fireNotification && notifier){
Logic.notifier(messageObject.data) Logic.notifier(messageObject.data)
} }
// temporary debugging measure
if (messageObject.updatedAll){
if (debug) console.log("Got em all.")
if (model.count > 20) deDouble()
loadStarted = false
}
// the api is stupid
if (messageObject.LinkHeader) {
// <https://mastodon.gamedev.place/api/v1/bookmarks?max_id=11041>; rel=\"next\",
// <https://mastodon.gamedev.place/api/v1/bookmarks?min_id=14158>; rel=\"prev\""
var matches = /max_id=([0-9]+)/.exec(messageObject.LinkHeader);
var maxlink = matches[0].split("=")[1];
var matches = /min_id=([0-9]+)/.exec(messageObject.LinkHeader);
var minlink = matches[0].split("=")[1];
if (debug) console.log("maxlink: " + maxlink)
if (debug) console.log("minlink: " + minlink)
linkprev = maxlink
linknext = minlink
}
} }
} }
Component.onCompleted: { Component.onCompleted: {
loadData("prepend") loadData("prepend")
if (debug) console.log("MyList completed: " + title)
} }
Timer { Timer {
triggeredOnStart: false; interval: 5*60*1000; running: true; repeat: true triggeredOnStart: false;
interval: {
/*
* Varied calls so that server isn't hit
* simultaenously ... this is hamfisted
*/
var listInterval = Math.floor(Math.random() * 60)*10*1000
if( title === "Home" ) listInterval = 20*60*1000
if( title === "Local" ) listInterval = 10*60*1000
if( title === "Federated" ) listInterval = 30*60*1000
if( title === "Bookmarks" ) listInterval = 40*60*1000
if( title === "Notifications" ) listInterval = 12*60*1000
if(debug) console.log(title + ' interval: ' + listInterval)
return listInterval
}
running: true;
repeat: true
onTriggered: { onTriggered: {
console.log(title + ' ' +Date().toString()) if(debug) console.log(title + ' ' + Date().toString())
loadData("prepend") // let's avoid pre and appending at the same time!
if ( ! loadStarted && ! deduping ) loadData("prepend")
} }
} }
/*
* NOT actually doing deduping :)
* utility called on updates to model to remove remove Duplicates:
* the dupes are probably a result of improper syncing of the models
* this is temporary and can probaly be removed because of the
* loadData method passing in to the WorkerScript
*/
function deDouble(){
deduping = true
var ids = []
var uniqueItems = []
var i
var j
var seenIt = 0
if (debug) console.log(model.count)
for(i = 0 ; i < model.count ; i++) {
ids.push(model.get(i).id)
uniqueItems = removeDuplicates(ids)
}
//if (debug) console.log(ids)
if (debug) console.log(uniqueItems.length)
if (debug) console.log( "max-one?:" + model.get(model.count - 2).id )
if (debug) console.log( "max:" + model.get(model.count - 1).id )
if ( uniqueItems.length < model.count) {
// it seems that only the last one, is an issue
/*if (model.get(model.count - 1).id > model.get(model.count - 2).id){
model.remove(model.count - 1,1)
}*/
if (debug) console.log(model.count)
for(j = 0; j <= uniqueItems.length - 1 ; j++) {
seenIt = 0
for(i = 0 ; i < model.count - 1 ; i++) {
if (model.get(i).id === uniqueItems[j]){
seenIt = seenIt+1
if (seenIt > 1) {
if (debug) console.log(uniqueItems[j] + " - " + seenIt)
// model.remove(i,1) // (model.get(i))
seenIt = seenIt-1
}
}
}
}
}
deduping = false
}
/* utility function because this version of qt doesn't support modern javascript
*
*/
function removeDuplicates(arr) {
var unique = [];
for(var i=0; i < arr.length; i++){
if(unique.indexOf(arr[i]) === -1) {
unique.push(arr[i]);
}
}
return unique;
}
/* Principle load function, uses websocket's worker.js
*
*/
function loadData(mode) { function loadData(mode) {
if (debug) console.log('loadData called: ' + mode + " in " + title)
// since the worker adds Duplicates
// we pass in current ids in the model
// and skip those on insert append in the worker
for(var i = 0 ; i < model.count ; i++) {
uniqueIds.push(model.get(i).id)
//if (debug) console.log(model.get(i).id)
}
uniqueIds = removeDuplicates(uniqueIds)
var p = [] var p = []
if (params.length) { if (params.length) {
for(var i = 0; i<params.length; i++) for(var i = 0; i<params.length; i++)
p.push(params[i]) p.push(params[i])
} }
/*
* for some types, min_id, max_id
* is obtained from link header
*/
if (mode === "append" && model.count) { if (mode === "append" && model.count) {
if ( linkprev === "" ) {
p.push({name: 'max_id', data: model.get(model.count-1).id}) p.push({name: 'max_id', data: model.get(model.count-1).id})
} else {
p.push({name: 'max_id', data: linkprev})
}
} }
if (mode === "prepend" && model.count) { if (mode === "prepend" && model.count) {
if ( linknext === "" ) {
p.push({name:'since_id', data: model.get(0).id}) p.push({name:'since_id', data: model.get(0).id})
} else {
p.push({name: 'min_id', data: linknext})
}
}
//if (debug) console.log(JSON.stringify(uniqueIds))
if(title === "Local") {
type = "timelines/public"
p.push({name:'local', data: "true"})
}
// we push the ids via params which we remove in the WorkerScript
if (model.count) {
p.push({name:'ids', data: uniqueIds})
} }
var msg = { var msg = {
@ -204,8 +368,9 @@ SilicaListView {
'conf' : Logic.conf 'conf' : Logic.conf
} }
console.log(JSON.stringify(msg)) //if (debug) console.log(JSON.stringify(msg))
if (type !== "") if (type !== "")
worker.sendMessage(msg) worker.sendMessage(msg)
} }
} }

View file

@ -1,6 +1,6 @@
import QtQuick 2.0 import QtQuick 2.0
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import QtMultimedia 5.0 import QtMultimedia 5.6
Item { Item {
@ -9,6 +9,7 @@ Item {
property string type : "" property string type : ""
property string previewURL: "" property string previewURL: ""
property string mediaURL: "" property string mediaURL: ""
property string url: ""
Rectangle { Rectangle {
opacity: 0.4 opacity: 0.4
@ -32,12 +33,37 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
} }
Image { /*Text {
anchors{
topMargin: 10
}
id: audioUrl
visible: type == 'audio' visible: type == 'audio'
//opacity: img.status === Image.Ready ? 0.0 : 1.0 text: "<a href='" + url + "'>" + 'Audio file' + '</a>'
font.pixelSize: Theme.fontSizeLarge
}*/
MediaItem {
id: audioContent
visible: type == 'audio'
opacity: img.status === Image.Ready ? 0.0 : 1.0
Behavior on opacity { FadeAnimator {} } Behavior on opacity { FadeAnimator {} }
source: "image://theme/icon-m-file-audio?" mimeType: 'audio/mp3'
url: mediaURL
mediaUrl: mediaURL
//source: "image://theme/icon-m-file-audio?"
anchors.centerIn: parent anchors.centerIn: parent
/*MouseArea {
anchors.fill: parent
onClicked: {
pageStack.push(Qt.resolvedUrl("./MediaItem.qml"), {
"url": url,
"type": type,
"mimeType": type
})
}
} */
} }
Rectangle { Rectangle {
@ -67,6 +93,7 @@ Item {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
visible: type != 'audio'
onClicked: { onClicked: {
pageStack.push(Qt.resolvedUrl("./MediaFullScreen.qml"), { pageStack.push(Qt.resolvedUrl("./MediaFullScreen.qml"), {
"previewURL": previewURL, "previewURL": previewURL,
@ -109,5 +136,19 @@ Item {
onClicked: parent.visible = false onClicked: parent.visible = false
} }
} }
/*IconButton {
id: mediaDlBtn
icon.source: "image://theme/icon-m-cloud-download"
anchors {
right: parent.right
rightMargin: Theme.horizontalPageMargin
bottom: parent.bottom
bottomMargin: Theme.horizontalPageMargin
}
onClicked: {
var filename = url.split("/")
FileDownloader.downloadFile(url, filename[filename.length-1])
}
}*/
} }
} }

View file

@ -38,7 +38,6 @@ SilicaGridView {
active: false active: false
unread: false unread: false
} }
ListElement { ListElement {
icon: "image://theme/icon-m-website?" icon: "image://theme/icon-m-website?"
slug: "federated" slug: "federated"
@ -47,6 +46,14 @@ SilicaGridView {
unread: false unread: false
} }
ListElement {
icon: "../../images/icon-s-bookmark.svg?"
//icon: "image://theme/icon-s-bookmark"
slug: "bookmarks"
name: "Bookmarks"
active: false
unread: false
}
ListElement { ListElement {
icon: "image://theme/icon-m-search?" icon: "image://theme/icon-m-search?"
slug: "search" slug: "search"

View file

@ -6,6 +6,8 @@ import "../../lib/API.js" as Logic
BackgroundItem { BackgroundItem {
id: delegate id: delegate
property bool debug:false
signal send (string notice) signal send (string notice)
signal navigateTo(string link) signal navigateTo(string link)
@ -195,9 +197,11 @@ BackgroundItem {
} }
onLinkActivated: { onLinkActivated: {
var test = link.split("/") var test = link.split("/")
if (debug) {
console.log(link) console.log(link)
console.log(JSON.stringify(test)) console.log(JSON.stringify(test))
console.log(JSON.stringify(test.length)) console.log(JSON.stringify(test.length))
}
if (test.length === 5 && (test[3] === "tags" || test[3] === "tag") ) { if (test.length === 5 && (test[3] === "tags" || test[3] === "tag") ) {
pageStack.pop(pageStack.find(function(page) { pageStack.pop(pageStack.find(function(page) {
var check = page.isFirstPage === true; var check = page.isFirstPage === true;
@ -447,11 +451,11 @@ BackgroundItem {
} }
onPressAndHold: { onPressAndHold: {
console.log(JSON.stringify(mdl.get(index))) if (debug) console.log(JSON.stringify(mdl.get(index)))
mnu.open(delegate) mnu.open(delegate)
} }
onDoubleClicked: { onDoubleClicked: {
console.log("double click") if (debug) console.log("double click")
} }
} }

View file

@ -1,3 +1,48 @@
* Thu Jan 5 2023 Mark Washeim <blueprint@poetaster.de> 1.1.3
- Add changes from gitlogs (++) Bump for tag release.
- Add conditional append all for search when no knownIds
- Add correction to linkprev for bookmarks (and follows, etc)
* Wed Jan 4 2023 Mark Washeim <blueprint@poetaster.de> 1.1.2
- Add conditional append all for search when no knownIds Workers.js
- Add linkprev/next for bookmarks (and follows, etc) MyList
- getLink method in Mastodon.js HEAD requests for Link
* Fri Dec 23 2022 Mark Washeim <blueprint@poetaster.de> 1.1.1
- This is an interim release that should not have been released
* Fri Dec 2 2022 Mark Washeim <blueprint@poetaster.de> 1.1.0
- Added some more debug flags to stop spewing the console.
- Added self to credits.
- Added MediaItem display for audio and Integrated MediaItem into MediaBlock/MyMedia elements.
- Add additional; media element for audio.
- Merge pull request #16 from cintema/patch-2
- Merge pull request #15 from cintema/patch-1
- Update harbour-tooterb.spec
- Update LoginPage.qml
- Merge pull request #5 from eson57/patch-2
- Update harbour-tooterb-sv.ts
* Wed Nov 16 2022 Mark Washeim <blueprint@poetaster.de> 1.0.9
- Forgot to remove the yaml from spec. duh.
- Boost release.
- Update translations, partially for new bookmarks view.
- Remove yaml since it's just in the way.
- Added navigation elements and model for bookmarks.
* Mon Nov 14 2022 Mark Washeim <blueprint@poetaster.de> 1.0.8-3
- Had neglected linguist include which is required for building on obs
* Wed Nov 9 2022 Mark Washeim <blueprint@poetaster.de> 1.0.8-2
- Added minimal info to spec/yaml to include a release in chum.
- Merge pull request #100 from poetaster/master
- Merge pull request #99 from juanro49/master
- Fix reblog content view
* Wed Nov 9 2022 Mark Washeim <blueprint@poetaster.de> 1.0.8
- Add python server for callbacks
- Add new WebView to better render callbacks
*Sun Jul 12 2020 molan <mol_an@sunrise.ch> 1.0.7-0 *Sun Jul 12 2020 molan <mol_an@sunrise.ch> 1.0.7-0
- Fix missing / wrong reblog and favourite counts in Retoots (issue #90) - Fix missing / wrong reblog and favourite counts in Retoots (issue #90)
- Added full landscape support - Added full landscape support

View file

@ -13,16 +13,18 @@ Name: harbour-tooterb
%{!?qtc_make:%define qtc_make make} %{!?qtc_make:%define qtc_make make}
%{?qtc_builddir:%define _builddir %qtc_builddir} %{?qtc_builddir:%define _builddir %qtc_builddir}
Summary: Tooter β Summary: Tooter β
Version: 1.0.7 Version: 1.1.3
Release: 0 Release: 1
Group: Qt/Qt Group: Qt/Qt
License: LICENSE License: GPLv3
URL: http://example.org/ URL: https://github.com/poetaster/harbour-tooter#readme
Source0: %{name}-%{version}.tar.bz2 Source0: %{name}-%{version}.tar.bz2
Source100: harbour-tooterb.yaml
Requires: sailfishsilica-qt5 >= 0.10.9 Requires: sailfishsilica-qt5 >= 0.10.9
Requires: nemo-qml-plugin-configuration-qt5 Requires: nemo-qml-plugin-configuration-qt5
Requires: nemo-qml-plugin-notifications-qt5 Requires: nemo-qml-plugin-notifications-qt5
Requires: pyotherside-qml-plugin-python3-qt5
BuildRequires: qt5-qttools-linguist
BuildRequires: pkgconfig(sailfishapp) >= 1.0.2 BuildRequires: pkgconfig(sailfishapp) >= 1.0.2
BuildRequires: pkgconfig(Qt5Core) BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5Qml) BuildRequires: pkgconfig(Qt5Qml)
@ -36,6 +38,19 @@ BuildRequires: desktop-file-utils
%description %description
Tooter Beta is a native client for Mastodon network instances. Tooter Beta is a native client for Mastodon network instances.
%if "%{?vendor}" == "chum"
PackageName: Tooter Beta
Type: desktop-application
Categories:
- Network
PackagerName: Mark Washeim (poetaster)
Custom:
- Repo: https://github.com/molan-git/harbour-tooter
- PackagingRepo: https://github.com/poetaster/harbour-tooter
Icon: https://raw.githubusercontent.com/poetaster/harbour-tooter/master/icons/256x256/harbour-tooterb.png
Url:
- Bugtracker: https://github.com/poetaster/harbour-tooter/issues
%endif
%prep %prep
%setup -q -n %{name}-%{version} %setup -q -n %{name}-%{version}

View file

@ -1,50 +0,0 @@
Name: harbour-tooterb
Summary: Tooter β
Version: 1.0.7
Release: 0
# The contents of the Group field should be one of the groups listed here:
# https://github.com/mer-tools/spectacle/blob/master/data/GROUPS
Group: Qt/Qt
URL: http://example.org/
License: LICENSE
# This must be generated before uploading a package to a remote build service.
# Usually this line does not need to be modified.
Sources:
- '%{name}-%{version}.tar.bz2'
Description: |
Tooter Beta is a native client for Mastodon network instances.
Configure: none
# The qtc5 builder inserts macros to allow QtCreator to have fine
# control over qmake/make execution
Builder: qtc5
# This section specifies build dependencies that are resolved using pkgconfig.
# This is the preferred way of specifying build dependencies for your package.
PkgConfigBR:
- sailfishapp >= 1.0.2
- Qt5Core
- Qt5Qml
- Qt5Quick
- Qt5DBus
- Qt5Multimedia
#- nemonotifications-qt5 nemo-qml-plugin-notifications-qt5
- nemo-qml-plugin-notifications-qt5-devel
- openssl
# Build dependencies without a pkgconfig setup can be listed here
# PkgBR:
# - qt5-qtmultimedia-plugin-mediaservice-gstmediaplayer
# Runtime dependencies which are not automatically detected
Requires:
- sailfishsilica-qt5 >= 0.10.9
# All installed files
Files:
- '%{_bindir}'
- '%{_datadir}/%{name}'
- '%{_datadir}/applications/%{name}.desktop'
- '%{_datadir}/icons/hicolor/*/apps/%{name}.png'
# For more information about yaml and what's supported in Sailfish OS
# build system, please see https://wiki.merproject.org/wiki/Spectacle

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Neuer Toot</translation> <translation>Neuer Toot</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation>Lesezeichen</translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation>Ladefehler</translation> <translation>Ladefehler</translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>um bei den Übersetzungen mitzuhelfen.</translation> <translation>um bei den Übersetzungen mitzuhelfen.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Νέος</translation> <translation>Νέος</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>για να βοηθήσετε την μετάφραση της εφαρμογής στην γλώσσα σας.</translation> <translation>για να βοηθήσετε την μετάφραση της εφαρμογής στην γλώσσα σας.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Nuevo toot</translation> <translation>Nuevo toot</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation>Marcador</translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation>Error al cargar</translation> <translation>Error al cargar</translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>para ayudar con traducciones.</translation> <translation>para ayudar con traducciones.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Nouveau pouet</translation> <translation>Nouveau pouet</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation>Marque-page</translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation>Erreur au chargement</translation> <translation>Erreur au chargement</translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>pour aider à traduire cette application.</translation> <translation>pour aider à traduire cette application.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Nuovo Toot</translation> <translation>Nuovo Toot</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation>Segnalibro</translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation>Errore durante caricamento</translation> <translation>Errore durante caricamento</translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>per aiutare nella traduzione dell&apos;app.</translation> <translation>per aiutare nella traduzione dell&apos;app.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Nieuwe Toot</translation> <translation>Nieuwe Toot</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation>Bookmarken</translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation>Laadfout</translation> <translation>Laadfout</translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>om deze app te helpen vertalen in jouw taal.</translation> <translation>om deze app te helpen vertalen in jouw taal.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Nieuwen toot</translation> <translation>Nieuwen toot</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>voor te helpen met dezen app in uw taal te vertalen.</translation> <translation>voor te helpen met dezen app in uw taal te vertalen.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation type="unfinished">Nòu Tut</translation> <translation type="unfinished">Nòu Tut</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>per ajudar a traduire laplicacion dins vòstra lenga.</translation> <translation>per ajudar a traduire laplicacion dins vòstra lenga.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Nowy wpis</translation> <translation>Nowy wpis</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Новый</translation> <translation>Новый</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation>Ошибка при загрузке</translation> <translation>Ошибка при загрузке</translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>чтобы помочь с переводом приложения на ваш язык.</translation> <translation>чтобы помочь с переводом приложения на ваш язык.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Novi toot</translation> <translation>Novi toot</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>и помозите у преводу апликације на други језик.</translation> <translation>и помозите у преводу апликације на други језик.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -25,50 +25,50 @@
<message> <message>
<source>Copy Link to Clipboard</source> <source>Copy Link to Clipboard</source>
<extracomment>Use the translation of &quot;Copy Link&quot; for a shorter PullDownMenu label</extracomment> <extracomment>Use the translation of &quot;Copy Link&quot; for a shorter PullDownMenu label</extracomment>
<translation type="unfinished"></translation> <translation>Kopiera länk till urklipp</translation>
</message> </message>
<message> <message>
<source>Write your warning here</source> <source>Write your warning here</source>
<extracomment>placeholderText in Toot content warning panel</extracomment> <extracomment>placeholderText in Toot content warning panel</extracomment>
<translation type="unfinished"></translation> <translation>Skriv din varningstext här</translation>
</message> </message>
<message> <message>
<source>What&apos;s on your mind?</source> <source>What&apos;s on your mind?</source>
<extracomment>placeholderText in Toot text panel</extracomment> <extracomment>placeholderText in Toot text panel</extracomment>
<translation type="unfinished"></translation> <translation>Vad tänker du ?</translation>
</message> </message>
<message> <message>
<source>Public</source> <source>Public</source>
<translation type="unfinished"></translation> <translation>Allmän</translation>
</message> </message>
<message> <message>
<source>Unlisted</source> <source>Unlisted</source>
<translation type="unfinished"></translation> <translation>Olistad</translation>
</message> </message>
<message> <message>
<source>Followers-only</source> <source>Followers-only</source>
<translation type="unfinished"></translation> <translation>Endast följare</translation>
</message> </message>
<message> <message>
<source>Direct</source> <source>Direct</source>
<translation type="unfinished"></translation> <translation>Direkt</translation>
</message> </message>
<message> <message>
<source>Toot sent!</source> <source>Toot sent!</source>
<translation type="unfinished"></translation> <translation>Toot skickad!</translation>
</message> </message>
<message> <message>
<source>Reply</source> <source>Reply</source>
<extracomment>&quot;Reply&quot; will show the Toot text entry Panel. &quot;Hide Reply&quot; closes it. Alternative: Use &quot;Close Reply&quot;</extracomment> <extracomment>&quot;Reply&quot; will show the Toot text entry Panel. &quot;Hide Reply&quot; closes it. Alternative: Use &quot;Close Reply&quot;</extracomment>
<translation type="unfinished"></translation> <translation>Svara</translation>
</message> </message>
<message> <message>
<source>Hide Reply</source> <source>Hide Reply</source>
<translation type="unfinished"></translation> <translation>Dölj svar</translation>
</message> </message>
<message> <message>
<source>Open in Browser</source> <source>Open in Browser</source>
<translation type="unfinished"></translation> <translation>Öppna i webbläsaren</translation>
</message> </message>
</context> </context>
<context> <context>
@ -104,19 +104,19 @@
</message> </message>
<message> <message>
<source>Instance</source> <source>Instance</source>
<translation type="unfinished"></translation> <translation>Instans</translation>
</message> </message>
<message> <message>
<source>Enter a valid Mastodon instance URL</source> <source>Enter a valid Mastodon instance URL</source>
<translation>Fyll i URL till Mastodoninstans</translation> <translation>Ange en giltig URL till Mastodoninstans</translation>
</message> </message>
<message> <message>
<source>Mastodon is a free, open-source social network. A decentralized alternative to commercial platforms, it avoids the risks of a single company monopolizing your communication. Pick a server that you trust whichever you choose, you can interact with everyone else. Anyone can run their own Mastodon instance and participate in the social network seamlessly.</source> <source>Mastodon is a free, open-source social network. A decentralized alternative to commercial platforms, it avoids the risks of a single company monopolizing your communication. Pick a server that you trust whichever you choose, you can interact with everyone else. Anyone can run their own Mastodon instance and participate in the social network seamlessly.</source>
<translation>Mastodon är ett fritt och öppet socialt nätverk byggt öppen källkod. Ett decentraliserat alternativ till kommersiella plattformar, vilket undviker att ett ensamt företag monopoliserar din kommunikation. Välj en server du litar --- beroende vilken du väljer, kan du interagera med alla andra. Vem som helst kan köra deras egen Mastodoninstans och delta i nätverket. Även du!</translation> <translation>Mastodon är ett fritt socialt nätverk byggt öppen källkod. Ett decentraliserat alternativ till kommersiella plattformar. Det undviker riskerna med att ett enda företag monopoliserar din kommunikation. Välj en server som du litar , oavsett vad du väljer kan du interagera med alla andra. Vem som helst kan köra sin egen Mastodon-instans och delta i det sociala nätverket sömlöst.</translation>
</message> </message>
<message> <message>
<source>Reload</source> <source>Reload</source>
<translation type="unfinished">Ladda mer</translation> <translation>Läs in igen</translation>
</message> </message>
</context> </context>
<context> <context>
@ -149,12 +149,31 @@
<source>New Toot</source> <source>New Toot</source>
<translation>Ny toot</translation> <translation>Ny toot</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation>Bokmärken</translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
<message> <message>
<source>Error loading</source> <source>Error loading</source>
<translation type="unfinished"></translation> <translation>Fel vid inläsning</translation>
</message>
</context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation>Bild</translation>
</message>
<message>
<source>Video</source>
<translation>Video</translation>
</message>
<message>
<source>PDF document</source>
<translation>PDF-dokument</translation>
</message> </message>
</context> </context>
<context> <context>
@ -184,15 +203,15 @@
</message> </message>
<message> <message>
<source>Reload</source> <source>Reload</source>
<translation>Ladda mer</translation> <translation>Läs in igen</translation>
</message> </message>
<message> <message>
<source>Open in Browser</source> <source>Open in Browser</source>
<translation type="unfinished"></translation> <translation>Öppna i webbläsaren</translation>
</message> </message>
<message> <message>
<source>Nothing found</source> <source>Nothing found</source>
<translation type="unfinished"></translation> <translation>Inget hittades</translation>
</message> </message>
</context> </context>
<context> <context>
@ -203,11 +222,11 @@
</message> </message>
<message> <message>
<source>Follows you</source> <source>Follows you</source>
<translation type="unfinished"></translation> <translation>Följer dig</translation>
</message> </message>
<message> <message>
<source>Group</source> <source>Group</source>
<translation type="unfinished"></translation> <translation>Grupp</translation>
</message> </message>
</context> </context>
<context> <context>
@ -215,7 +234,7 @@
<message> <message>
<source>About</source> <source>About</source>
<extracomment>If there&apos;s no good translation for &quot;About&quot;, use &quot;Details&quot; (in details about profile).</extracomment> <extracomment>If there&apos;s no good translation for &quot;About&quot;, use &quot;Details&quot; (in details about profile).</extracomment>
<translation type="unfinished"></translation> <translation>Om</translation>
</message> </message>
<message> <message>
<source>Followers</source> <source>Followers</source>
@ -230,11 +249,11 @@
<message> <message>
<source>Statuses</source> <source>Statuses</source>
<extracomment>Will show as: &quot;115 Statuses&quot;</extracomment> <extracomment>Will show as: &quot;115 Statuses&quot;</extracomment>
<translation>Statusar</translation> <translation>Status</translation>
</message> </message>
<message> <message>
<source>Mention</source> <source>Mention</source>
<translation type="unfinished"></translation> <translation>Omnämnande</translation>
</message> </message>
<message> <message>
<source>Unfollow</source> <source>Unfollow</source>
@ -244,7 +263,7 @@
<message> <message>
<source>Requested</source> <source>Requested</source>
<extracomment>Is a button. Keep it as short as possible.</extracomment> <extracomment>Is a button. Keep it as short as possible.</extracomment>
<translation type="unfinished"></translation> <translation>Efterfrågad</translation>
</message> </message>
<message> <message>
<source>Follow</source> <source>Follow</source>
@ -254,7 +273,7 @@
<message> <message>
<source>Unmute</source> <source>Unmute</source>
<extracomment>Is a button. Keep it as short as possible.</extracomment> <extracomment>Is a button. Keep it as short as possible.</extracomment>
<translation>Avtysta</translation> <translation>Frigör</translation>
</message> </message>
<message> <message>
<source>Mute</source> <source>Mute</source>
@ -280,15 +299,15 @@
</message> </message>
<message> <message>
<source>Options</source> <source>Options</source>
<translation type="unfinished"></translation> <translation>Alternativ</translation>
</message> </message>
<message> <message>
<source>Load Images in Toots</source> <source>Load Images in Toots</source>
<translation>Ladda bilder i toots</translation> <translation>Läs in bilder i tootar</translation>
</message> </message>
<message> <message>
<source>Disable this option if you want to preserve your data connection</source> <source>Disable this option if you want to preserve your data connection</source>
<translation>Inaktivera det här alternativet om du vill behålla din dataanslutning</translation> <translation>Inaktivera det här alternativet om du vill bevara din dataanslutning</translation>
</message> </message>
<message> <message>
<source>Account</source> <source>Account</source>
@ -296,7 +315,7 @@
</message> </message>
<message> <message>
<source>Remove Account</source> <source>Remove Account</source>
<translation>Radera konto</translation> <translation>Ta bort konto</translation>
</message> </message>
<message> <message>
<source>Add Account</source> <source>Add Account</source>
@ -304,11 +323,11 @@
</message> </message>
<message> <message>
<source>Deauthorize this app from using your account and remove account data from phone</source> <source>Deauthorize this app from using your account and remove account data from phone</source>
<translation>Avauktorisera denna app och radera ditt konto</translation> <translation>Avauktorisera denna app och ta bort din kontodata från telefonen</translation>
</message> </message>
<message> <message>
<source>Authorize this app to access your Mastodon account</source> <source>Authorize this app to access your Mastodon account</source>
<translation>Godkänn denna app att använda ditt Mastodon-konto dina vägnar</translation> <translation>Auktorisera denna app att använda ditt Mastodon-konto</translation>
</message> </message>
<message> <message>
<source>Translate</source> <source>Translate</source>
@ -325,27 +344,27 @@
</message> </message>
<message> <message>
<source>Visual identity</source> <source>Visual identity</source>
<translation type="unfinished"></translation> <translation>Visuell identitet</translation>
</message> </message>
<message> <message>
<source>Development and translations</source> <source>Development and translations</source>
<translation type="unfinished"></translation> <translation>Utveckling och översättningar</translation>
</message> </message>
<message> <message>
<source>Occitan &amp; French translation</source> <source>Occitan &amp; French translation</source>
<translation type="unfinished"></translation> <translation>Occitan &amp; Fransk översättning</translation>
</message> </message>
<message> <message>
<source>Chinese translation</source> <source>Chinese translation</source>
<translation type="unfinished"></translation> <translation>Kinesisk översättning</translation>
</message> </message>
<message> <message>
<source>Dutch translation</source> <source>Dutch translation</source>
<translation type="unfinished"></translation> <translation>Nederländsk översättning</translation>
</message> </message>
<message> <message>
<source>Spanish translation</source> <source>Spanish translation</source>
<translation type="unfinished"></translation> <translation>Spansk översättning</translation>
</message> </message>
<message> <message>
<source>Use</source> <source>Use</source>
@ -354,7 +373,11 @@
</message> </message>
<message> <message>
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>för att hjälpa med app-översättningar till ditt språk.</translation> <translation>för att hjälpa till med app-översättning till ditt språk.</translation>
</message>
<message>
<source>Development</source>
<translation>Utveckling</translation>
</message> </message>
</context> </context>
<context> <context>
@ -377,19 +400,19 @@
</message> </message>
<message> <message>
<source>Mention</source> <source>Mention</source>
<translation type="unfinished"></translation> <translation>Omnämnande</translation>
</message> </message>
<message> <message>
<source>Conversation</source> <source>Conversation</source>
<translation type="unfinished"></translation> <translation>Konversation</translation>
</message> </message>
<message> <message>
<source>Remove Bookmark</source> <source>Remove Bookmark</source>
<translation type="unfinished"></translation> <translation>Ta bort bokmärke</translation>
</message> </message>
<message> <message>
<source>Bookmark</source> <source>Bookmark</source>
<translation type="unfinished"></translation> <translation>Bokmärk</translation>
</message> </message>
</context> </context>
</TS> </TS>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation></translation> <translation></translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>使.</translation> <translation>使.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>

View file

@ -149,6 +149,10 @@
<source>New Toot</source> <source>New Toot</source>
<translation>New Toot</translation> <translation>New Toot</translation>
</message> </message>
<message>
<source>Bookmarks</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>MediaFullScreen</name> <name>MediaFullScreen</name>
@ -157,6 +161,21 @@
<translation>Error loading</translation> <translation>Error loading</translation>
</message> </message>
</context> </context>
<context>
<name>MediaItem</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF document</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MiniStatus</name> <name>MiniStatus</name>
<message> <message>
@ -356,6 +375,10 @@
<source>to help with app translation to your language.</source> <source>to help with app translation to your language.</source>
<translation>to help with app translation to your language.</translation> <translation>to help with app translation to your language.</translation>
</message> </message>
<message>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>VisualContainer</name> <name>VisualContainer</name>