ui-improvements

- open profile picture
- improved media viewer
- improved user suggestion
- better responsive UI design
- code refactoring
- updated settings page
This commit is contained in:
molan-git 2020-06-03 07:34:33 +02:00
parent fd2f317f25
commit 16e211ddb9
23 changed files with 525 additions and 370 deletions

View file

@ -57,6 +57,8 @@ DISTFILES += qml/harbour-tooter.qml \
qml/pages/ProfilePage.qml \
qml/pages/SettingsPage.qml \
qml/pages/components/InfoBanner.qml \
qml/pages/components/MediaFullScreen.qml \
qml/pages/components/ProfileImage.qml \
qml/pages/components/VisualContainer.qml \
qml/pages/components/MiniStatus.qml \
qml/pages/components/MiniHeader.qml \
@ -66,7 +68,6 @@ DISTFILES += qml/harbour-tooter.qml \
qml/pages/components/ProfileHeader.qml \
qml/pages/components/MediaBlock.qml \
qml/pages/components/MyImage.qml \
qml/pages/components/ImageFullScreen.qml \
qml/cover/CoverPage.qml \
qml/pages/MainPage.qml \
qml/pages/LoginPage.qml \

View file

@ -57,7 +57,6 @@ CoverBackground {
horizontalAlignment: Image.AlignLeft
verticalAlignment: Image.AlignBottom
fillMode: Image.PreserveAspectFit
source: "../images/tooter.svg"
}
Timer {
@ -78,6 +77,7 @@ CoverBackground {
}
source: "image://theme/icon-s-alarm?" + Theme.highlightColor
}
Label {
id: notificationsLbl
anchors {
@ -112,8 +112,11 @@ CoverBackground {
CoverAction {
iconSource: "image://theme/icon-cover-new"
onTriggered: {
pageStack.push(Qt.resolvedUrl("./../pages/Conversation.qml"), {})
appWindow.activate();
pageStack.push(Qt.resolvedUrl("./../pages/ConversationPage.qml"), {
headerTitle: qsTr("New Toot"),
type: "new"
})
appWindow.activate()
}
}
}

View file

@ -47,7 +47,7 @@ ApplicationWindow {
Logic.conf['notificationLastID'] = 0
if (Logic.conf['instance']) {
Logic.api = new Logic.MastodonAPI({
Logic.api = Logic.mastodonAPI({
"instance": Logic.conf['instance'],
"api_user_token": ""
})

View file

@ -3,7 +3,7 @@
// do whatever you want with it
// but please don't hurt it (and keep this header)
var MastodonAPI = function(config) {
var mastodonAPI = function(config) {
var apiBase = config.instance + "/api/v1/";
return {
setConfig: function (key, value) {
@ -236,7 +236,7 @@ var MastodonAPI = function(config) {
};
// node.js
if (typeof module !== 'undefined') { module.exports = MastodonAPI; };
if (typeof module !== 'undefined') { module.exports = mastodonAPI; };
String.prototype.replaceAll = function(search, replacement) {
var target = this;

View file

@ -22,7 +22,7 @@ WorkerScript.onMessage = function(msg) {
if (typeof msg.conf['loadImages'] !== "undefined")
loadImages = msg.conf['loadImages']
var API = MastodonAPI({ instance: msg.conf.instance, api_user_token: msg.conf.api_user_token});
var API = mastodonAPI({ instance: msg.conf.instance, api_user_token: msg.conf.api_user_token});
if (msg.method === "POST"){
API.post(msg.action, msg.params, function(data) {
if (msg.bgAction){
@ -298,13 +298,13 @@ function addEmojis(item, data){
var emoji, i;
for (i = 0; i < data["emojis"].length; i++){
emoji = data["emojis"][i];
item['content'] = item['content'].replaceAll(":"+emoji.shortcode+":", "<img src=\"" + emoji.static_url+"\" align=\"middle\" width=\"24\" height=\"24\">")
item['content'] = item['content'].replaceAll(":"+emoji.shortcode+":", "<img src=\"" + emoji.static_url+"\" align=\"top\" width=\"50\" height=\"50\">")
//console.log(JSON.stringify(data["emojis"][i]))
}
if (data["reblog"])
for (i = 0; i < data["reblog"]["emojis"].length; i++){
emoji = data["reblog"]["emojis"][i];
item['content'] = item['content'].replaceAll(":"+emoji.shortcode+":", "<img src=\"" + emoji.static_url+"\" align=\"middle\" width=\"24\" height=\"24\">")
item['content'] = item['content'].replaceAll(":"+emoji.shortcode+":", "<img src=\"" + emoji.static_url+"\" align=\"top\" width=\"50\" height=\"50\">")
}
return item;

View file

@ -43,10 +43,6 @@ Page {
}
}
InfoBanner {
id: sentBanner
}
ListModel {
id: mediaModel
onCountChanged: {
@ -73,11 +69,13 @@ Page {
title: headerTitle // pageTitle pushed from MainPage.qml or VisualContainer.qml
}
clip: true
anchors {
top: parent.top
bottom: panel.top
left: parent.left
right: parent.right
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: if (panel.open == true) {
panel.top
} else {
hiddenPanel.top
}
model: mdl
section {
@ -100,12 +98,7 @@ Page {
}
PullDownMenu {
visible: type == "reply" && toot_url != ""
/* MenuItem {
text: qsTr("Open in Browser")
onClicked: Qt.openUrlExternally(toot_url);
} */
// ! url isn't always fetched. Needs a solution.
visible: type === "reply" && toot_url !== ""
MenuItem {
text: qsTr("Copy Link to Clipboard")
onClicked: Clipboard.text = toot_url;
@ -121,16 +114,17 @@ Page {
anchors.right: panel.right
anchors.top: parent.top
height: implicitHeight
//height: suggestedModel.count > 6 ? Theme.itemSizeMedium * 6 : Theme.itemSizeMedium * suggestedModel.count
color: Theme.highlightDimmerColor
SilicaListView {
rotation: 180
anchors.fill: parent
model: suggestedModel
clip: true
quickScroll: false
VerticalScrollDecorator {}
delegate: ItemUser {
rotation: 180
onClicked: {
var start = toot.cursorPosition
while (toot.text[start] !== "@" && start > 0) {
@ -149,24 +143,20 @@ Page {
}
}
onCountChanged: {
positionViewAtIndex(suggestedModel.count - 1, ListView.End)
positionViewAtBeginning(suggestedModel.count - 1, ListView.Beginning)
}
}
}
DockedPanel {
id: panel
open: true
//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
open: true
animationDuration: 200
Rectangle {
width: parent.width
@ -260,8 +250,12 @@ Page {
IconButton {
id: btnSmileys
property string selection
onSelectionChanged: {
console.log(selection)
opacity: 0.7
icon {
color: Theme.highlightColor
width: Theme.iconSizeSmallPlus
fillMode: Image.PreserveAspectFit
source: "../../qml/images/emojiselect.svg"
}
anchors {
top: warningContent.bottom
@ -269,8 +263,9 @@ Page {
right: parent.right
rightMargin: Theme.paddingSmall
}
opacity: 0.6
icon.source: "../../qml/images/emojiselect.svg"
onSelectionChanged: {
console.log(selection)
}
onClicked: pageStack.push(emojiSelect)
}
@ -501,12 +496,12 @@ Page {
}
BackgroundItem {
id: showPanel
id: hiddenPanel
visible: !panel.open
height: Theme.paddingMedium
height: Theme.paddingLarge * 0.5
width: parent.width
opacity: enabled ? 1.0 : 0.0
Behavior on opacity { FadeAnimator {} }
opacity: enabled ? 0.6 : 0.0
Behavior on opacity { FadeAnimator { duration: 400 } }
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
@ -518,8 +513,18 @@ Page {
}
Rectangle {
id: hiddenPanelBackground
width: parent.width
height: progressBarShowPanel.height
height: parent.height
color: Theme.highlightBackgroundColor
opacity: 0.4
anchors.fill: parent
}
Rectangle {
id: progressBarBackground
width: parent.width
height: progressBarHiddenPanel.height
color: Theme.highlightBackgroundColor
opacity: 0.2
anchors {
@ -530,19 +535,7 @@ Page {
}
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
id: progressBarHiddenPanel
width: toot.text.length ? panel.width * (toot.text.length / tootMaxChar) : 0
height: Theme.itemSizeSmall * 0.05
color: Theme.highlightBackgroundColor
@ -559,4 +552,8 @@ Page {
id: emojiSelect
}
InfoBanner {
id: sentBanner
}
}

View file

@ -6,6 +6,7 @@ import "./components/"
Page {
id: mainPage
property bool isFirstPage: true
property bool isTablet: true //Screen.sizeCategory >= Screen.Large
@ -39,6 +40,7 @@ Page {
height: parent.itemHeight
onOpenDrawer: infoPanel.open = setDrawer
}
MyList{
id: tlNotifications;
title: qsTr("Notifications")
@ -49,6 +51,7 @@ Page {
height: parent.itemHeight
onOpenDrawer: infoPanel.open = setDrawer
}
MyList{
id: tlLocal;
title: qsTr("Local")
@ -59,6 +62,7 @@ Page {
height: parent.itemHeight
onOpenDrawer: infoPanel.open = setDrawer
}
MyList{
id: tlPublic;
title: qsTr("Federated")
@ -68,6 +72,7 @@ Page {
height: parent.itemHeight
onOpenDrawer: infoPanel.open = setDrawer
}
Item {
id: tlSearch;
width: parent.itemWidth
@ -84,6 +89,7 @@ Page {
id: loader
anchors.fill: parent
}
Column {
id: headerContainer
width: tlSearch.width
@ -103,6 +109,7 @@ Page {
}
}
}
Component {
id: loading
BusyIndicator {
@ -111,6 +118,7 @@ Page {
running: true
}
}
Component {
id: tagListComponent
MyList {
@ -135,6 +143,7 @@ Page {
}
}
}
Component {
id: userListComponent
MyList {
@ -172,9 +181,7 @@ Page {
}
}
}
}
}
SlideshowView {
@ -187,12 +194,9 @@ Page {
onCurrentIndexChanged: {
navigation.slideshowIndexChanged(currentIndex)
}
anchors {
fill: parent
leftMargin: 0
top: parent.top
topMargin: 0
rightMargin: mainPage.isPortrait ? 0 : infoPanel.visibleSize
bottomMargin: mainPage.isPortrait ? infoPanel.visibleSize : 0
}
@ -209,11 +213,9 @@ Page {
icon.source: "image://theme/icon-l-add"
anchors {
right: (mainPage.isPortrait ? parent.right : infoPanel.left)
rightMargin: Theme.paddingLarge
bottom: (mainPage.isPortrait ? infoPanel.top : parent.bottom)
margins: {
left: Theme.paddingLarge
bottom: Theme.paddingLarge
}
bottomMargin: Theme.paddingLarge
}
onClicked: {
pageStack.push(Qt.resolvedUrl("ConversationPage.qml"), {
@ -242,6 +244,7 @@ Page {
Qt.openUrlExternally(href);
}
}
Component.onCompleted: {
console.log("aaa")
}

View file

@ -7,6 +7,7 @@ import QtGraphicalEffects 1.0
Page {
id: profilePage
property ListModel tweets
property string display_name: ""
property string username: ""
@ -141,27 +142,28 @@ Page {
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
anchors {
top: parent.top
bottom: profileExpander.top
left: parent.left
right: parent.right
}
}
// ProfilePage ExpandingSection
ExpandingSectionGroup {
id: expander
id: profileExpander
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
ExpandingSection {
id: expandingSection1
title: qsTr("About")
@ -172,22 +174,23 @@ Page {
Rectangle {
id: txtContainer
width: expander.width
width: parent.width
height: Math.min(txtNote.height, parent.height*0.488)
color: "transparent"
visible: {
if ((note.text === "") && (note.text === "<p></p>") ) {
if ((note.text === "") || (note.text === "<p></p>") ) {
false
} else {
true
}
}
SilicaListView {
id: txtFlickable
anchors.fill: txtContainer
anchors.fill: parent
clip: true
quickScroll: false
VerticalScrollDecorator { flickable: txtNote }
VerticalScrollDecorator {}
Text {
id: txtNote
@ -228,6 +231,7 @@ Page {
anchors.horizontalCenter: parent.horizontalCenter
anchors.leftMargin: Theme.paddingLarge
anchors.rightMargin: Theme.paddingLarge
Text {
id: txtFollowers
visible: followers_count ? true : false
@ -236,6 +240,7 @@ Page {
color: Theme.highlightColor
wrapMode: Text.Wrap
}
Text {
id: txtFollowing
visible: following_count ? true : false
@ -244,6 +249,7 @@ Page {
color: Theme.highlightColor
wrapMode: Text.Wrap
}
Text {
id: txtStatuses
visible: statuses_count ? true : false
@ -252,6 +258,7 @@ Page {
color: Theme.highlightColor
wrapMode: Text.Wrap
}
/*Text {
id: txtFavourites
visible: favourites_count ? true : false
@ -304,11 +311,12 @@ Page {
// 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)
color: (muting ? palette.errorColor : palette.primaryColor)
onClicked: {
var msg = {
'method' : 'POST',
@ -319,6 +327,7 @@ Page {
worker.sendMessage(msg);
}
}
Button {
id: btnBlock
preferredWidth: Theme.buttonWidthSmall

View file

@ -5,33 +5,86 @@ import "../lib/API.js" as Logic
Page {
id: settingsPage
allowedOrientations: Orientation.All
SilicaFlickable {
anchors.fill: parent
contentHeight: column.height + Theme.paddingLarge
contentWidth: parent.width
anchors.fill: parent
RemorsePopup { id: remorsePopup }
VerticalScrollDecorator {}
Column {
id: column
spacing: Theme.paddingSmall
spacing: Theme.paddingMedium
width: parent.width
PageHeader {
title: qsTr("Settings")
}
Column {
// No spacing in this column
width: parent.width
SectionHeader { text: "Options"}
IconTextSwitch {
text: qsTr("Load Images in Toots")
description: qsTr("Disable this option if you want to preserve your data connection")
icon.source: "image://theme/icon-m-image"
enabled: true
checked: typeof Logic.conf['loadImages'] !== "undefined" && Logic.conf['loadImages']
onClicked: {
Logic.conf['loadImages'] = checked
}
}
IconTextSwitch {
text: qsTr("Use smaller Font Size in Toots")
description: qsTr("Enable this option if you prefer to use a smaller font size in displayed Toots")
icon.source: "image://theme/icon-m-font-size"
enabled: false
//checked: typeof Logic.conf['loadImages'] !== "undefined" && Logic.conf['loadImages']
//onClicked: {
// Logic.conf['loadImages'] = checked
//}
}
SectionHeader { text: "Account"}
Item {
id: removeAccount
width: parent.width
height: txtRemoveAccount.height + btnRemoveAccount.height + Theme.paddingLarge
anchors {
left: parent.left
leftMargin: Theme.paddingLarge
right: parent.right
rightMargin: Theme.paddingLarge
}
Icon {
id: icnRemoveAccount
color: Theme.secondaryColor
width: Theme.iconSizeMedium
fillMode: Image.PreserveAspectFit
source: Logic.conf['login'] ? "image://theme/icon-m-contact" : "image://theme/icon-m-add"
anchors.right: parent.right
}
Column {
id: clnRemoveAccount
spacing: Theme.paddingMedium
anchors {
left: parent.left
right: icnRemoveAccount.left
}
Button {
id: btnRemoveAccount
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;
checked = false;
timer1.start();
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
remorsePopup.execute(btnRemoveAccount.text, function() {
if (Logic.conf['login']) {
Logic.conf['login'] = false
Logic.conf['instance'] = null;
@ -40,10 +93,7 @@ Page {
pageStack.push(Qt.resolvedUrl("LoginPage.qml"))
})
}
/* busy = true;
checked = false;
timer1.start()
}*/
Timer {
id: timer1
interval: 4700
@ -51,16 +101,23 @@ Page {
}
}
IconTextSwitch {
//enabled: false
checked: typeof Logic.conf['loadImages'] !== "undefined" && Logic.conf['loadImages']
text: qsTr("Load images in toots")
description: qsTr("Disable this option if you want to preserve your data connection")
icon.source: "image://theme/icon-m-image"
onClicked: {
Logic.conf['loadImages'] = checked
Label {
id: txtRemoveAccount
text: Logic.conf['login'] ? qsTr("Deauthorize this app from using your account and remove account data from phone") : qsTr("Authorize this app to access your Mastodon account")
font.pixelSize: Theme.fontSizeExtraSmall
wrapMode: Text.Wrap
color: Theme.secondaryColor
anchors {
left: parent.left
leftMargin: Theme.paddingLarge * 1.9
right: parent.right
rightMargin: Theme.paddingLarge * 1.2
}
}
}
}
/* SectionHeader { text: "Support"}
IconTextSwitch {
text: qsTr("Translate")
@ -77,8 +134,7 @@ Page {
interval: 4700
onTriggered: parent.busy = false
}
}
}
} */
SectionHeader {
text: qsTr("Credits")
@ -89,52 +145,61 @@ Page {
anchors {
left: parent.left
right: parent.right
rightMargin: Theme.horizontalPageMargin
rightMargin: Theme.paddingLarge
}
Repeater {
model: ListModel {
ListElement {
name: "Duško Angirević"
desc: qsTr("UI/UX design and development")
mastodon: "dysko@mastodon.social"
mail: ""
}
ListElement {
name: "Miodrag Nikolić"
desc: qsTr("Visual identity")
mastodon: ""
mail: "micotakis@gmail.com"
}
ListElement {
name: "Molan"
desc: qsTr("Development and translations")
mastodon: "molan@fosstodon.org"
mail: ""
}
ListElement {
name: "Quentin PAGÈS / Quenti ♏"
desc: qsTr("Occitan & French translation")
mastodon: "Quenti@framapiaf.org"
mail: ""
}
ListElement {
name: "Luchy Kon / dashinfantry"
desc: qsTr("Chinese translation")
mastodon: ""
mail: "dashinfantry@gmail.com"
}
ListElement {
name: "André Koot"
desc: qsTr("Dutch translation")
mastodon: "meneer@mastodon.social"
mail: "https://twitter.com/meneer"
}
ListElement {
name: "CarmenFdez"
desc: qsTr("Spanish translation")
mastodon: ""
mail: ""
}
ListElement {
name: "Mohamed-Touhami MAHDI"
desc: qsTr("Added README file")
@ -146,31 +211,29 @@ Page {
Item {
width: parent.width
height: Theme.itemSizeMedium
IconButton {
id: btn
icon.source: "image://theme/" + (model.mastodon !== "" ? "icon-m-outline-chat" : "icon-m-mail") + "?" + (pressed
? Theme.highlightColor : Theme.primaryColor)
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
}
icon.source: "image://theme/" + (model.mastodon !== "" ? "icon-m-contact" : "icon-m-mail") + "?" + (pressed
? Theme.highlightColor
: Theme.primaryColor)
onClicked: {
if (model.mastodon !== ""){
var m = Qt.createQmlObject('import QtQuick 2.0; ListModel { }', Qt.application, 'InternalQmlObject');
pageStack.push(Qt.resolvedUrl("ConversationPage.qml"), {
toot_id: 0,
title: model.name,
headerTitle: "Mention",
description: '@'+model.mastodon,
avatar: "",
mdl: m,
type: "reply"
type: "new"
})
} else {
Qt.openUrlExternally("mailto:"+model.mail);
}
}
}
Column {
anchors {
verticalCenter: parent.verticalCenter
@ -183,9 +246,10 @@ Page {
Label {
id: lblName
text: model.name
color: Theme.secondaryColor
color: Theme.highlightColor
font.pixelSize: Theme.fontSizeSmall
}
Label {
text: model.desc
color: Theme.secondaryHighlightColor

View file

@ -4,6 +4,7 @@ import Sailfish.Silica 1.0
Component {
id: emojiComponent
Dialog {
id: emoticonsDialog
canAccept: false //selector.currentIndex >= 0
@ -13,20 +14,17 @@ Component {
// 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")
}
cellWidth: gridView.width / 6
cellHeight: cellWidth
anchors.fill: parent
model: ListModel {
id: listEmojis
ListElement { section: "smileys"; glyph: "😁" }
ListElement { section: "smileys"; glyph: "😂" }
ListElement { section: "smileys"; glyph: "😃" }
@ -142,11 +140,12 @@ Component {
delegate: BackgroundItem {
width: gridView.cellWidth
height: gridView.cellHeight
Label {
anchors.centerIn: parent
color: (highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
font.pixelSize: Theme.fontSizeLarge
text: glyph
font.pixelSize: Theme.fontSizeLarge
color: (highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
anchors.centerIn: parent
}
onClicked: {
var cursorPosition = toot.cursorPosition
@ -158,6 +157,8 @@ Component {
emoticonsDialog.accept()
}
}
VerticalScrollDecorator {flickable: listEmojis }
}
}

View file

@ -4,28 +4,24 @@ import Sailfish.Silica 1.0
DockedPanel {
id: root
z: 100
dock: Dock.Top
width: parent.width
height: content.height
dock: Dock.Top
Rectangle {
id: content
width: root.width
height: infoLabel.height + 5*Theme.paddingMedium
//anchors.topMargin: 20
color: Theme.highlightBackgroundColor
opacity: 1.0
width: root.width
height: infoLabel.height + 3*Theme.paddingMedium
Label {
id: infoLabel
text : ""
color: Theme.primaryColor
font.family: Theme.fontFamilyHeading
font.pixelSize: Theme.fontSizeMedium
//font.weight: Font.Bold
width: parent.width
color: Theme.highlightColor
wrapMode: Text.WrapAnywhere
width: parent.width
anchors {
left: parent.left
leftMargin: Theme.horizontalPageMargin*2
@ -34,6 +30,7 @@ DockedPanel {
verticalCenter: parent.verticalCenter
}
}
MouseArea {
anchors.fill: parent
onClicked: {

View file

@ -4,9 +4,11 @@ import Sailfish.Silica 1.0
BackgroundItem {
id: delegate
signal openUser (string notice)
height: Theme.itemSizeMedium
width: parent.width
height: Theme.itemSizeMedium
Rectangle {
id: avatar
@ -23,6 +25,7 @@ BackgroundItem {
anchors.fill: parent
source: model.account_avatar
}
BusyIndicator {
size: BusyIndicatorSize.Small
opacity: img.status === Image.Ready ? 0.0 : 1.0
@ -30,6 +33,7 @@ BackgroundItem {
running: avatar.status !== Image.Ready;
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: pageStack.push(Qt.resolvedUrl("./../ProfilePage.qml"), {

View file

@ -4,10 +4,12 @@ import QtMultimedia 5.0
Item {
id: holder
property ListModel model
property double wRatio : 16/9
property double hRatio : 9/16
id: holder
width: width
height: height
Component.onCompleted: {
@ -92,6 +94,7 @@ Item {
}
}
}
MyImage {
id: placeholder2
width: 2
@ -110,6 +113,7 @@ Item {
}
}
}
MyImage {
id: placeholder3
width: 2
@ -128,6 +132,7 @@ Item {
}
}
}
MyImage {
id: placeholder4
width: 2

View file

@ -4,12 +4,13 @@ import QtMultimedia 5.0
FullscreenContentPage {
id: mediaPage
allowedOrientations: Orientation.All
property string type: ""
property string previewURL: ""
property string mediaURL: ""
id: imagePage
allowedOrientations: Orientation.All
Component.onCompleted: function(){
console.log(type)
console.log(previewURL)
@ -29,8 +30,6 @@ FullscreenContentPage {
id: videoFlickable
visible: false
anchors.fill: parent
contentWidth: imageContainer.width
contentHeight: imageContainer.height
clip: true
Image {
@ -57,7 +56,6 @@ FullscreenContentPage {
return;
}
}
onPlaybackStateChanged: {
console.log(playbackState)
switch (playbackState){
@ -72,7 +70,6 @@ FullscreenContentPage {
return;
}
}
onPositionChanged: function(){
//console.log(duration)
//console.log(bufferProgress)
@ -84,17 +81,18 @@ FullscreenContentPage {
playerProgress.value = position
}
}
onStopped: function(){
stop()
}
IconButton {
id: playerIcon
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.leftMargin: Theme.paddingLarge
anchors.bottomMargin: Theme.paddingLarge*1.5
anchors {
left: parent.left
bottom: parent.bottom
leftMargin: Theme.horizontalPageMargin
bottomMargin: Theme.horizontalPageMargin
}
icon.source: "image://theme/icon-m-play"
onClicked: function() {
if (video.playbackState === MediaPlayer.PlayingState)
@ -105,26 +103,15 @@ FullscreenContentPage {
}
ProgressBar {
indeterminate: true
id: playerProgress
anchors.left: playerIcon.right
anchors.right: videoDlBtn.left
anchors.verticalCenter: playerIcon.verticalCenter
anchors.leftMargin: 0
anchors.bottomMargin: Theme.paddingLarge*1.5
}
IconButton {
id: videoDlBtn
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: Theme.paddingLarge
anchors.bottomMargin: Theme.paddingLarge*1.5
icon.source: "image://theme/icon-m-cloud-download"
onClicked: {
var filename = mediaURL.split("/");
FileDownloader.downloadFile(mediaURL, filename[filename.length-1]);
}
indeterminate: true
width: 400
anchors {
verticalCenter: playerIcon.verticalCenter
left: playerIcon.right
right: parent.right
rightMargin: Theme.horizontalPageMargin + Theme.iconSizeMedium
bottomMargin: Theme.horizontalPageMargin
}
Rectangle {
@ -135,16 +122,17 @@ FullscreenContentPage {
color: Theme.highlightDimmerColor
height: videoError.height + 2*Theme.paddingMedium
width: parent.width
Label {
anchors.centerIn: parent
id: videoError
width: parent.width - 2*Theme.paddingMedium
wrapMode: Text.Wrap
height: contentHeight
visible: false;
font.pixelSize: Theme.fontSizeSmall;
visible: false
text: video.errorString
font.pixelSize: Theme.fontSizeSmall
color: Theme.highlightColor
wrapMode: Text.Wrap
width: parent.width - 2*Theme.paddingMedium
height: contentHeight
anchors.centerIn: parent
}
}
@ -159,15 +147,17 @@ FullscreenContentPage {
}
}
}
}
Flickable {
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();
anchors.fill: parent
onHeightChanged: if (imagePreview.status === Image.Ready) {
imagePreview.fitToScreen()
}
Item {
id: imageContainer
@ -176,18 +166,21 @@ FullscreenContentPage {
Image {
id: imagePreview
property real prevScale
function fitToScreen() {
scale = Math.min(imageFlickable.width / width, imageFlickable.height / height, 1)
scale = Math.min(imageFlickable.width / width, imageFlickable.height / height, imageFlickable.width, imageFlickable.height)
pinchArea.minScale = scale
prevScale = scale
}
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
cache: true
asynchronous: true
sourceSize.height: 2000;
smooth: true // might slower performance
sourceSize.width: mediaPage.width
smooth: false
anchors.centerIn: parent
onStatusChanged: {
if (status == Image.Ready) {
fitToScreen()
@ -195,15 +188,6 @@ FullscreenContentPage {
}
}
NumberAnimation {
id: loadedAnimation
target: imagePreview
property: "opacity"
duration: 250
from: 0; to: 1
easing.type: Easing.InOutQuad
}
onScaleChanged: {
if ((width * scale) > imageFlickable.width) {
var xoff = (imageFlickable.width / 2 + imageFlickable.contentX) * scale / prevScale;
@ -215,19 +199,30 @@ FullscreenContentPage {
}
prevScale = scale
}
NumberAnimation {
id: loadedAnimation
target: imagePreview
property: "opacity"
duration: 250
from: 0; to: 1
easing.type: Easing.InOutQuad
}
}
}
PinchArea {
id: pinchArea
opacity: 0.3
property real minScale: 1.0
property real maxScale: 3.0
anchors.fill: parent
enabled: imagePreview.status === Image.Ready
pinch.target: imagePreview
pinch.minimumScale: minScale * 0.5 // This is to create "bounce back effect"
pinch.maximumScale: maxScale * 1.5 // when over zoomed
pinch.maximumScale: maxScale * 1.5 // when over zoomed}
onPinchFinished: {
imageFlickable.returnToBounds()
@ -249,7 +244,6 @@ FullscreenContentPage {
from: imagePreview.scale
}
}
}
Loader {
anchors.centerIn: parent
@ -266,18 +260,16 @@ FullscreenContentPage {
Component {
id: loadingIndicator
Item {
width: mediaPage.width
height: childrenRect.height
width: imagePage.width
ProgressCircle {
id: imageLoadingIndicator
progressValue: imagePreview.progress
progressColor: inAlternateCycle ? Theme.highlightColor : Theme.highlightDimmerColor
backgroundColor: inAlternateCycle ? Theme.highlightDimmerColor : Theme.highlightColor
anchors.horizontalCenter: parent.horizontalCenter
progressValue: imagePreview.progress
}
}
}
}
@ -285,31 +277,41 @@ FullscreenContentPage {
Component {
id: failedLoading
Text {
font.pixelSize: Theme.fontSizeSmall;
text: qsTr("Error loading")
font.pixelSize: Theme.fontSizeSmall;
color: Theme.highlightColor
}
}
}
VerticalScrollDecorator { flickable: imageFlickable }
}
IconButton {
y: Theme.paddingLarge
anchors.right: parent.right
anchors.rightMargin: Theme.horizontalPageMargin
id: dismissBtn
icon.source: "image://theme/icon-m-dismiss"
anchors {
top: parent.top
topMargin: Theme.horizontalPageMargin
right: parent.right
rightMargin: Theme.horizontalPageMargin
}
onClicked: pageStack.pop()
}
IconButton {
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: Theme.paddingLarge
anchors.bottomMargin: Theme.paddingLarge*1.5
id: mediaDlBtn
anchors {
right: parent.right
rightMargin: Theme.horizontalPageMargin
bottom: parent.bottom
bottomMargin: Theme.horizontalPageMargin
}
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

@ -37,26 +37,24 @@ Item {
width: account_locked ? Theme.iconSizeExtraSmall*0.8 : 0
opacity: 0.8
height: width
source: "image://theme/icon-s-secure?" + (pressed
? Theme.highlightColor
: Theme.primaryColor)
source: "image://theme/icon-s-secure?" + (pressed ? Theme.highlightColor : Theme.primaryColor)
}
Label {
id: lblScreenName
truncationMode: TruncationMode.Fade
text: '@'+account_username
font.pixelSize: Theme.fontSizeExtraSmall
color: (pressed ? Theme.secondaryHighlightColor : Theme.secondaryColor)
anchors {
left: iconVerified.right
right: lblDate.left
leftMargin: Theme.paddingMedium
baseline: lblName.baseline
}
truncationMode: TruncationMode.Fade
text: '@'+account_username
font.pixelSize: Theme.fontSizeExtraSmall
color: (pressed ? Theme.secondaryHighlightColor : Theme.secondaryColor)
}
Label {
Label {
id: lblDate
color: (pressed ? Theme.highlightColor : Theme.primaryColor)
text: Format.formatDate(created_at, new Date() - created_at < 60*60*1000 ? Formatter.DurationElapsedShort : Formatter.TimeValueTwentyFourHours)
@ -64,8 +62,8 @@ Item {
horizontalAlignment: Text.AlignRight
anchors {
right: parent.right
baseline: lblName.baseline
rightMargin: Theme.horizontalPageMargin
baseline: lblName.baseline
}
}

View file

@ -9,6 +9,10 @@ Item {
width: parent.width
Image {
id: icon
visible: type.length
width: Theme.iconSizeExtraSmall
height: width
source: typeof typeIcon !== "undefined" ? typeIcon : ""
anchors {
top: parent.top
topMargin: Theme.paddingMedium
@ -16,12 +20,8 @@ Item {
left: parent.left
leftMargin: Theme.horizontalPageMargin + Theme.iconSizeMedium - width
}
visible: type.length
width: Theme.iconSizeExtraSmall
height: width
source: typeof typeIcon !== "undefined" ? typeIcon : ""
}
Label {
id: lblRtByName
visible: type.length
@ -48,7 +48,6 @@ Item {
}
return typeof reblog_account_username !== "undefined" ? '@' + reblog_account_username + ' ' + action : ''
}
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.highlightColor
}

View file

@ -5,33 +5,38 @@ import QtMultimedia 5.0
Item {
id: myImage
property string type : ""
property string previewURL: ""
property string mediaURL: ""
Rectangle {
opacity: 0.2
anchors.fill: parent
color: Theme.highlightDimmerColor
anchors.fill: parent
}
Image {
anchors.centerIn: parent
source: "image://theme/icon-m-image"
anchors.centerIn: parent
}
Rectangle {
id: progressRec
anchors.bottom: parent.bottom
width: 0
height: Theme.paddingSmall
color: Theme.highlightBackgroundColor
anchors.bottom: parent.bottom
}
Image {
id: img
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
asynchronous: true
opacity: status === Image.Ready ? 1.0 : 0.0
Behavior on opacity { FadeAnimator {} }
source: previewURL
fillMode: Image.PreserveAspectCrop
anchors.fill: parent
onProgressChanged: {
if (progress != 1)
progressRec.width = parent.width * progress
@ -39,35 +44,48 @@ Item {
progressRec.width = 0;
}
}
MouseArea {
anchors.fill: parent
onClicked: {
pageStack.push(Qt.resolvedUrl("./ImageFullScreen.qml"), {"previewURL": previewURL, "mediaURL": mediaURL, "type": type})
pageStack.push(Qt.resolvedUrl("./MediaFullScreen.qml"), {
"previewURL": previewURL,
"mediaURL": mediaURL,
"type": type
})
}
}
Image {
id: videoIcon
visible: type === "video" || type === "gifv"
anchors.centerIn: parent
source: "image://theme/icon-l-play"
anchors.centerIn: parent
}
BusyIndicator {
id: mediaLoader
size: BusyIndicatorSize.Large
running: img.status !== Image.Ready
opacity: img.status === Image.Ready ? 0.0 : 1.0
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
Rectangle {
anchors.fill: parent
id: mediaWarning
color: Theme.highlightDimmerColor
visible: typeof status_sensitive != 'undefined' && status_sensitive ? true : false
anchors.fill: parent
Image {
source: "image://theme/icon-l-attention?"+Theme.highlightColor
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: parent.visible = false;
onClicked: parent.visible = false
}
}
}

View file

@ -61,13 +61,7 @@ SilicaListView {
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: {

View file

@ -4,13 +4,16 @@ import QtGraphicalEffects 1.0
SilicaGridView {
id: gridView
property bool isPortrait: false
signal slideshowShow(int vIndex)
signal slideshowIndexChanged(int vIndex)
onSlideshowIndexChanged: {
navigateTo(vIndex)
}
id: gridView
property bool isPortrait: false
ListModel {
id: listModel
ListElement {
@ -20,12 +23,14 @@ SilicaGridView {
active: true
unread: false
}
ListElement {
icon: "image://theme/icon-m-alarm"
slug: "notifications"
name: "Notifications"
active: false
}
ListElement {
icon: "image://theme/icon-m-whereami"
slug: "local"
@ -33,6 +38,7 @@ SilicaGridView {
active: false
unread: false
}
ListElement {
icon: "image://theme/icon-m-website"
slug: "federated"
@ -40,6 +46,7 @@ SilicaGridView {
active: false
unread: false
}
ListElement {
icon: "image://theme/icon-m-search"
slug: "search"
@ -49,15 +56,13 @@ SilicaGridView {
}
}
model: listModel
anchors.fill: parent
currentIndex: -1
cellWidth: isPortrait ? gridView.width : gridView.width / model.count
cellHeight: isPortrait ? gridView.height/model.count : gridView.height
anchors.fill: parent
delegate: BackgroundItem {
clip: true
id: rectangle
clip: true
width: gridView.cellWidth
height: gridView.cellHeight
GridView.onAdd: AddAnimation {
@ -66,88 +71,93 @@ SilicaGridView {
GridView.onRemove: RemoveAnimation {
target: rectangle
}
GlassItem {
id: effect
visible: !isPortrait && unread
dimmed: true
color: Theme.highlightColor
width: Theme.itemSizeMedium
height: Theme.itemSizeMedium
dimmed: true
anchors.bottom: parent.bottom
anchors.bottomMargin: -height/2
anchors.horizontalCenter: parent.horizontalCenter
color: Theme.highlightColor
anchors {
bottom: parent.bottom
bottomMargin: -height/2
horizontalCenter: parent.horizontalCenter
}
}
GlassItem {
id: effect2
visible: isPortrait && unread
dimmed: false
color: Theme.highlightColor
width: Theme.itemSizeMedium
height: Theme.itemSizeMedium
dimmed: false
anchors.right: parent.right;
anchors.rightMargin: -height/2;
anchors.verticalCenter: parent.verticalCenter
color: Theme.highlightColor
anchors {
right: parent.right
rightMargin: -height/2
verticalCenter: parent.verticalCenter
}
}
OpacityRampEffect {
sourceItem: label
offset: 0.5
}
ColorOverlay {
anchors.fill: image
source: image
color: (highlighted ? Theme.highlightColor : (model.active ? Theme.primaryColor : Theme.secondaryHighlightColor))
anchors.fill: image
}
Image {
id: image
visible: false
source: model.icon // +'?'+ (highlighted ? Theme.highlightColor : (model.active ? Theme.primaryColor : Theme.secondaryHighlightColor))
sourceSize.width: Theme.iconSizeMedium
sourceSize.height: Theme.iconSizeMedium
source: model.icon// +'?'+ (highlighted ? Theme.highlightColor : (model.active ? Theme.primaryColor : Theme.secondaryHighlightColor))
anchors.centerIn: parent
visible: false
// smooth: true
}
Text {
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.paddingSmall
anchors.left: parent.left
anchors.right: parent.right
horizontalAlignment: Text.AlignHCenter
visible: false
text: model.name
font.pixelSize: Theme.fontSizeExtraSmall/2
color: (highlighted
? Theme.highlightColor
: (model.active ? Theme.primaryColor : Theme.secondaryHighlightColor))
horizontalAlignment: Text.AlignHCenter
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
bottomMargin: Theme.paddingSmall
}
}
Label {
id: label
visible: false
anchors {
bottom: parent.bottom
}
horizontalAlignment : Text.AlignHCente
width: parent.width
color: (highlighted ? Theme.highlightColor : Theme.secondaryHighlightColor)
text: {
return model.name.toUpperCase();
}
font.pixelSize: Theme.fontSizeExtraSmall
font.family: Theme.fontFamilyHeading
width: parent.width
horizontalAlignment : Text.AlignHCenter
anchors.bottom: parent.bottom
}
font {
pixelSize: Theme.fontSizeExtraSmall
family: Theme.fontFamilyHeading
}
}
onClicked: {
slideshowShow(index)
console.log(index)
navigateTo(model.slug)
effect.state = "right"
}
}
function navigateTo(slug){
for(var i = 0; i < listModel.count; i++){
if (listModel.get(i).slug === slug || i===slug)
@ -156,9 +166,9 @@ SilicaGridView {
listModel.setProperty(i, 'active', false);
}
console.log(slug)
}
VerticalScrollDecorator {}
}

View file

@ -4,48 +4,69 @@ import Sailfish.Silica 1.0
Item {
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
Rectangle {
id: bgImage
anchors.fill: parent
opacity: 0.2
gradient: Gradient {
GradientStop { position: 0.0; color: Theme.highlightBackgroundColor }
GradientStop { position: 1.0; color: Theme.highlightBackgroundColor }
}
anchors.fill: parent
Image {
anchors.fill: bgImage
asynchronous: true
fillMode: Image.PreserveAspectCrop
source: bg
opacity: 0.8
anchors.fill: parent
}
}
Image {
id: icon
anchors {
left: parent.left
leftMargin: Theme.paddingLarge
top: parent.top
topMargin: Theme.paddingLarge
}
asynchronous: true
width: description === "" ? Theme.iconSizeMedium : Theme.iconSizeLarge
height: width
source:
if (icon.status === Image.Error)
source = "../../images/icon-l-profile.svg?" + (pressed
? Theme.highlightColor
: Theme.primaryColor)
else image
width: description === "" ? Theme.iconSizeMedium : Theme.iconSizeLarge
height: width
anchors {
left: parent.left
leftMargin: Theme.paddingLarge
top: parent.top
topMargin: Theme.paddingLarge
}
Button {
id: imageButton
opacity: 0
width: Theme.iconSizeExtraLarge * 1.2
anchors {
top: parent.top
left: parent.left
bottom: parent.bottom
}
onClicked: {
pageStack.push(Qt.resolvedUrl("ProfileImage.qml"), {
"image": image
})
}
}
}
Column {
anchors {
left: icon.right
@ -54,6 +75,7 @@ Item {
rightMargin: Theme.paddingLarge
verticalCenter: parent.verticalCenter
}
Label {
id: ttl
text:
@ -61,23 +83,24 @@ Item {
description.split('@')[0]
}
else title
height: contentHeight
color: Theme.highlightColor
font.pixelSize: Theme.fontSizeLarge
font.family: Theme.fontFamilyHeading
horizontalAlignment: Text.AlignRight
color: Theme.highlightColor
truncationMode: TruncationMode.Fade
width: parent.width
height: contentHeight
horizontalAlignment: Text.AlignRight
}
Label {
height: description === "" ? 0 : contentHeight
text: "@"+description
color: Theme.secondaryHighlightColor
font.pixelSize: Theme.fontSizeSmall
font.family: Theme.fontFamilyHeading
horizontalAlignment: Text.AlignRight
color: Theme.secondaryHighlightColor
truncationMode: TruncationMode.Fade
width: parent.width
height: description === "" ? 0 : contentHeight
horizontalAlignment: Text.AlignRight
}
}

View file

@ -0,0 +1,27 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
FullscreenContentPage {
id: profileImage
allowedOrientations: Orientation.All
property string image: ""
Image {
source: image
fillMode: Image.PreserveAspectFit
anchors.fill: parent
}
IconButton {
icon.source: "image://theme/icon-m-dismiss"
anchors {
top: profileImage.top
topMargin: Theme.horizontalPageMargin
right: parent.right
rightMargin: Theme.horizontalPageMargin
}
onClicked: pageStack.pop()
}
}

View file

@ -26,6 +26,8 @@ BackgroundItem {
visible: type.length
anchors.left: lblName.left
anchors.bottom: iconRT.bottom
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.secondaryColor
text: {
var action;
switch(type){
@ -43,8 +45,6 @@ BackgroundItem {
}
return '@' + retweetScreenName + ' ' + action
}
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.secondaryColor
}
Image {
@ -116,12 +116,12 @@ BackgroundItem {
}
Label {
id: lblDate
function timestamp() {
var txt = Format.formatDate(created_at, Formatter.Timepoint)
var elapsed = Format.formatDate(created_at, Formatter.DurationElapsedShort)
return (elapsed ? elapsed : txt )
}
id: lblDate
color: (pressed ? Theme.highlightColor : Theme.primaryColor)
text: Format.formatDate(created_at, new Date() - created_at < 60*60*1000 ? Formatter.DurationElapsedShort : Formatter.TimeValueTwentyFourHours)
font.pixelSize: Theme.fontSizeExtraSmall

View file

@ -17,7 +17,7 @@ class Notifications : public QObject
{
Q_OBJECT
public:
explicit Notifications(QObject *parent = 0);
explicit Notifications(QObject *parent = nullptr);
Q_INVOKABLE void notify(QString appName, QString summary, QString body, bool preview, QString ts, QString issuekey);
};