Add / improve UI elements

- new ProfilePage expander
- flexible UI elements for various screensizes
- various UI changes
- code refactoring
This commit is contained in:
molan-git 2020-05-29 20:05:05 +02:00
parent 41041a77aa
commit fd2f317f25
25 changed files with 868 additions and 779 deletions

View file

@ -4,8 +4,3 @@ X-Nemo-Application-Type=silica-qt5
Icon=harbour-tooter
Exec=harbour-tooter
Name=Tooter
# translation example:
# your app name in German locale (de)
#
# Remember to comment out the following line, if you do not want to use
# a different app name in German locale (de).

View file

@ -53,6 +53,9 @@ HEADERS += src/dbusAdaptor.h
HEADERS += src/dbus.h
DISTFILES += qml/harbour-tooter.qml \
qml/pages/ConversationPage.qml \
qml/pages/ProfilePage.qml \
qml/pages/SettingsPage.qml \
qml/pages/components/InfoBanner.qml \
qml/pages/components/VisualContainer.qml \
qml/pages/components/MiniStatus.qml \
@ -67,11 +70,8 @@ DISTFILES += qml/harbour-tooter.qml \
qml/cover/CoverPage.qml \
qml/pages/MainPage.qml \
qml/pages/LoginPage.qml \
qml/pages/Conversation.qml \
qml/pages/components/Toot.qml \
qml/pages/Browser.qml \
qml/pages/Profile.qml \
qml/pages/Settings.qml \
qml/lib/API.js \
qml/images/notification.svg \
qml/images/verified.svg \
@ -95,7 +95,7 @@ SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172
# to disable building translations every time, comment out the
# following CONFIG line
CONFIG += sailfishapp_i18n
# CONFIG += sailfishapp_i18n
# German translation is enabled as an example. If you aren't
# planning to localize your app, remember to comment out the

View file

@ -30,9 +30,9 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import "../lib/API.js" as Logic
CoverBackground {
onStatusChanged: {
switch (status ){
@ -135,5 +135,5 @@ CoverBackground {
notificationsLbl.text = notificationsNum;
Logic.conf.notificationLastID = notificationLastID;
}
}
}

View file

@ -33,23 +33,26 @@ import Sailfish.Silica 1.0
import "pages"
import "./lib/API.js" as Logic
ApplicationWindow
{
ApplicationWindow {
id: appWindow
//initialPage: Component { FirstPage { } }
cover: Qt.resolvedUrl("cover/CoverPage.qml")
allowedOrientations: defaultAllowedOrientations
Component.onCompleted: {
var obj = {};
Logic.mediator.installTo(obj);
var obj = {}
Logic.mediator.installTo(obj)
obj.subscribe('confLoaded', function() {
console.log('confLoaded');
//console.log(JSON.stringify(Logic.conf))
if (!Logic.conf['notificationLastID'])
Logic.conf['notificationLastID'] = 0;
Logic.conf['notificationLastID'] = 0
if (Logic.conf['instance']) {
Logic.api = new Logic.MastodonAPI({ instance: Logic.conf['instance'], api_user_token: "" });
Logic.api = new Logic.MastodonAPI({
"instance": Logic.conf['instance'],
"api_user_token": ""
})
}
if (Logic.conf['login']) {
//Logic.conf['notificationLastID'] = 0
Logic.api.setConfig("api_user_token", Logic.conf['api_user_token'])
@ -57,17 +60,12 @@ ApplicationWindow
Logic.api.get('instance', [], function(data) {
console.log(JSON.stringify(data))
pageStack.push(Qt.resolvedUrl("./pages/MainPage.qml"), {})
});
//
//
})
//pageStack.push(Qt.resolvedUrl("./pages/Conversation.qml"), {})
} else {
pageStack.push(Qt.resolvedUrl("./pages/LoginPage.qml"), {})
}
});
})
Logic.init()
}
@ -75,19 +73,18 @@ ApplicationWindow
//Logic.conf.notificationLastID = 0;
Logic.saveData()
}
Connections
{
Connections {
target: Dbus
onViewtoot:
{
onViewtoot: {
console.log(key, "dbus onViewtoot")
}
onActivateapp:
{
onActivateapp: {
console.log ("dbus activate app")
pageStack.pop(pageStack.find( function(page){ return (page._depth === 0) }))
pageStack.pop(pageStack.find( function(page) {
return (page._depth === 0)
}))
activate()
}
}
}
}

View file

@ -56,8 +56,8 @@ var MastodonAPI = function(config) {
http.setRequestHeader("Connection", "close");
http.onreadystatechange = function() { // Call a function when the state changes.
if (http.readyState == 4) {
if (http.status == 200) {
if (http.readyState === 4) {
if (http.status === 200) {
console.log("Successful GET API request to " +apiBase+endpoint);
callback(JSON.parse(http.response),http.status)
} else {
@ -91,8 +91,8 @@ var MastodonAPI = function(config) {
http.setRequestHeader("Connection", "close");
http.onreadystatechange = function() { // Call a function when the state changes.
if (http.readyState == 4) {
if (http.status == 200) {
if (http.readyState === 4) {
if (http.status === 200) {
console.log("Successful POST API request to " +apiBase+endpoint);
callback(JSON.parse(http.response),http.status)
} else {
@ -180,8 +180,8 @@ var MastodonAPI = function(config) {
http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
http.onreadystatechange = function() { // Call a function when the state changes.
if (http.readyState == 4) {
if (http.status == 200) {
if (http.readyState === 4) {
if (http.status === 200) {
console.log("Registered Application: " + http.response);
callback(http.response)
} else {
@ -221,8 +221,8 @@ var MastodonAPI = function(config) {
http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
http.onreadystatechange = function() { // Call a function when the state changes.
if (http.readyState == 4) {
if (http.status == 200) {
if (http.readyState === 4) {
if (http.status === 200) {
console.log("Got Token: " + http.response);
callback(http.response)
} else {

View file

@ -66,6 +66,7 @@ Page {
id: header
visible: false
}
SilicaListView {
id: conversationList
header: PageHeader {
@ -97,6 +98,7 @@ Page {
}
}
}
PullDownMenu {
visible: type == "reply" && toot_url != ""
/* MenuItem {
@ -110,19 +112,24 @@ Page {
}
}
}
Rectangle {
id: predictionList
visible: false
anchors.bottom: panel.top
anchors.left: parent.left
anchors.right: panel.right
height: suggestedModel.count > 6 ? Theme.itemSizeMedium * 6 : Theme.itemSizeMedium * suggestedModel.count
anchors.top: parent.top
height: implicitHeight
//height: suggestedModel.count > 6 ? Theme.itemSizeMedium * 6 : Theme.itemSizeMedium * suggestedModel.count
color: Theme.highlightDimmerColor
SilicaListView {
anchors.fill: parent
model: suggestedModel
clip: true
quickScroll: false
VerticalScrollDecorator {}
delegate: ItemUser {
onClicked: {
var start = toot.cursorPosition
@ -150,17 +157,17 @@ Page {
DockedPanel {
id: panel
open: true
onExpandedChanged: {
if (!expanded) {
show()
}
}
//onExpandedChanged: {
// if (!expanded) {
// show()
// }
//}
width: parent.width
height: progressBar.height + toot.height + (mediaModel.count ? uploadedImages.height : 0)
+ btnContentWarning.height + Theme.paddingMedium
+ (warningContent.visible ? warningContent.height : 0)
dock: Dock.Bottom
Rectangle {
width: parent.width
height: progressBar.height
@ -172,6 +179,7 @@ Page {
top: parent.top
}
}
Rectangle {
id: progressBar
width: toot.text.length ? panel.width * (toot.text.length / tootMaxChar) : 0
@ -201,10 +209,9 @@ Page {
placeholderColor: palette.highlightColor
color: palette.highlightColor
horizontalAlignment: Text.AlignLeft
EnterKey.onClicked: {
//tweet()
}
EnterKey.onClicked: {}
}
TextInput {
id: textOperations
visible: false
@ -221,17 +228,19 @@ Page {
}
autoScrollEnabled: true
labelVisible: false
//focus: true
text: description !== "" && (description.charAt(0) == '@'
text: description !== "" && (description.charAt(0) === '@'
|| description.charAt(
0) == '#') ? description + ' ' : ''
height: Math.max(270, Math.min(900, implicitHeight))
0) === '#') ? description + ' ' : ''
height: if (type !== "reply") {
Math.max(conversationPage.height / 3, Math.min(conversationPage.height * 0.65, implicitHeight))
}
else {
Math.max(conversationPage.height / 4, Math.min(conversationPage.height * 0.65, implicitHeight))
}
horizontalAlignment: Text.AlignLeft
placeholderText: qsTr("What's on your mind?")
font.pixelSize: Theme.fontSizeSmall
EnterKey.onClicked: {
//tweet()
}
EnterKey.onClicked: {}
onTextChanged: {
textOperations.text = toot.text
textOperations.cursorPosition = toot.cursorPosition
@ -247,32 +256,33 @@ Page {
}
}
}
IconButton {
id: btnSmileys
property string selection
onSelectionChanged: {
console.log(selection)
}
anchors {
top: warningContent.bottom
bottom: bottom.top
right: parent.right
rightMargin: Theme.paddingSmall
}
opacity: 0.8
icon.source: "../../qml/images/emojiselect.svg" + (pressed ? Theme.highlightColor : (warningContent.visible ? Theme.secondaryHighlightColor : Theme.primaryColor))
onClicked: pageStack.push(firstWizardPage)
opacity: 0.6
icon.source: "../../qml/images/emojiselect.svg"
onClicked: pageStack.push(emojiSelect)
}
SilicaGridView {
id: uploadedImages
width: parent.width
anchors.top: bottom.toot
anchors.bottom: parent.bottom
height: mediaModel.count ? Theme.itemSizeSmall : 0
height: mediaModel.count ? Theme.itemSizeExtraLarge : 0
model: mediaModel
cellWidth: uploadedImages.width / 4
cellHeight: Theme.itemSizeSmall
cellHeight: Theme.itemSizeExtraLarge
delegate: BackgroundItem {
id: myDelegate
width: uploadedImages.cellWidth
@ -280,12 +290,12 @@ Page {
RemorseItem {
id: remorse
}
Image {
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
source: model.preview_url
}
onClicked: {
var idx = index
console.log(idx)
@ -303,7 +313,6 @@ Page {
duration: 800
}
}
remove: Transition {
NumberAnimation {
property: "opacity"
@ -320,6 +329,7 @@ Page {
}
}
}
IconButton {
id: btnContentWarning
anchors {
@ -332,6 +342,7 @@ Page {
+ (pressed ? Theme.highlightColor : (warningContent.visible ? Theme.secondaryHighlightColor : Theme.primaryColor))
onClicked: warningContent.visible = !warningContent.visible
}
IconButton {
id: btnAddImage
enabled: mediaModel.count < 4
@ -357,21 +368,18 @@ Page {
})
}
}
ImageUploader {
id: imageUploader
onProgressChanged: {
console.log("progress " + progress)
uploadProgress.width = parent.width * progress
}
onSuccess: {
uploadProgress.width = 0
console.log(replyData)
mediaModel.append(JSON.parse(replyData))
}
onFailure: {
uploadProgress.width = 0
btnAddImage.enabled = true
@ -379,6 +387,7 @@ Page {
console.log(statusText)
}
}
ComboBox {
id: privacy
anchors {
@ -421,7 +430,6 @@ Page {
console.log(mediaModel.get(k).id)
media_ids.push(mediaModel.get(k).id)
}
var msg = {
"action": 'statuses',
"method": 'POST',
@ -445,7 +453,7 @@ Page {
worker.sendMessage(msg)
warningContent.text = ""
toot.text = ""
mediaModel.clear();
mediaModel.clear()
sentBanner.showText(qsTr("Toot sent!"))
}
}
@ -455,9 +463,10 @@ Page {
color: Theme.highlightBackgroundColor
anchors.bottom: parent.bottom
anchors.left: parent.left
height: 3
height: Theme.itemSizeSmall * 0.05
}
}
Component.onCompleted: {
toot.cursorPosition = toot.text.length
if (mdl.count > 0) {
@ -490,157 +499,64 @@ Page {
"conf": Logic.conf
})
}
Component {
id: firstWizardPage
Dialog {
id: emoticonsDialog
canAccept: false //selector.currentIndex >= 0
//acceptDestination: conversationPage
onAcceptPendingChanged: {
if (acceptPending) {
// Tell the destination page what the selected category is
// acceptDestinationInstance.category = selector.value
}
BackgroundItem {
id: showPanel
visible: !panel.open
height: Theme.paddingMedium
width: parent.width
opacity: enabled ? 1.0 : 0.0
Behavior on opacity { FadeAnimator {} }
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
}
SilicaGridView {
id: gridView
MouseArea {
anchors.fill: parent
cellWidth: gridView.width / 6
cellHeight: cellWidth
header: PageHeader {
title: qsTr("Emojis")
description: qsTr("Tap to insert")
onClicked: panel.open = !panel.open
}
model: ListModel {
ListElement { section: "smileys"; glyph: "😁" }
ListElement { section: "smileys"; glyph: "😂" }
ListElement { section: "smileys"; glyph: "😃" }
ListElement { section: "smileys"; glyph: "😄" }
ListElement { section: "smileys"; glyph: "😅" }
ListElement { section: "smileys"; glyph: "😆" }
ListElement { section: "smileys"; glyph: "😉" }
ListElement { section: "smileys"; glyph: "😊" }
ListElement { section: "smileys"; glyph: "😋" }
ListElement { section: "smileys"; glyph: "😌" }
ListElement { section: "smileys"; glyph: "😍" }
ListElement { section: "smileys"; glyph: "😏" }
ListElement { section: "smileys"; glyph: "😒" }
ListElement { section: "smileys"; glyph: "😓" }
ListElement { section: "smileys"; glyph: "😔" }
ListElement { section: "smileys"; glyph: "😖" }
ListElement { section: "smileys"; glyph: "😘" }
ListElement { section: "smileys"; glyph: "😚" }
ListElement { section: "smileys"; glyph: "😜" }
ListElement { section: "smileys"; glyph: "😝" }
ListElement { section: "smileys"; glyph: "😞" }
ListElement { section: "smileys"; glyph: "😠" }
ListElement { section: "smileys"; glyph: "😡" }
ListElement { section: "smileys"; glyph: "😢" }
ListElement { section: "smileys"; glyph: "😣" }
ListElement { section: "smileys"; glyph: "😤" }
ListElement { section: "smileys"; glyph: "😥" }
ListElement { section: "smileys"; glyph: "😨" }
ListElement { section: "smileys"; glyph: "😩" }
ListElement { section: "smileys"; glyph: "😪" }
ListElement { section: "smileys"; glyph: "😫" }
ListElement { section: "smileys"; glyph: "😭" }
ListElement { section: "smileys"; glyph: "😰" }
ListElement { section: "smileys"; glyph: "😱" }
ListElement { section: "smileys"; glyph: "😲" }
ListElement { section: "smileys"; glyph: "😳" }
ListElement { section: "smileys"; glyph: "😵" }
ListElement { section: "smileys"; glyph: "😷" }
ListElement { section: "smileys"; glyph: "😸" }
ListElement { section: "smileys"; glyph: "😹" }
ListElement { section: "smileys"; glyph: "😺" }
ListElement { section: "smileys"; glyph: "😻" }
ListElement { section: "smileys"; glyph: "😼" }
ListElement { section: "smileys"; glyph: "😽" }
ListElement { section: "smileys"; glyph: "😾" }
ListElement { section: "smileys"; glyph: "😿" }
ListElement { section: "smileys"; glyph: "🙀" }
ListElement { section: "smileys"; glyph: "🙅" }
ListElement { section: "smileys"; glyph: "🙆" }
ListElement { section: "smileys"; glyph: "🙇" }
ListElement { section: "smileys"; glyph: "🙈" }
ListElement { section: "smileys"; glyph: "🙉" }
ListElement { section: "smileys"; glyph: "🙊" }
ListElement { section: "smileys"; glyph: "🙋" }
ListElement { section: "smileys"; glyph: "🙌" }
ListElement { section: "smileys"; glyph: "🙍" }
ListElement { section: "smileys"; glyph: "🙎" }
ListElement { section: "smileys"; glyph: "🙏" }
ListElement { section: "Transport and map"; glyph: "🚀" }
ListElement { section: "Transport and map"; glyph: "🚃" }
ListElement { section: "Transport and map"; glyph: "🚀" }
ListElement { section: "Transport and map"; glyph: "🚄" }
ListElement { section: "Transport and map"; glyph: "🚅" }
ListElement { section: "Transport and map"; glyph: "🚇" }
ListElement { section: "Transport and map"; glyph: "🚉" }
ListElement { section: "Transport and map"; glyph: "🚌" }
ListElement { section: "Transport and map"; glyph: "🚏" }
ListElement { section: "Transport and map"; glyph: "🚑" }
ListElement { section: "Transport and map"; glyph: "🚒" }
ListElement { section: "Transport and map"; glyph: "🚓" }
ListElement { section: "Transport and map"; glyph: "🚕" }
ListElement { section: "Transport and map"; glyph: "🚗" }
ListElement { section: "Transport and map"; glyph: "🚙" }
ListElement { section: "Transport and map"; glyph: "🚚" }
ListElement { section: "Transport and map"; glyph: "🚢" }
ListElement { section: "Transport and map"; glyph: "🚨" }
ListElement { section: "Transport and map"; glyph: "🚩" }
ListElement { section: "Transport and map"; glyph: "🚪" }
ListElement { section: "Transport and map"; glyph: "🚫" }
ListElement { section: "Transport and map"; glyph: "🚬" }
ListElement { section: "Transport and map"; glyph: "🚭" }
ListElement { section: "Transport and map"; glyph: "🚲" }
ListElement { section: "Transport and map"; glyph: "🚶" }
ListElement { section: "Transport and map"; glyph: "🚹" }
ListElement { section: "Transport and map"; glyph: "🚺" }
ListElement { section: "Transport and map"; glyph: "🚻" }
ListElement { section: "Transport and map"; glyph: "🚼" }
ListElement { section: "Transport and map"; glyph: "🚽" }
ListElement { section: "Transport and map"; glyph: "🚾" }
ListElement { section: "Transport and map"; glyph: "🛀" }
Rectangle {
width: parent.width
height: progressBarShowPanel.height
color: Theme.highlightBackgroundColor
opacity: 0.2
anchors {
left: parent.left
right: parent.right
top: parent.top
}
}
ListElement { section: "Horoscope Signs"; glyph: "♈" }
ListElement { section: "Horoscope Signs"; glyph: "♉" }
ListElement { section: "Horoscope Signs"; glyph: "♊" }
ListElement { section: "Horoscope Signs"; glyph: "♋" }
ListElement { section: "Horoscope Signs"; glyph: "♌" }
ListElement { section: "Horoscope Signs"; glyph: "♍" }
ListElement { section: "Horoscope Signs"; glyph: "♎" }
ListElement { section: "Horoscope Signs"; glyph: "♏" }
ListElement { section: "Horoscope Signs"; glyph: "♐" }
ListElement { section: "Horoscope Signs"; glyph: "♑" }
ListElement { section: "Horoscope Signs"; glyph: "♒" }
ListElement { section: "Horoscope Signs"; glyph: "♓" }
}
delegate: BackgroundItem {
width: gridView.cellWidth
height: gridView.cellHeight
Label {
anchors.centerIn: parent
color: (highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
font.pixelSize: Theme.fontSizeLarge
text: glyph
}
onClicked: {
var cursorPosition = toot.cursorPosition
toot.text = toot.text.substring(
0, cursorPosition) + model.glyph + toot.text.substring(
cursorPosition)
toot.cursorPosition = cursorPosition + model.glyph.length
emoticonsDialog.canAccept = true
emoticonsDialog.accept()
Rectangle {
color: Theme.highlightBackgroundColor
opacity: 0.2
height: showPanel.height
width: showPanel.width
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
bottom: parent.bottom
}
}
Rectangle {
id: progressBarShowPanel
width: toot.text.length ? panel.width * (toot.text.length / tootMaxChar) : 0
height: Theme.itemSizeSmall * 0.05
color: Theme.highlightBackgroundColor
opacity: 0.7
anchors {
left: parent.left
top: parent.top
}
}
}
EmojiSelect {
id: emojiSelect
}
}

View file

@ -1,33 +1,3 @@
/*
Copyright (C) 2013 Jolla Ltd.
Contact: Thomas Perl <thomas.perl@jollamobile.com>
All rights reserved.
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import QtWebKit 3.0
import Sailfish.Silica 1.0
@ -192,5 +162,5 @@ Page {
}
}
}
}
}

View file

@ -1,33 +1,3 @@
/*
Copyright (C) 2013 Jolla Ltd.
Contact: Thomas Perl <thomas.perl@jollamobile.com>
All rights reserved.
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import "../lib/API.js" as Logic
@ -37,7 +7,7 @@ import "./components/"
Page {
id: mainPage
property bool isFirstPage: true
property bool isTablet: true; //Screen.sizeCategory >= Screen.Large
property bool isTablet: true //Screen.sizeCategory >= Screen.Large
// The effective value will be restricted by ApplicationWindow.allowedOrientations
allowedOrientations: Orientation.All
@ -185,7 +155,7 @@ Page {
delegate: ItemUser {
onClicked: {
pageStack.push(Qt.resolvedUrl("Profile.qml"), {
pageStack.push(Qt.resolvedUrl("ProfilePage.qml"), {
"display_name": model.account_display_name,
"username": model.account_acct,
"user_id": model.account_id,
@ -207,7 +177,6 @@ Page {
}
SlideshowView {
id: slideshow
width: parent.width
@ -233,6 +202,11 @@ Page {
}
IconButton {
id: newTweet
width: Theme.iconSizeLarge
height: width
visible: !isPortrait ? true : !infoPanel.open
icon.source: "image://theme/icon-l-add"
anchors {
right: (mainPage.isPortrait ? parent.right : infoPanel.left)
bottom: (mainPage.isPortrait ? infoPanel.top : parent.bottom)
@ -241,14 +215,11 @@ Page {
bottom: Theme.paddingLarge
}
}
id: newTweet
width: Theme.iconSizeLarge
height: width
visible: !isPortrait ? true : !infoPanel.open
icon.source: "image://theme/icon-l-add"
onClicked: {
pageStack.push(Qt.resolvedUrl("Conversation.qml"), {headerTitle: qsTr("New Toot"), type: "new"})
pageStack.push(Qt.resolvedUrl("ConversationPage.qml"), {
headerTitle: qsTr("New Toot"),
type: "new"
})
}
}
@ -274,5 +245,5 @@ Page {
Component.onCompleted: {
console.log("aaa")
}
}
}

View file

@ -1,306 +0,0 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import "../lib/API.js" as Logic
import "./components/"
import QtGraphicalEffects 1.0
Page {
property ListModel tweets;
property string display_name : "";
property string username : "";
property string profileImage : "";
property int user_id;
property int statuses_count;
property int following_count;
property int followers_count;
property int favourites_count;
property int reblogs_count;
property int count_moments;
property string profileBackground: "";
property string note: "";
property string url: "";
property bool locked : false;
property date created_at;
property bool following : false;
property bool requested : false;
property bool followed_by : false;
property bool blocking : false;
property bool muting : false;
property bool domain_blocking : false;
WorkerScript {
id: worker
source: "../lib/Worker.js"
onMessage: {
console.log(JSON.stringify(messageObject))
if(messageObject.action.indexOf("accounts/search") > -1 ){
user_id = messageObject.data.id
followers_count = messageObject.data.followers_count
following_count = messageObject.data.following_count
username = messageObject.data.acct
display_name = messageObject.data.display_name
profileImage = messageObject.data.avatar_static
profileBackground = messageObject.data.header_static
var msg = {
'action' : "accounts/relationships/",
'params' : [ {name: "id", data: user_id}],
'conf' : Logic.conf
};
worker.sendMessage(msg);
list.loadData("prepend")
}
if(messageObject.action === "accounts/relationships/"){
console.log(JSON.stringify(messageObject))
following= messageObject.data.following
requested= messageObject.data.requested
followed_by= messageObject.data.followed_by
blocking= messageObject.data.blocking
muting= messageObject.data.muting
domain_blocking= messageObject.data.domain_blocking
}
switch (messageObject.key) {
case 'followers_count':
followers_count = messageObject.data
break;
case 'following_count':
following_count = messageObject.data
break;
case 'acct':
// line below was commented out, reason unknown
// username = messageObject.data
break;
case 'locked':
locked = messageObject.data
break;
case 'created_at':
created_at = messageObject.data
break;
case 'statuses_count':
statuses_count = messageObject.data
break;
case 'note':
note = messageObject.data
break;
case 'url':
url = messageObject.data
break;
case 'following':
following = messageObject.data
followers_count = followers_count + (following ? 1 : - 1)
break;
case 'muting':
muting = messageObject.data
break;
case 'muting':
muting = messageObject.data
break;
case 'blocking':
blocking = messageObject.data
followers_count = followers_count + (blocking ? -1 : 0)
break;
case 'followed_by':
followed_by = messageObject.data
break;
}
}
}
// The effective value will be restricted by ApplicationWindow.allowedOrientations
allowedOrientations: Orientation.All
Component.onCompleted: {
var msg;
if (user_id) {
msg = {
'action' : "accounts/relationships/",
'params' : [ {name: "id", data: user_id}],
'conf' : Logic.conf
};
worker.sendMessage(msg);
msg = {
'action' : "accounts/"+user_id,
'conf' : Logic.conf
};
worker.sendMessage(msg);
} else {
var instance = Logic.conf['instance'].split("//")
msg = {
'action' : "accounts/search?limit=1&q="+username.replace("@"+instance[1], ""),
'conf' : Logic.conf
};
worker.sendMessage(msg);
}
}
MyList {
id: list
header: ProfileHeader {
id: header
title: display_name
description: username
image: profileImage
bg: profileBackground
}
anchors {
top: parent.top
bottom: expander.top
left: parent.left
right: parent.right
}
clip: true
mdl: ListModel {}
type: "accounts/"+user_id+"/statuses"
vars: {}
conf: Logic.conf
}
ExpandingSectionGroup {
id: expander
//currentIndex: 0
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
ExpandingSection {
title: qsTr("Summary")
content.sourceComponent: Column {
spacing: Theme.paddingMedium
anchors.bottomMargin: Theme.paddingLarge
DetailItem {
visible: followers_count ? true : false
label: qsTr("Followers")
value: followers_count
}
DetailItem {
visible: following_count ? true : false
label: qsTr("Following")
value: (following_count)
}
DetailItem {
visible: statuses_count ? true : false
label: qsTr("Statuses")
value: (statuses_count)
}
DetailItem {
visible: favourites_count ? true : false
label: qsTr("Favourites")
value: (favourites_count)
}
Column {
spacing: Theme.paddingMedium
anchors.horizontalCenter: parent.horizontalCenter
Button {
id: btnFollow
text: (following ? qsTr("Unfollow") : (requested ? qsTr("Follow request sent!") : qsTr("Follow")))
onClicked: {
var msg = {
'method' : 'POST',
'params' : [],
'action' : "accounts/" + user_id + (following ? '/unfollow':'/follow'),
'conf' : Logic.conf
};
worker.sendMessage(msg);
}
}
Button {
id: btnMute
text: (muting ? qsTr("Unmute") : qsTr("Mute"))
onClicked: {
var msg = {
'method' : 'POST',
'params' : [],
'action' : "accounts/" + user_id + (muting ? '/unmute':'/mute'),
'conf' : Logic.conf
};
worker.sendMessage(msg);
}
}
Button {
id: btnBlock
text: (blocking ? qsTr("Unblock") : qsTr("Block") )
onClicked: {
var msg = {
'method' : 'POST',
'params' : [],
'action' : "accounts/" + user_id + (blocking ? '/unblock':'/block'),
'conf' : Logic.conf
};
worker.sendMessage(msg);
}
}
}
Label {
text: " "
}
}
}
ExpandingSection {
title: qsTr("Bio")
content.sourceComponent: Column {
spacing: Theme.paddingMedium
anchors.bottomMargin: Theme.paddingLarge
Text {
x: Theme.horizontalPageMargin
width: parent.width - ( 2 * Theme.horizontalPageMargin )
id: txtnote
text: note
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.secondaryColor
linkColor: Theme.secondaryHighlightColor
wrapMode: Text.Wrap
anchors {
horizontalCenter: parent.horizontalCenter
}
onLinkActivated: {
var test = link.split("/")
console.log(link)
console.log(JSON.stringify(test))
console.log(JSON.stringify(test.length))
if (test.length === 5 && (test[3] === "tags" || test[3] === "tag") ) {
pageStack.pop(pageStack.find(function(page) {
var check = page.isFirstPage === true;
if (check)
page.onLinkActivated(link)
return check;
}));
send(link)
// function still missing for user accounts
// } else if (test.length === 4 && test[3][0] === "@" ) {
} else {
Qt.openUrlExternally(link);
}
}
}
Column {
spacing: Theme.paddingMedium
anchors.horizontalCenter: parent.horizontalCenter
Button {
text: qsTr("Open Profile in Browser")
onClicked: {
Qt.openUrlExternally(url);
}
}
}
Label {
text: " "
}
}
}
}
}

374
qml/pages/ProfilePage.qml Normal file
View file

@ -0,0 +1,374 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import "../lib/API.js" as Logic
import "./components/"
import QtGraphicalEffects 1.0
Page {
id: profilePage
property ListModel tweets
property string display_name: ""
property string username: ""
property string profileImage: ""
property int user_id
property int statuses_count
property int following_count
property int followers_count
property int favourites_count
property int reblogs_count
property int count_moments
property string profileBackground: ""
property string note: ""
property string url: ""
property bool locked: false
property date created_at
property bool following: false
property bool requested: false
property bool followed_by: false
property bool blocking: false
property bool muting: false
property bool domain_blocking: false
WorkerScript {
id: worker
source: "../lib/Worker.js"
onMessage: {
console.log(JSON.stringify(messageObject))
if(messageObject.action.indexOf("accounts/search") > -1 ){
user_id = messageObject.data.id
followers_count = messageObject.data.followers_count
following_count = messageObject.data.following_count
username = messageObject.data.acct
display_name = messageObject.data.display_name
profileImage = messageObject.data.avatar_static
profileBackground = messageObject.data.header_static
var msg = {
'action' : "accounts/relationships/",
'params' : [ {name: "id", data: user_id}],
'conf' : Logic.conf
};
worker.sendMessage(msg);
list.loadData("prepend")
}
if(messageObject.action === "accounts/relationships/"){
console.log(JSON.stringify(messageObject))
following= messageObject.data.following
requested= messageObject.data.requested
followed_by= messageObject.data.followed_by
blocking= messageObject.data.blocking
muting= messageObject.data.muting
domain_blocking= messageObject.data.domain_blocking
}
switch (messageObject.key) {
case 'followers_count':
followers_count = messageObject.data
break;
case 'following_count':
following_count = messageObject.data
break;
case 'acct':
// line below was commented out, reason unknown
// username = messageObject.data
break;
case 'locked':
locked = messageObject.data
break;
case 'created_at':
created_at = messageObject.data
break;
case 'statuses_count':
statuses_count = messageObject.data
break;
case 'note':
note = messageObject.data
break;
case 'url':
url = messageObject.data
break;
case 'following':
following = messageObject.data
followers_count = followers_count + (following ? 1 : - 1)
break;
case 'muting':
muting = messageObject.data
break;
case 'blocking':
blocking = messageObject.data
followers_count = followers_count + (blocking ? -1 : 0)
break;
case 'followed_by':
followed_by = messageObject.data
break;
}
}
}
// The effective value will be restricted by ApplicationWindow.allowedOrientations
allowedOrientations: Orientation.All
Component.onCompleted: {
var msg
if (user_id) {
msg = {
'action' : "accounts/relationships/",
'params' : [ {name: "id", data: user_id}],
'conf' : Logic.conf
}
worker.sendMessage(msg)
msg = {
'action' : "accounts/"+user_id,
'conf' : Logic.conf
}
worker.sendMessage(msg)
} else {
var instance = Logic.conf['instance'].split("//")
msg = {
'action' : "accounts/search?limit=1&q="+username.replace("@"+instance[1], ""),
'conf' : Logic.conf
}
worker.sendMessage(msg)
}
}
MyList {
id: list
header: ProfileHeader {
id: profileHeader
title: display_name
description: username
image: profileImage
bg: profileBackground
}
anchors {
top: parent.top
bottom: expander.top
left: parent.left
right: parent.right
}
clip: true
mdl: ListModel {}
type: "accounts/"+user_id+"/statuses"
vars: {}
conf: Logic.conf
}
// ProfilePage ExpandingSection
ExpandingSectionGroup {
id: expander
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
ExpandingSection {
id: expandingSection1
title: qsTr("About")
content.sourceComponent: Column {
height: Math.min(txtContainer, parent.height*0.7)
spacing: Theme.paddingSmall
anchors.bottomMargin: Theme.paddingLarge
Rectangle {
id: txtContainer
width: expander.width
height: Math.min(txtNote.height, parent.height*0.488)
color: "transparent"
visible: {
if ((note.text === "") && (note.text === "<p></p>") ) {
false
} else {
true
}
}
SilicaListView {
id: txtFlickable
anchors.fill: txtContainer
clip: true
quickScroll: false
VerticalScrollDecorator { flickable: txtNote }
Text {
id: txtNote
text: note
textFormat: Text.StyledText
wrapMode: Text.Wrap
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.secondaryColor
linkColor: Theme.highlightColor
width: parent.width - ( 2 * Theme.horizontalPageMargin )
anchors.horizontalCenter: parent.horizontalCenter
onLinkActivated: {
var test = link.split("/")
console.log(link)
console.log(JSON.stringify(test))
console.log(JSON.stringify(test.length))
if (test.length === 5 && (test[3] === "tags" || test[3] === "tag") ) {
pageStack.pop(pageStack.find(function(page) {
var check = page.isFirstPage === true;
if (check)
page.onLinkActivated(link)
return check;
}));
send(link)
/* Function still missing for user accounts */
// } else if (test.length === 4 && test[3][0] === "@" ) {
} else {
Qt.openUrlExternally(link);
}
}
}
}
}
Row {
id: statsRow
spacing: Theme.paddingLarge
anchors.horizontalCenter: parent.horizontalCenter
anchors.leftMargin: Theme.paddingLarge
anchors.rightMargin: Theme.paddingLarge
Text {
id: txtFollowers
visible: followers_count ? true : false
text: followers_count+" "+qsTr("Followers")
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.highlightColor
wrapMode: Text.Wrap
}
Text {
id: txtFollowing
visible: following_count ? true : false
text: following_count+" "+qsTr("Following")
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.highlightColor
wrapMode: Text.Wrap
}
Text {
id: txtStatuses
visible: statuses_count ? true : false
text: statuses_count+" "+qsTr("Statuses")
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.highlightColor
wrapMode: Text.Wrap
}
/*Text {
id: txtFavourites
visible: favourites_count ? true : false
text: favourites_count+" "+qsTr("Favourites")
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.highlightColor
wrapMode: Text.Wrap
} */
}
Label {
id: separatorLabel1
x: Theme.horizontalPageMargin
width: parent.width - ( 2 * Theme.horizontalPageMargin )
font.pixelSize: Theme.fontSizeExtraSmall
wrapMode: Text.Wrap
anchors {
horizontalCenter: parent.horizontalCenter
}
}
ButtonLayout {
id: btnLayout
Button {
id: btnMention
preferredWidth: Theme.buttonWidthSmall
text: "Mention"
onClicked: {
pageStack.push(Qt.resolvedUrl("ConversationPage.qml"), {
headerTitle: "Mention",
description: "@"+username,
type: "new"
})
}
}
Button {
id: btnFollow
preferredWidth: Theme.buttonWidthSmall
text: (following ? qsTr("Unfollow") : (requested ? qsTr("Requested") : qsTr("Follow")))
color: (following ? highlightColor : (requested ? palette.errorColor : palette.primaryColor))
onClicked: {
var msg = {
'method' : 'POST',
'params' : [],
'action' : "accounts/" + user_id + (following ? '/unfollow':'/follow'),
'conf' : Logic.conf
};
worker.sendMessage(msg);
// to-do: create notification banner "Follow request sent!"
}
}
Button {
id: btnMute
preferredWidth: Theme.buttonWidthSmall
text: (muting ? qsTr("Unmute") : qsTr("Mute"))
color: (muting ? highlightColor : palette.primaryColor)
onClicked: {
var msg = {
'method' : 'POST',
'params' : [],
'action' : "accounts/" + user_id + (muting ? '/unmute':'/mute'),
'conf' : Logic.conf
};
worker.sendMessage(msg);
}
}
Button {
id: btnBlock
preferredWidth: Theme.buttonWidthSmall
text: (blocking ? qsTr("Unblock") : qsTr("Block") )
color: (blocking ? palette.errorColor : palette.primaryColor)
onClicked: {
var msg = {
'method' : 'POST',
'params' : [],
'action' : "accounts/" + user_id + (blocking ? '/unblock':'/block'),
'conf' : Logic.conf
};
worker.sendMessage(msg);
}
}
}
Separator {
id: btnSeparator
width: parent.width
height: Theme.paddingMedium
color: Theme.primaryColor
opacity: 0.0
horizontalAlignment: Qt.AlignHCenter
}
Button {
id: btnBrowser
text: qsTr("Open in Browser")
preferredWidth: Theme.buttonWidthMedium
anchors {
horizontalCenter: parent.horizontalCenter
}
onClicked: {
Qt.openUrlExternally(url);
}
}
Label {
id: separatorLabel2
x: Theme.horizontalPageMargin
width: parent.width - ( 2 * Theme.horizontalPageMargin )
font.pixelSize: Theme.fontSizeExtraSmall
wrapMode: Text.Wrap
anchors {
horizontalCenter: parent.horizontalCenter
}
}
}
}
}
}

View file

@ -1,16 +1,15 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import "../lib/API.js" as Logic
Page {
id: settingsPage
SilicaFlickable {
anchors.fill: parent
contentHeight: column.height + Theme.paddingLarge
contentWidth: parent.width
RemorsePopup { id: remorsePopup }
VerticalScrollDecorator {}
Column {
id: column
@ -22,13 +21,12 @@ Page {
Column {
// No spacing in this column
width: parent.width
IconTextSwitch {
id: removeAccount
text: Logic.conf['login'] ? qsTr("Remove Account") : qsTr("Add Account")
description: Logic.conf['login'] ? qsTr("Deauthorize this app and remove your account") : qsTr("Authorize this app to access your Mastodon account")
icon.source: Logic.conf['login'] ? "image://theme/icon-m-contact" : "image://theme/icon-m-add"
onCheckedChanged: {
remorsePopup.execute(removeAccount.text, function() {
busy = true;
@ -42,11 +40,9 @@ Page {
pageStack.push(Qt.resolvedUrl("LoginPage.qml"))
})
}
/* busy = true;
checked = false;
timer1.start()
}*/
Timer {
id: timer1
@ -54,6 +50,7 @@ Page {
onTriggered: parent.busy = false
}
}
IconTextSwitch {
//enabled: false
checked: typeof Logic.conf['loadImages'] !== "undefined" && Logic.conf['loadImages']
@ -64,6 +61,7 @@ Page {
Logic.conf['loadImages'] = checked
}
}
IconTextSwitch {
text: qsTr("Translate")
description: qsTr("Use Transifex to help with app translation to your language")
@ -81,6 +79,7 @@ Page {
}
}
}
SectionHeader {
text: qsTr("Credits")
}
@ -109,8 +108,8 @@ Page {
ListElement {
name: "Molan"
desc: qsTr("Development and translations")
mastodon: ""
mail: "mol_an@sunrise.ch"
mastodon: "molan@fosstodon.org"
mail: ""
}
ListElement {
name: "Quentin PAGÈS / Quenti ♏"
@ -159,7 +158,7 @@ Page {
onClicked: {
if (model.mastodon !== ""){
var m = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject');
pageStack.push(Qt.resolvedUrl("Conversation.qml"), {
pageStack.push(Qt.resolvedUrl("ConversationPage.qml"), {
toot_id: 0,
title: model.name,
description: '@'+model.mastodon,
@ -198,4 +197,5 @@ Page {
}
}
}
}

View file

@ -0,0 +1,165 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Component {
id: emojiComponent
Dialog {
id: emoticonsDialog
canAccept: false //selector.currentIndex >= 0
onAcceptPendingChanged: {
if (acceptPending) {
// Tell the destination page what the selected category is
// acceptDestinationInstance.category = selector.value
}
}
SilicaGridView {
id: gridView
anchors.fill: parent
//anchors.rightMargin: Theme.paddingLarge
//anchors.leftMargin: Theme.paddingLarge
cellWidth: gridView.width / 6
cellHeight: cellWidth
VerticalScrollDecorator {flickable: listEmojis }
header: PageHeader {
title: qsTr("Emojis")
description: qsTr("Tap to insert")
}
model: ListModel {
id: listEmojis
ListElement { section: "smileys"; glyph: "😁" }
ListElement { section: "smileys"; glyph: "😂" }
ListElement { section: "smileys"; glyph: "😃" }
ListElement { section: "smileys"; glyph: "😄" }
ListElement { section: "smileys"; glyph: "😅" }
ListElement { section: "smileys"; glyph: "😆" }
ListElement { section: "smileys"; glyph: "😉" }
ListElement { section: "smileys"; glyph: "😊" }
ListElement { section: "smileys"; glyph: "😋" }
ListElement { section: "smileys"; glyph: "😎" }
ListElement { section: "smileys"; glyph: "😌" }
ListElement { section: "smileys"; glyph: "😍" }
ListElement { section: "smileys"; glyph: "😘" }
ListElement { section: "smileys"; glyph: "😏" }
ListElement { section: "smileys"; glyph: "😒" }
ListElement { section: "smileys"; glyph: "😓" }
ListElement { section: "smileys"; glyph: "😔" }
ListElement { section: "smileys"; glyph: "😖" }
ListElement { section: "smileys"; glyph: "😚" }
ListElement { section: "smileys"; glyph: "😜" }
ListElement { section: "smileys"; glyph: "😝" }
ListElement { section: "smileys"; glyph: "😞" }
ListElement { section: "smileys"; glyph: "😠" }
ListElement { section: "smileys"; glyph: "😡" }
ListElement { section: "smileys"; glyph: "😢" }
ListElement { section: "smileys"; glyph: "😣" }
ListElement { section: "smileys"; glyph: "😤" }
ListElement { section: "smileys"; glyph: "😥" }
ListElement { section: "smileys"; glyph: "😨" }
ListElement { section: "smileys"; glyph: "😩" }
ListElement { section: "smileys"; glyph: "😪" }
ListElement { section: "smileys"; glyph: "😫" }
ListElement { section: "smileys"; glyph: "😭" }
ListElement { section: "smileys"; glyph: "😰" }
ListElement { section: "smileys"; glyph: "😱" }
ListElement { section: "smileys"; glyph: "😲" }
ListElement { section: "smileys"; glyph: "😳" }
ListElement { section: "smileys"; glyph: "😵" }
ListElement { section: "smileys"; glyph: "😷" }
ListElement { section: "smileys"; glyph: "😸" }
ListElement { section: "smileys"; glyph: "😹" }
ListElement { section: "smileys"; glyph: "😺" }
ListElement { section: "smileys"; glyph: "😻" }
ListElement { section: "smileys"; glyph: "😼" }
ListElement { section: "smileys"; glyph: "😽" }
ListElement { section: "smileys"; glyph: "😾" }
ListElement { section: "smileys"; glyph: "😿" }
ListElement { section: "smileys"; glyph: "🙀" }
ListElement { section: "People and Fantasy"; glyph: "🙅" }
ListElement { section: "People and Fantasy"; glyph: "🙆" }
ListElement { section: "People and Fantasy"; glyph: "🙇" }
ListElement { section: "People and Fantasy"; glyph: "🙈" }
ListElement { section: "People and Fantasy"; glyph: "🙉" }
ListElement { section: "People and Fantasy"; glyph: "🙊" }
ListElement { section: "People and Fantasy"; glyph: "🙋" }
ListElement { section: "People and Fantasy"; glyph: "🙍" }
ListElement { section: "People and Fantasy"; glyph: "🙎" }
ListElement { section: "People and Fantasy"; glyph: "👍" }
ListElement { section: "People and Fantasy"; glyph: "👎" }
ListElement { section: "People and Fantasy"; glyph: "🙌" }
ListElement { section: "People and Fantasy"; glyph: "✊" }
ListElement { section: "People and Fantasy"; glyph: "💪" }
ListElement { section: "People and Fantasy"; glyph: "👉" }
ListElement { section: "People and Fantasy"; glyph: "🙏" }
ListElement { section: "Transport and Map"; glyph: "🚀" }
ListElement { section: "Transport and Map"; glyph: "🚃" }
ListElement { section: "Transport and Map"; glyph: "🚀" }
ListElement { section: "Transport and Map"; glyph: "🚄" }
ListElement { section: "Transport and Map"; glyph: "🚅" }
ListElement { section: "Transport and Map"; glyph: "🚇" }
ListElement { section: "Transport and Map"; glyph: "🚉" }
ListElement { section: "Transport and Map"; glyph: "🚌" }
ListElement { section: "Transport and Map"; glyph: "🚏" }
ListElement { section: "Transport and Map"; glyph: "🚑" }
ListElement { section: "Transport and Map"; glyph: "🚒" }
ListElement { section: "Transport and Map"; glyph: "🚓" }
ListElement { section: "Transport and Map"; glyph: "🚕" }
ListElement { section: "Transport and Map"; glyph: "🚗" }
ListElement { section: "Transport and Map"; glyph: "🚙" }
ListElement { section: "Transport and Map"; glyph: "🚚" }
ListElement { section: "Transport and Map"; glyph: "🚢" }
ListElement { section: "Transport and Map"; glyph: "🚨" }
ListElement { section: "Transport and Map"; glyph: "🚩" }
ListElement { section: "Transport and Map"; glyph: "🚪" }
ListElement { section: "Transport and Map"; glyph: "🚫" }
ListElement { section: "Transport and Map"; glyph: "🚬" }
ListElement { section: "Transport and Map"; glyph: "🚭" }
ListElement { section: "Transport and Map"; glyph: "🚲" }
ListElement { section: "Transport and Map"; glyph: "🚶" }
ListElement { section: "Transport and Map"; glyph: "🚹" }
ListElement { section: "Transport and Map"; glyph: "🚺" }
ListElement { section: "Transport and Map"; glyph: "🚻" }
ListElement { section: "Transport and Map"; glyph: "🚼" }
ListElement { section: "Transport and Map"; glyph: "🚽" }
ListElement { section: "Transport and Map"; glyph: "🚾" }
ListElement { section: "Transport and Map"; glyph: "🛀" }
ListElement { section: "Horoscope Signs"; glyph: "♈" }
ListElement { section: "Horoscope Signs"; glyph: "♉" }
ListElement { section: "Horoscope Signs"; glyph: "♊" }
ListElement { section: "Horoscope Signs"; glyph: "♋" }
ListElement { section: "Horoscope Signs"; glyph: "♌" }
ListElement { section: "Horoscope Signs"; glyph: "♍" }
ListElement { section: "Horoscope Signs"; glyph: "♎" }
ListElement { section: "Horoscope Signs"; glyph: "♏" }
ListElement { section: "Horoscope Signs"; glyph: "♐" }
ListElement { section: "Horoscope Signs"; glyph: "♑" }
ListElement { section: "Horoscope Signs"; glyph: "♒" }
ListElement { section: "Horoscope Signs"; glyph: "♓" }
}
delegate: BackgroundItem {
width: gridView.cellWidth
height: gridView.cellHeight
Label {
anchors.centerIn: parent
color: (highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
font.pixelSize: Theme.fontSizeLarge
text: glyph
}
onClicked: {
var cursorPosition = toot.cursorPosition
toot.text = toot.text.substring(
0, cursorPosition) + model.glyph + toot.text.substring(
cursorPosition)
toot.cursorPosition = cursorPosition + model.glyph.length
emoticonsDialog.canAccept = true
emoticonsDialog.accept()
}
}
}
}
}

View file

@ -2,11 +2,13 @@ import QtQuick 2.0
import Sailfish.Silica 1.0
import QtMultimedia 5.0
FullscreenContentPage {
id: imagePage
property string type: ""
property string previewURL: ""
property string mediaURL: ""
id: imagePage
allowedOrientations: Orientation.All
Component.onCompleted: function(){
console.log(type)
@ -14,31 +16,12 @@ FullscreenContentPage {
console.log(mediaURL)
if (type != 'gifv' && type != 'video') {
imagePreview.source = mediaURL
imageFlickable.visible = true;
imageFlickable.visible = true
} else {
video.source = mediaURL
video.fillMode = VideoOutput.PreserveAspectFit
video.play()
videoFlickable.visible = true;
}
}
Item {
id: overlay
z: 100
property bool active: true
enabled: active
anchors.fill: parent
opacity: active ? 1.0 : 0.0
Behavior on opacity { FadeAnimator {}}
IconButton {
y: Theme.paddingLarge
anchors {
right: parent.right
rightMargin: Theme.horizontalPageMargin
}
icon.source: "image://theme/icon-m-dismiss"
onClicked: pageStack.pop()
videoFlickable.visible = true
}
}
@ -46,14 +29,17 @@ FullscreenContentPage {
id: videoFlickable
visible: false
anchors.fill: parent
contentWidth: imageContainer.width; contentHeight: imageContainer.height
contentWidth: imageContainer.width
contentHeight: imageContainer.height
clip: true
Image {
id: videoPreview
fillMode: Image.PreserveAspectFit
anchors.fill: parent
source: previewURL
}
Video {
id: video
anchors.fill: parent
@ -82,7 +68,7 @@ FullscreenContentPage {
playerIcon.icon.source = "image://theme/icon-m-play"
return;
case MediaPlayer.StoppedState:
playerIcon.icon.source = "image://theme/icon-m-stop"
playerIcon.icon.source = "image://theme/icon-m-reload"
return;
}
}
@ -97,11 +83,10 @@ FullscreenContentPage {
playerProgress.minimumValue = 0
playerProgress.value = position
}
}
onStopped: function(){
play()
stop()
}
IconButton {
@ -128,20 +113,20 @@ FullscreenContentPage {
anchors.leftMargin: 0
anchors.bottomMargin: Theme.paddingLarge*1.5
}
IconButton {
id: videoDlBtn
visible: true
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: Theme.paddingLarge
anchors.bottomMargin: Theme.paddingLarge*1.5
icon.source: "image://theme/icon-m-device-download"
icon.opacity: 0.0
icon.source: "image://theme/icon-m-cloud-download"
onClicked: {
var filename = mediaURL.split("/");
FileDownloader.downloadFile(mediaURL, filename[filename.length-1]);
}
}
Rectangle {
visible: videoError.text != ""
anchors.left: parent.left
@ -163,7 +148,6 @@ FullscreenContentPage {
}
}
MouseArea {
anchors.fill: parent
onClicked: function() {
@ -180,7 +164,8 @@ FullscreenContentPage {
id: imageFlickable
visible: false
anchors.fill: parent
contentWidth: imageContainer.width; contentHeight: imageContainer.height
contentWidth: imageContainer.width
contentHeight: imageContainer.height
clip: true
onHeightChanged: if (imagePreview.status === Image.Ready) imagePreview.fitToScreen();
@ -201,8 +186,8 @@ FullscreenContentPage {
fillMode: Image.PreserveAspectFit
cache: true
asynchronous: true
sourceSize.height: 1000;
smooth: false
sourceSize.height: 2000;
smooth: true // might slower performance
onStatusChanged: {
if (status == Image.Ready) {
fitToScreen()
@ -255,6 +240,7 @@ FullscreenContentPage {
bounceBackAnimation.start()
}
}
NumberAnimation {
id: bounceBackAnimation
target: imagePreview
@ -280,17 +266,22 @@ FullscreenContentPage {
Component {
id: loadingIndicator
Item {
height: childrenRect.height
width: imagePage.width
ProgressCircle {
id: imageLoadingIndicator
progressColor: inAlternateCycle ? Theme.highlightColor : Theme.highlightDimmerColor
backgroundColor: inAlternateCycle ? Theme.highlightDimmerColor : Theme.highlightColor
anchors.horizontalCenter: parent.horizontalCenter
progressValue: imagePreview.progress
}
}
}
}
Component {
id: failedLoading
Text {
@ -299,17 +290,26 @@ FullscreenContentPage {
color: Theme.highlightColor
}
}
IconButton {
y: Theme.paddingLarge
anchors.right: parent.right
anchors.rightMargin: Theme.horizontalPageMargin
icon.source: "image://theme/icon-m-dismiss"
onClicked: pageStack.pop()
}
IconButton {
visible: true
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: Theme.paddingLarge
anchors.bottomMargin: Theme.paddingLarge*1.5
icon.source: "image://theme/icon-m-device-download"
icon.source: "image://theme/icon-m-cloud-download"
onClicked: {
var filename = mediaURL.split("/");
FileDownloader.downloadFile(mediaURL, filename[filename.length-1]);
}
}
VerticalScrollDecorator { flickable: imageFlickable }
}

View file

@ -51,11 +51,12 @@ DockedPanel {
Timer {
id: autoClose
interval: 6000
interval: 4500
running: false
onTriggered: {
root.hide()
stop()
}
}
}

View file

@ -32,14 +32,16 @@ BackgroundItem {
}
MouseArea {
anchors.fill: parent
onClicked: pageStack.push(Qt.resolvedUrl("./../Profile.qml"), {
onClicked: pageStack.push(Qt.resolvedUrl("./../ProfilePage.qml"), {
"display_name": model.account_display_name,
"username": model.account_acct,
"user_id": model.account_id,
"profileImage": model.account_avatar
"profileImage": model.account_avatar,
"profileBackground": model.account_header
})
}
}
Column {
anchors.left: avatar.right
anchors.leftMargin: Theme.paddingLarge
@ -63,6 +65,8 @@ BackgroundItem {
"display_name": model.account_display_name,
"username": model.account_acct,
"user_id": model.account_id,
"profileImage": model.account_avatar
"profileImage": model.account_avatar,
"profileBackground": model.account_header
})
}

View file

@ -146,8 +146,5 @@ Item {
}
}
}
}

View file

@ -1,8 +1,9 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
id: miniheader
id: miniHeader
height: lblName.height
width: parent.width
@ -67,4 +68,5 @@ Item {
rightMargin: Theme.horizontalPageMargin
}
}
}

View file

@ -1,8 +1,9 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
id: ministatus
id: miniStatus
visible: true
height: icon.height+Theme.paddingMedium
width: parent.width
@ -42,7 +43,7 @@ Item {
action = qsTr('followed you');
break;
default:
ministatus.visible = false
miniStatus.visible = false
action = type;
}
return typeof reblog_account_username !== "undefined" ? '@' + reblog_account_username + ' ' + action : ''

View file

@ -2,7 +2,9 @@ import QtQuick 2.0
import Sailfish.Silica 1.0
import QtMultimedia 5.0
Item {
id: myImage
property string type : ""
property string previewURL: ""
property string mediaURL: ""
@ -37,8 +39,6 @@ Item {
progressRec.width = 0;
}
}
MouseArea {
anchors.fill: parent
onClicked: {

View file

@ -3,9 +3,10 @@ import Sailfish.Silica 1.0
import "../../lib/API.js" as Logic
import "."
SilicaListView {
id: myList
property string type;
property string type
property string title
property string vwPlaceholderText: qsTr("Loading")
property string vwPlaceholderHint: qsTr("please wait...")
@ -13,21 +14,19 @@ SilicaListView {
property ListModel mdl: []
property variant params: []
property var locale: Qt.locale()
property bool autoLoadMore : true;
property bool loadStarted : false;
property int scrollOffset;
property bool autoLoadMore: true
property bool loadStarted: false
property int scrollOffset
property string action: ""
property variant vars
property variant conf
property bool notifier : false;
property bool notifier: false
model: mdl
signal notify (string what, int num)
onNotify: {
console.log(what + " - " + num)
}
signal openDrawer (bool setDrawer)
onOpenDrawer: {
//console.log("Open drawer: " + setDrawer)
@ -37,7 +36,6 @@ SilicaListView {
console.log("LIST send signal emitted with notice: " + notice)
}
BusyIndicator {
size: BusyIndicatorSize.Large
running: myList.model.count === 0 && !viewPlaceHolder.visible
@ -49,8 +47,6 @@ SilicaListView {
description: myList.description
}
ViewPlaceholder {
id: viewPlaceHolder
enabled: model.count === 0
@ -62,10 +58,16 @@ SilicaListView {
MenuItem {
text: qsTr("Settings")
onClicked: {
pageStack.push(Qt.resolvedUrl("../Settings.qml"), {})
pageStack.push(Qt.resolvedUrl("../SettingsPage.qml"), {})
}
}
/* MenuItem {
text: qsTr("Open in Browser")
visible: profile_url != ""
onClicked: {
Clipboard.text = profile_url
}
} */
MenuItem {
text: qsTr("Load more")
onClicked: {
@ -95,10 +97,9 @@ SilicaListView {
}
onCountChanged: {
loadStarted = false;
loadStarted = false
/*contentY = scrollOffset
console.log("CountChanged!")*/
}
footer: Item{
@ -122,17 +123,16 @@ SilicaListView {
}
}
onContentYChanged: {
if (Math.abs(contentY - scrollOffset) > Theme.itemSizeMedium) {
openDrawer(contentY - scrollOffset > 0 ? false : true )
scrollOffset = contentY
}
if(contentY+height > footerItem.y && !loadStarted && autoLoadMore){
loadData("append")
loadStarted = true;
loadStarted = true
}
}
VerticalScrollDecorator {}
WorkerScript {
@ -161,16 +161,16 @@ SilicaListView {
}
}
function loadData(mode){
var p = [];
var p = []
if (params.length)
for(var i = 0; i<params.length; i++)
p.push(params[i])
if (mode === "append" && model.count){
p.push({name: 'max_id', data: model.get(model.count-1).id});
p.push({name: 'max_id', data: model.get(model.count-1).id})
}
if (mode === "prepend" && model.count){
p.push({name:'since_id', data: model.get(0).id});
p.push({name:'since_id', data: model.get(0).id})
}
var msg = {
@ -182,7 +182,7 @@ SilicaListView {
};
console.log(JSON.stringify(msg))
if (type !== "")
worker.sendMessage(msg);
worker.sendMessage(msg)
}
}

View file

@ -2,13 +2,13 @@ import QtQuick 2.0
import Sailfish.Silica 1.0
import QtGraphicalEffects 1.0
SilicaGridView {
signal slideshowShow(int vIndex);
signal slideshowIndexChanged(int vIndex);
signal slideshowShow(int vIndex)
signal slideshowIndexChanged(int vIndex)
onSlideshowIndexChanged: {
navigateTo(vIndex)
}
id: gridView
property bool isPortrait: false
ListModel {
@ -55,7 +55,6 @@ SilicaGridView {
cellWidth: isPortrait ? gridView.width : gridView.width / model.count
cellHeight: isPortrait ? gridView.height/model.count : gridView.height
delegate: BackgroundItem {
clip: true
id: rectangle
@ -78,7 +77,6 @@ SilicaGridView {
anchors.horizontalCenter: parent.horizontalCenter
color: Theme.highlightColor
}
GlassItem {
id: effect2
visible: isPortrait && unread
@ -90,18 +88,10 @@ SilicaGridView {
anchors.verticalCenter: parent.verticalCenter
color: Theme.highlightColor
}
OpacityRampEffect {
sourceItem: label
offset: 0.5
}
/*Image {
source: model.icon + (highlighted
? Theme.highlightColor
: (model.active ? Theme.primaryColor : Theme.secondaryHighlightColor))
anchors.centerIn: parent
}*/
ColorOverlay {
anchors.fill: image
source: image
@ -170,4 +160,5 @@ SilicaGridView {
}
VerticalScrollDecorator {}
}

View file

@ -1,13 +1,14 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
id: header
property int value: 0;
property string title: "";
property string description: "";
property string image: "";
property string bg: "";
id: profileHeader
property int value: 0
property string title: ""
property string description: ""
property string image: ""
property string bg: ""
width: parent.width
height: icon.height + Theme.paddingLarge*2

View file

@ -2,32 +2,30 @@ import QtQuick 2.0
import Sailfish.Silica 1.0
import QtGraphicalEffects 1.0
BackgroundItem {
signal send (string notice)
signal navigateTo(string link)
id: delegate
//property string text: "0"
width: parent.width
signal navigateTo(string link)
height: lblText.paintedHeight + (lblText.text.length > 0 ? Theme.paddingLarge : 0 )+ lblName.paintedHeight + (type.length ? Theme.paddingLarge + iconRT.height : 0) + Theme.paddingLarge
Image {
id: iconRT
y: Theme.paddingLarge
anchors {
right: avatar.right
}
anchors.right: avatar.right
visible: type.length
width: Theme.iconSizeExtraSmall
height: width
source: "../../images/boosted.svg"
}
Label {
id: lblRtByName
visible: type.length
anchors {
left: lblName.left
bottom: iconRT.bottom
}
anchors.left: lblName.left
anchors.bottom: iconRT.bottom
text: {
var action;
switch(type){
@ -45,10 +43,10 @@ BackgroundItem {
}
return '@' + retweetScreenName + ' ' + action
}
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.secondaryColor
}
Image {
id: avatar
x: Theme.horizontalPageMargin
@ -62,28 +60,28 @@ BackgroundItem {
MouseArea {
anchors.fill: parent
onClicked: {
pageStack.push(Qt.resolvedUrl("../Profile.qml"), {
pageStack.push(Qt.resolvedUrl("../ProfilePage.qml"), {
"display_name": account_display_name,
"username": account_username,
"profileImage": account_avatar
})
}
}
}
Label {
id: lblName
text: account_display_name
font.weight: Font.Bold
font.pixelSize: Theme.fontSizeSmall
color: (pressed ? Theme.highlightColor : Theme.primaryColor)
anchors {
top: avatar.top
topMargin: 0
left: avatar.right
leftMargin: Theme.paddingMedium
}
text: account_display_name
font.weight: Font.Bold
font.pixelSize: Theme.fontSizeSmall
color: (pressed ? Theme.highlightColor : Theme.primaryColor)
}
Image {
@ -103,7 +101,6 @@ BackgroundItem {
: Theme.primaryColor)
}
Label {
id: lblScreenName
anchors {
@ -117,6 +114,7 @@ BackgroundItem {
font.pixelSize: Theme.fontSizeExtraSmall
color: (pressed ? Theme.secondaryHighlightColor : Theme.secondaryColor)
}
Label {
function timestamp() {
var txt = Format.formatDate(created_at, Formatter.Timepoint)
@ -135,7 +133,7 @@ BackgroundItem {
}
}
Label {
Text {
id: lblText
anchors {
left: lblName.left
@ -154,20 +152,16 @@ BackgroundItem {
"profileImage": ""
})
} else if (link[0] === "#") {
pageStack.pop(pageStack.find(function(page) {
var check = page.isFirstPage === true;
if (check)
page.onLinkActivated(link)
return check;
}));
send(link)
} else {
Qt.openUrlExternally(link);
}
}
text: content
textFormat: Text.RichText
@ -178,10 +172,12 @@ BackgroundItem {
color: (pressed ? Theme.highlightColor : Theme.primaryColor)
}
onClicked: {
pageStack.push(Qt.resolvedUrl("../Conversation.qml"), {
pageStack.push(Qt.resolvedUrl("../ConversationPage.qml"), {
headerTitle: "Conversation",
toot_id: id,
title: account_display_name,
description: '@'+account_username,
toot_url: status_url,
//title: account_display_name,
description: '@'+account_acc,
avatar: account_avatar,
type: "reply"
})

View file

@ -2,12 +2,13 @@ import QtQuick 2.2
import Sailfish.Silica 1.0
import "../../lib/API.js" as Logic
BackgroundItem {
id: delegate
signal send (string notice)
signal navigateTo(string link)
width: parent.width
height: mnu.height + miniHeader.height + (typeof attachments !== "undefined" && attachments.count ? media.height + Theme.paddingLarge + Theme.paddingMedium: Theme.paddingLarge) + lblContent.height + Theme.paddingLarge + (ministatus.visible ? ministatus.height : 0)
height: mnu.height + miniHeader.height + (typeof attachments !== "undefined" && attachments.count ? media.height + Theme.paddingLarge + Theme.paddingMedium: Theme.paddingLarge) + lblContent.height + Theme.paddingLarge + (miniStatus.visible ? miniStatus.height : 0)
Rectangle {
x: 0;
y: 0;
@ -15,11 +16,11 @@ BackgroundItem {
width: parent.width
height: parent.height
opacity: 0.3
color: Theme.highlightBackgroundColor;
color: Theme.highlightBackgroundColor
}
MiniStatus {
id: ministatus
id: miniStatus
anchors {
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
@ -31,8 +32,8 @@ BackgroundItem {
Image {
id: avatar
anchors {
top: ministatus.visible ? ministatus.bottom : parent.top
topMargin: ministatus.visible ? Theme.paddingMedium : Theme.paddingLarge
top: miniStatus.visible ? miniStatus.bottom : parent.top
topMargin: miniStatus.visible ? Theme.paddingMedium : Theme.paddingLarge
left: parent.left
leftMargin: Theme.horizontalPageMargin
}
@ -50,10 +51,11 @@ BackgroundItem {
? Theme.highlightColor
: Theme.primaryColor)
}
MouseArea {
anchors.fill: parent
onClicked: {
pageStack.push(Qt.resolvedUrl("../Profile.qml"), {
pageStack.push(Qt.resolvedUrl("../ProfilePage.qml"), {
"display_name": model.account_display_name,
"username": model.account_acct,
"user_id": model.account_id,
@ -63,6 +65,7 @@ BackgroundItem {
}
}
Image {
id: iconTR
anchors {
@ -75,6 +78,7 @@ BackgroundItem {
height: width
source: "image://theme/icon-s-retweet"
}
Rectangle {
color: Theme.highlightDimmerColor
width: Theme.iconSizeSmall
@ -98,6 +102,7 @@ BackgroundItem {
}
}
}
MiniHeader {
id: miniHeader
anchors {
@ -106,6 +111,7 @@ BackgroundItem {
right: parent.right
}
}
Text {
id: lblContent
anchors {
@ -176,6 +182,7 @@ BackgroundItem {
wrapMode: Text.Wrap
text: model.status_spoiler_text
}
MouseArea {
anchors.fill: parent
onClicked: parent.visible = false;
@ -225,6 +232,7 @@ BackgroundItem {
height: width
source: "image://theme/icon-s-retweet?" + (!model.reblogged ? Theme.highlightColor : Theme.primaryColor)
}
Label {
anchors {
left: icRT.right
@ -236,6 +244,7 @@ BackgroundItem {
color: !model.reblogged ? Theme.highlightColor : Theme.primaryColor
}
}
MenuItem {
enabled: model.type !== "follow"
text: typeof model.favourited !== "undefined" && model.favourited ? qsTr("Unfavorite") : qsTr("Favorite")
@ -251,6 +260,7 @@ BackgroundItem {
model.favourites_count = !status ? model.favourites_count+1 : (model.favourites_count > 0 ? model.favourites_count-1 : model.favourites_count);
model.favourited = !model.favourited
}
Image {
id: icFA
anchors {
@ -262,6 +272,7 @@ BackgroundItem {
height: width
source: "image://theme/icon-s-favorite?" + (!model.favourited ? Theme.highlightColor : Theme.primaryColor)
}
Label {
anchors {
left: icFA.right
@ -279,7 +290,7 @@ BackgroundItem {
var m = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject');
if (typeof mdl !== "undefined")
m.append(mdl.get(index))
pageStack.push(Qt.resolvedUrl("../Conversation.qml"), {
pageStack.push(Qt.resolvedUrl("../ConversationPage.qml"), {
headerTitle: "Conversation",
toot_id: status_id,
toot_url: status_url,

View file

@ -23,21 +23,21 @@
<context>
<name>Conversation</name>
<message>
<source>Delete</source>
<translation>Delete</translation>
</message>
<message>
<source>Emojis</source>
<translation>Emojis</translation>
</message>
<message>
<source>Tap to insert</source>
<translation>Tap to insert</translation>
<source>Copy Link to Clipboard</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write your warning here</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>What&apos;s on your mind?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Public</source>
<translation type="unfinished"></translation>
@ -54,16 +54,19 @@
<source>Direct</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>What&apos;s on your mind?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Toot sent!</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EmojiPage</name>
<message>
<source>Copy Link to Clipboard</source>
<source>Emojis</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Tap to insert</source>
<translation type="unfinished"></translation>
</message>
</context>