started to move the internal structure to widgets to make everything more modular

This commit is contained in:
Dorian Zedler 2019-05-23 23:17:27 +02:00
parent 4567e0b568
commit f2ce94f474
21 changed files with 1535 additions and 26 deletions

View file

@ -1,5 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<manifest package="com.itsblue.blueROCKtest" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="0.01b" android:versionCode="1" android:installLocation="auto"> <manifest package="com.itsblue.blueROCKtest" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="0.01.1b" android:versionCode="2" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="blueROCK" android:icon="@drawable/icon"> <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="blueROCK" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="blueROCK (for digitalROCK)" android:screenOrientation="unspecified" android:launchMode="singleTop"> <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="blueROCK (for digitalROCK)" android:screenOrientation="unspecified" android:launchMode="singleTop">
<intent-filter> <intent-filter>

View file

@ -41,6 +41,7 @@ public slots:
QVariant getCalendar(QString nation, int year); QVariant getCalendar(QString nation, int year);
QVariant getRanking(int competitionId, int categoryId, bool registrationData = false, bool rankingData = false, const int routeNumber = -2); QVariant getRanking(int competitionId, int categoryId, bool registrationData = false, bool rankingData = false, const int routeNumber = -2);
QVariant getAthlete(int perId); QVariant getAthlete(int perId);
QVariant getWidgetData(QVariantMap params);
}; };

View file

@ -26,14 +26,13 @@ DataListView {
model: listData[ root.listKey ] === undefined ? 0:listData[ root.listKey ].length model: listData[ root.listKey ] === undefined ? 0:listData[ root.listKey ].length
delegate: ItemDelegate { delegate: ItemDelegate {
id: partDel id: partDel
property var thisData: listData[ "athletes" ][index] property var thisData: listData[ "athletes" ][index]
width: parent.width width: parent.width
height: parseInt(thisData.cat) === root.catId ? undefined:0
opacity: 0 opacity: 0
scale: 0.9 scale: 0.9

View file

@ -209,6 +209,8 @@ Page {
Label { Label {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: bestResultsRep.model > 0
width: parent.width width: parent.width
wrapMode: Label.Wrap wrapMode: Label.Wrap
@ -229,12 +231,34 @@ Page {
} }
Button {
id: toggleAllResultsBt
anchors.horizontalCenter: parent.horizontalCenter
visible: bestResultsRep.model > 0
flat: true
text: bestResultsRep.showAllResults ? qsTr("show best results"):qsTr("show all results")
onClicked: {
bestResultsRep.showAllResults = !bestResultsRep.showAllResults
}
Behavior on text {
FadeAnimation {
target: toggleAllResultsBt
fadeDuration: 150
}
}
}
Repeater { Repeater {
id: bestResultsRep id: bestResultsRep
property var bestResults: getBestResults() property var bestResults: showAllResults ? getAllResults() : getBestResults()
property bool showAllResults: false
function getBestResults(){ function getBestResults(){
@ -260,16 +284,44 @@ Page {
return bestResults return bestResults
} }
function getAllResults() {
var allResults = root.perData["results"]
allResults.sort(function(a, b) {
// sort results by date
var aTs = Date.fromLocaleString(Qt.locale(), a["date"], "yyyy-MM-dd").getTime()
var bTs = Date.fromLocaleString(Qt.locale(), b["date"], "yyyy-MM-dd").getTime()
return bTs - aTs;
});
return allResults
}
width: parent.width width: parent.width
model: bestResults.length > 12 ? 12:bestResults.length model: bestResults.length
delegate: Row { delegate: Row {
id: bestResultRow id: bestResultRow
property var thisData: bestResultsRep.bestResults[index]
width: parent.width width: parent.width
height: 50 height: 50
opacity: 0
scale: 0.9
onThisDataChanged: {
fadeInPa.start()
}
ParallelAnimation {
id: fadeInPa
NumberAnimation { target: bestResultRow; property: "opacity"; from: 0; to: 1.0; duration: 400 }
NumberAnimation { target: bestResultRow; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
}
Label { Label {
id: bestResultRankLa id: bestResultRankLa

View file

@ -298,6 +298,14 @@ Page {
text: name text: name
} }
/*
Label {
id: infola
width: parent.width
text: calendarList.listData[index].info
}*/
Label { Label {
id: dateLa id: dateLa
@ -323,6 +331,7 @@ Page {
} }
} }
} }
Dialog { Dialog {
@ -347,13 +356,21 @@ Page {
catSelectPu.cupCat = cupCat catSelectPu.cupCat = cupCat
catSelectPu.index = index catSelectPu.index = index
var tmpCatObj
if(cupCat){ if(cupCat){
catSelectPu.catObj = root.calendarData["cups"][index]["cats"] tmpCatObj = root.calendarData["cups"][index]["cats"]
} }
else { else {
catSelectPu.catObj = calendarList.listData[index]["cats"] tmpCatObj = calendarList.listData[index]["cats"]
} }
tmpCatObj.sort(function(a, b) {
return parseInt(a["GrpId"]) - parseInt(b["GrpId"]);
});
catSelectPu.catObj = tmpCatObj
catSelectPu.open() catSelectPu.open()
} }

View file

@ -29,6 +29,52 @@ Page {
property string subTitle: getSubtitle() property string subTitle: getSubtitle()
property bool titleIsPageTitle: true property bool titleIsPageTitle: true
property Component headerComponent: Item {
anchors.fill: parent
Button {
id: cupToolBt
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: parent.width * 0.1
}
height: parent.height * 0.5
width: height
onClicked: {
catSelectPu.appear()
}
onPressed: cupToolBt.scale = 0.9
onReleased: cupToolBt.scale = 1.0
background: Image {
anchors.centerIn: parent
source: "qrc:/icons/more_black.png"
height: parent.height > parent.width ? parent.width : parent.height
width: height
mipmap: true
fillMode: Image.PreserveAspectFit
Behavior on scale {
PropertyAnimation {
duration: 100
}
}
}
}
}
property bool ready property bool ready
property int status: -1 property int status: -1
@ -44,6 +90,14 @@ Page {
property bool res: catStatus === 0 || catStatus === 1 // result data property bool res: catStatus === 0 || catStatus === 1 // result data
property bool stl: catStatus === 2 || catStatus === 3 // startlist data property bool stl: catStatus === 2 || catStatus === 3 // startlist data
/*
enum DataType {
RankingData,
RegistrationData,
ResultData,
StartlistData
}*/
property var rankingData property var rankingData
property string listKey: root.reg ? "athletes":"participants" property string listKey: root.reg ? "athletes":"participants"
@ -57,7 +111,9 @@ Page {
function loadData(comp, cat, reg, rak, route){ function loadData(comp, cat, reg, rak, route){
root.status = 905 root.status = 905
console.log("[info][QML] getting ranking data of comp: " + comp + " , cat: " + cat + " and status: " + root.catStatus) console.log("[info][QML] getting ranking data of comp: " + comp + " , cat: " + cat + " and status: " + root.catStatus)
if(route === -1){ if(route === -1){
route = -2 route = -2
} }
@ -65,12 +121,13 @@ Page {
var ret = serverConn.getRanking(comp, cat, reg, rak, route) var ret = serverConn.getRanking(comp, cat, reg, rak, route)
root.status = ret["status"] root.status = ret["status"]
console.log(root.status)
if(parseInt(ret["status"]) === 200){ if(parseInt(ret["status"]) === 200){
// request was successfull -> prepare data // request was successfull -> prepare data
if(root.reg){ if(root.reg){
// if the data is registration data, athletes of other cats need to be removed // if the data is registration data, athletes of other cats need to be removed
/*
var validAthletes = [] var validAthletes = []
for(var i = 0; i < ret["data"]["athletes"].length; i++){ for(var i = 0; i < ret["data"]["athletes"].length; i++){
@ -79,7 +136,7 @@ Page {
validAthletes.push(ret["data"]["athletes"][i]) validAthletes.push(ret["data"]["athletes"][i])
} }
} }
ret["data"]["athletes"] = validAthletes ret["data"]["athletes"] = validAthletes*/
root.rankingData = ret["data"] root.rankingData = ret["data"]
} }
@ -349,4 +406,109 @@ Page {
} }
} }
Dialog {
id: catSelectPu
property var catObj: undefined
x: 0 //root.width / 2 - width / 2
y: root.height - height //root.height / 2 - height / 2
width: root.width
height: catsLv.implicitHeight
modal: true
focus: true
title: qsTr("select category")
function appear() {
var tmpCatObj
tmpCatObj = root.rankingData["categorys"]
tmpCatObj.sort(function(a, b) {
return parseInt(a["GrpId"]) - parseInt(b["GrpId"]);
});
catSelectPu.catObj = tmpCatObj
catSelectPu.open()
}
function getText(index){
// ----------------------------
// get the text
// returns list with [catId, catName]
return [catSelectPu.catObj[index]["GrpId"], catSelectPu.catObj[index]["name"]]
}
ListView {
id: catsLv
property int delegateHeight: 50
anchors.fill: parent
implicitWidth: parent.width
implicitHeight: root.height * 0.6 < ( (delegateHeight + spacing) * model ) ? root.height * 0.6 : (delegateHeight + spacing) * model + 75
model: catSelectPu.catObj !== undefined ? catSelectPu.catObj.length:0
ScrollIndicator.vertical: ScrollIndicator {
parent: catsLv.parent
anchors {
top: catsLv.top
left: catsLv.right
margins: 10
leftMargin: 3
bottom: catsLv.bottom
}
}
delegate: Button {
id: catBt
property var catData: catSelectPu.getText(index)
width: parent.width
height: text !== "" ? catsLv.delegateHeight:0
flat: true
text: catData[1]
onClicked: {
catSelectPu.close()
root.catId = catData[0]
root.loadData(root.comId, root.catId, root.reg, root.rak, root.routeNumber)
}
}
}
enter: Transition {
NumberAnimation { property: "opacity"; from: 0.0; to: 1.0 }
NumberAnimation {
property: "y"
from: root.height - catSelectPu.height * 0.7
to: root.height - catSelectPu.height
}
}
exit: Transition {
NumberAnimation { property: "opacity"; from: 1.0; to: 0.0 }
NumberAnimation {
property: "y"
from: root.height - catSelectPu.height
to: root.height - catSelectPu.height * 0.7
}
}
}
} }

View file

@ -25,6 +25,20 @@ Page {
id: root id: root
title: "start" title: "start"
property Component headerComponent: null
Label {
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: root.height * 0.03
}
font.pixelSize: anchors.topMargin
font.bold: true
text: "blueROCK"
}
Grid { Grid {
id: menuGr id: menuGr
@ -34,7 +48,7 @@ Page {
rows: app.landscape() ? 1:3 rows: app.landscape() ? 1:3
columns: app.landscape() ? 3:1 columns: app.landscape() ? 3:1
spacing: app.landscape() ? parent.height * 0.1:parent.width * 0.1 spacing: !app.landscape() ? parent.height * 0.08:parent.width * 0.1
property int buttonSize: app.landscape() ? parent.width * 0.2:parent.height * 0.2 property int buttonSize: app.landscape() ? parent.width * 0.2:parent.height * 0.2
@ -82,4 +96,24 @@ Page {
} }
Label {
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: root.height * 0.03
}
width: parent.width * 0.9
height: anchors.bottomMargin
wrapMode: "Wrap"
horizontalAlignment: Text.AlignHCenter
text: "Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>, (c) 1990-2019 by Ralf Becker."
onLinkActivated: {
Qt.openUrlExternally(link)
}
}
} }

View file

@ -0,0 +1,266 @@
/*
blueROCK - for digital rock
Copyright (C) 2019 Dorian Zedler
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.9
import QtQuick.Controls 2.4
import QtGraphicalEffects 1.0
import "../Components"
Page {
id: root
enum WidgetType {
Competitions,
Profile,
Registration,
Startlist,
Result,
Ranking,
Aggregated // not yet implemented
}
title: widgetLd.item === null ? "":widgetLd.item.title
property Component headerComponent: widgetLd.item === null ? null:widgetLd.item.headerComponent
property var params
property int status: -1
property bool ready: false
property var widgetData
property int widgetType
Component.onCompleted: {
loadData(params)
}
function loadData(params) {
// params is an object and can contain: {
// comp: competitionId,
// person: personId,
// cat: categoryId,
// nation: nationString ('', 'GER', 'SUI')
// type: ('','starters', 'nat_team_ranking', 'sektionenwertung', 'regionalzentren'),
//}
var ret = serverConn.getWidgetData(params)
if(ret["status"] === 200){
root.widgetData = ret["data"]
root.widgetType = checkWidgetType(params, root.widgetData)
console.log(widgetType)
widgetLd.load()
root.ready = true
}
root.status = ret["status"]
}
function updateData(params, openLoadingDl) {
if(openLoadingDl)
loadingDl.open()
for(var prop in params){
if(params.hasOwnProperty(prop)){
root.params[prop] = params[prop]
}
}
loadData(root.params)
if(openLoadingDl)
loadingDl.close()
}
function checkWidgetType(params, widgetData){
var widgetType
function hasParam(object, key, value){
if(object[key] !== undefined){
if(value !== undefined){
return object[key] === value
}
return true
}
return false
}
// check the type of the requested widget
if(hasParam(params, 'perId')){
// person profile
widgetType = WidgetPage.WidgetType.Profile
}
else if(hasParam(params, 'nation')){
// competition calendar
widgetType = WidgetPage.WidgetType.Competitions
}
else if(hasParam(params, 'comp') && hasParam(params, 'type', 'starters')){
// registration
widgetType = WidgetPage.WidgetType.Registration
}
else if(hasParam(params, 'type', 'startlist') || (widgetData.participants !== undefined && widgetData.participants[0] && !widgetData.participants[0].result_rank && widgetData.discipline !== 'ranking')){
// startlist
widgetType = WidgetPage.WidgetType.Startlist
}
else if(hasParam(params, 'comp') && hasParam(params, 'cat')){
// results
widgetType = WidgetPage.WidgetType.Result
}
else if( hasParam(params, 'cat') && hasParam(params, 'cup') && !hasParam(params, 'comp')){
// ranking data
widgetType = WidgetPage.WidgetType.Ranking
}
else if(hasParam(params, 'type', 'nat_team_ranking') || hasParam(params, 'type', 'sektionenwertung') || hasParam(params, 'type', 'regionalzentren')){
// aggregated
widgetType = WidgetPage.WidgetType.Aggregated
}
return widgetType
}
Loader {
id: widgetLd
property alias selector: selectorPu
property var updateData: root.updateData
property alias params: root.params
anchors.fill: parent
source: ""
function load() {
widgetLd.source = getFile(root.widgetType)
widgetLd.item.widgetData = root.widgetData
}
function getFile(widgetType) {
var path = "qrc:/Widgets/"
switch(widgetType){
case WidgetPage.WidgetType.Competitions:
path += "CalendarWidget"
break
case WidgetPage.WidgetType.Profile:
path += "ProfileWidget"
break
case WidgetPage.WidgetType.Registration:
path += "RegistrationWidget"
break
}
path += ".qml"
return path
}
}
Dialog {
id: selectorPu
property var dataObj
signal selectionFinished(int index, var data)
x: 0 //root.width / 2 - width / 2
y: root.height - height //root.height / 2 - height / 2
width: root.width
height: selectorLv.implicitHeight
modal: true
focus: true
title: qsTr("select category")
function appear(dataObj) {
selectorPu.dataObj = dataObj
selectorPu.open()
}
ListView {
id: selectorLv
property int delegateHeight: 50
anchors.fill: parent
implicitWidth: parent.width
implicitHeight: root.height * 0.6 < ( (delegateHeight + spacing) * model ) ? root.height * 0.6 : (delegateHeight + spacing) * model + 75
model: selectorPu.dataObj !== undefined ? selectorPu.dataObj.length:0
ScrollIndicator.vertical: ScrollIndicator {
parent: selectorLv.parent
anchors {
top: selectorLv.top
left: selectorLv.right
margins: 10
leftMargin: 3
bottom: selectorLv.bottom
}
}
delegate: Button {
id: catBt
width: parent.width
height: text !== "" ? selectorLv.delegateHeight:0
flat: true
text: selectorPu.dataObj[index].text
onClicked: {
selectorPu.close()
selectorPu.selectionFinished(index, selectorPu.dataObj[index].data)
}
}
}
enter: Transition {
NumberAnimation { property: "opacity"; from: 0.0; to: 1.0 }
NumberAnimation {
property: "y"
from: root.height - selectorPu.height * 0.7
to: root.height - selectorPu.height
}
}
exit: Transition {
NumberAnimation { property: "opacity"; from: 1.0; to: 0.0 }
NumberAnimation {
property: "y"
from: root.height - selectorPu.height
to: root.height - selectorPu.height * 0.7
}
}
}
}

View file

@ -0,0 +1,340 @@
/*
blueROCK - for digital rock
Copyright (C) 2019 Dorian Zedler
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.9
import QtQuick.Controls 2.4
import "../Components"
DataListView {
id: control
property string title: (params.nation === "" ? "IFSC":params.nation === "GER" ? "DAV":"SAC") + " " + qsTr("competition calendar")
property Component headerComponent: Item {
anchors.fill: parent
Row {
anchors.fill: parent
anchors.rightMargin: 5
spacing: width * 0.05
Label {
id: yearLa
anchors.verticalCenter: parent.verticalCenter
width: parent.width * 0.4
height: parent.height * 0.6
fontSizeMode: Text.Fit
font.pixelSize: height
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
minimumPixelSize: 1
text: control.year
Behavior on text {
FadeAnimation {
target: yearLa
}
}
}
Button {
id:yearToolBt
anchors {
verticalCenter: parent.verticalCenter
}
height: parent.height * 0.5
width: parent.width * 0.25
onClicked: {
control.changeYear()
}
onPressed: yearToolBt.scale = 0.9
onReleased: yearToolBt.scale = 1.0
background: Image {
anchors.centerIn: parent
source: "qrc:/icons/calendar.png"
height: parent.height > parent.width ? parent.width : parent.height
width: height
mipmap: true
fillMode: Image.PreserveAspectFit
Behavior on scale {
PropertyAnimation {
duration: 100
}
}
}
}
Button {
id: cupToolBt
anchors {
verticalCenter: parent.verticalCenter
}
height: parent.height * 0.5
width: parent.width * 0.25
onClicked: {
cupSelectPu.open()
}
onPressed: cupToolBt.scale = 0.9
onReleased: cupToolBt.scale = 1.0
background: Image {
anchors.centerIn: parent
source: "qrc:/icons/cup.png"
height: parent.height > parent.width ? parent.width : parent.height
width: height
mipmap: true
fillMode: Image.PreserveAspectFit
Behavior on scale {
PropertyAnimation {
duration: 100
}
}
}
}
}
}
property var widgetData
property int year: new Date().getFullYear()
anchors.fill: parent
//boundsBehavior: Flickable.StopAtBounds
model: widgetData["competitions"].length
status: root.status
onRefresh: {
updateData({}, false)
}
onModelChanged: {
autoScroll()
}
function autoScroll() {
var compList = control.widgetData["competitions"]
if(parseInt(control.year) === new Date().getFullYear()){
for(var i = 0; i < compList.length; i ++){
// get the start date pf the competition
var startDate = Date.fromLocaleString(Qt.locale(), compList[i]["date"], "yyyy-MM-dd")
// get the duration of the competition
var durationString = compList[i]["duration"] === undefined ? "1":compList[i]["duration"]
var days = parseInt(durationString.replace(/\D/g,''))
// calculate the end date of the competition
var endDate = new Date(startDate.valueOf())
endDate.setDate(endDate.getDate() + days);
//console.log(compList[i]["date"] + ": " + startDate + " to " + endDate)
if(endDate.getTime() < new Date().getTime()){
// end date is already over -> move the list view down!
control.positionViewAtIndex(i, ListView.Top)
//console.log("moving down!")
}
}
}
}
function getCompCatData(compCatId) {
var obj = app.compCats
for(var prop in obj) {
// go through the whole array and search for data keys
if (obj.hasOwnProperty(prop) && obj[prop]["cat_id"].indexOf(compCatId) >= 0) {
//console.log("found cat: " + obj[prop]['label'])
return obj[prop]
}
}
}
function openComp(compIndex){
var cats = control.widgetData["competitions"][compIndex]["cats"]
cats.sort(function(a, b) {
return parseInt(a["GrpId"]) - parseInt(b["GrpId"]);
});
var selectOptions = []
for(var prop in cats){
if (cats.hasOwnProperty(prop)) {
selectOptions.push({text: cats[prop]["name"], data:{cat: cats[prop]["GrpId"], comp: control.widgetData["competitions"][compIndex]["WetId"], status:cats[prop]["status"]}})
}
}
selector.appear(selectOptions)
}
function changeYear(){
var years = control.widgetData["years"]
var selectOptions = []
for(var prop in years){
if (years.hasOwnProperty(prop)) {
selectOptions.push({text: years[prop], data:{year: years[prop]}})
}
}
selector.appear(selectOptions)
}
Connections {
target: selector
onSelectionFinished: {
if(data.comp !== undefined){
console.log(data.status)
app.openWidget({comp: data.comp, cat: data.cat, type:data.status === 4 ? 'starters':''})
}
else if(data.year !== undefined){
updateData({year: data.year}, true)
control.year = data.year
}
}
}
delegate: ItemDelegate {
id: competitionDel
property bool over
property var thisData: control.widgetData["competitions"][index]
property string name: thisData["name"]
property string date: thisData["date_span"]
property var cats: thisData["cats"]
property int catId: thisData["cat_id"] === undefined ? 0:thisData["cat_id"]
width: parent.width
height: compDelCol.height + 10
enabled: thisData["cats"] !== undefined && thisData["cats"].length > 0
opacity: 0
scale: 0.9
onThisDataChanged: {
fadeInPa.start()
}
onClicked: {
control.openComp(index)
}
ParallelAnimation {
id: fadeInPa
NumberAnimation { target: competitionDel; property: "opacity"; from: 0; to: 1.0; duration: 400 }
NumberAnimation { target: competitionDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
}
Rectangle {
id: delBackroundRect
anchors.fill: parent
opacity: 0.5
color: control.getCompCatData(catId) === undefined ? "white":control.getCompCatData(catId)["bgcolor"]
}
Column {
id: compDelCol
anchors.centerIn: parent
width: parent.width * 0.97
spacing: 10
Label {
id: nameLa
width: parent.width
font.bold: true
wrapMode: Text.WordWrap
text: name
}
/*
Label {
id: infola
width: parent.width
text: thisData.info
}*/
Label {
id: dateLa
color: "grey"
text: date
}
}
Rectangle {
id: bottomLineRa
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
height: 1
color: "lightgrey"
}
}
}

View file

@ -0,0 +1,388 @@
import QtQuick 2.9
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.3
import "../Components"
Page {
id: control
title: widgetData["firstname"] + " " + widgetData["lastname"]
property bool titleIsPageTitle: true
property bool ready
property int status: -1
property int perId: -1
property var widgetData: ({})
Component.onCompleted: {
if(control.loadData(control.perId)){
control.ready = true
}
}
function loadData(perId) {
console.log("loading athlete: ", perId)
control.status = 905
var ret = serverConn.getAthlete(perId)
control.status = ret["status"]
if(ret["status"] === 200){
control.widgetData = ret["data"]
return true
}
else {
return false
}
}
ScrollView {
id: mainSv
anchors.fill: parent
anchors.margins: 10
anchors.rightMargin: 14
contentWidth: parent.width - anchors.leftMargin - anchors.rightMargin
ScrollBar.vertical: ScrollBar {
anchors {
top: mainSv.top
left: mainSv.right
margins: 10
leftMargin: 3
bottom: mainSv.bottom
}
width: 8
active: true
}
Column {
id: mainCol
width: parent.width
Row {
height: control.height * 0.3
width: parent.width
Image {
id: photo
property bool ready: false
anchors.verticalCenter: parent.verticalCenter
height: parent.height * 0.9
width: status === Image.Null || status === Image.Error ? 0:parent.width * 0.5
fillMode: Image.PreserveAspectFit
source: widgetData["photo"] === undefined ? "":widgetData["photo"].replace("https", "http").replace("www.digitalrock.de", "egw.ifsc-climbing.org")
asynchronous: true
FancyBusyIndicator {
height: width
anchors.centerIn: parent
opacity: photo.status === Image.Loading
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
height: parent.height * 0.9
width: parent.width - photo.width
Label {
height: parent.height * 0.2
width: parent.width
font.pixelSize: height * 0.6
font.bold: true
minimumPixelSize: 1
fontSizeMode: Text.Fit
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: widgetData["firstname"] + " " + widgetData["lastname"]
}
Label {
height: parent.height * 0.2
width: parent.width
font.pixelSize: height * 0.6
font.bold: false
minimumPixelSize: 1
fontSizeMode: Text.Fit
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: widgetData["nation"]
}
Label {
height: parent.height * 0.15
width: parent.width
font.pixelSize: height * 0.6
font.bold: false
minimumPixelSize: 1
fontSizeMode: Text.Fit
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: "<html><a href='" + widgetData["fed_url"] + "'>" + widgetData["federation"] + "</a>"
onLinkActivated: {
Qt.openUrlExternally(link)
}
}
Label {
height: parent.height * 0.15
width: parent.width
font.pixelSize: height * 0.6
font.bold: false
minimumPixelSize: 1
fontSizeMode: Text.Fit
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: qsTr("age") + ": " + widgetData["age"]
}
Label {
height: parent.height * 0.15
width: parent.width
font.pixelSize: height * 0.6
font.bold: false
minimumPixelSize: 1
fontSizeMode: Text.Fit
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: qsTr("year of birth") + ": " + widgetData["birthdate"]
}
Label {
height: parent.height * 0.15
width: parent.width
font.pixelSize: height * 0.6
font.bold: false
minimumPixelSize: 1
fontSizeMode: Text.Fit
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: qsTr("city") + ": " + widgetData["city"]
}
}
}
Label {
anchors.horizontalCenter: parent.horizontalCenter
visible: bestResultsRep.model > 0
width: parent.width
wrapMode: Label.Wrap
text: widgetData["freetext"]
}
Rectangle {
id: separatorLine
anchors.horizontalCenter: parent.horizontalCenter
height: 1
width: parent.width * 0.9
color: "black"
}
Button {
id: toggleAllResultsBt
anchors.horizontalCenter: parent.horizontalCenter
visible: bestResultsRep.model > 0
flat: true
text: bestResultsRep.showAllResults ? qsTr("show best results"):qsTr("show all results")
onClicked: {
bestResultsRep.showAllResults = !bestResultsRep.showAllResults
}
Behavior on text {
FadeAnimation {
target: toggleAllResultsBt
fadeDuration: 150
}
}
}
Repeater {
id: bestResultsRep
property var bestResults: showAllResults ? getAllResults() : getBestResults()
property bool showAllResults: false
function getBestResults(){
var allResults = control.widgetData["results"]
allResults.sort(function(a, b) {
// sort results by weight
var year = new Date().getFullYear();
var weightA = a.rank/2 + (year-parseInt(a.date)) + 4*!a.nation;
var weightB = b.rank/2 + (year-parseInt(b.date)) + 4*!b.nation;
return weightA - weightB;
});
var bestResults = allResults.slice(0,12)
bestResults.sort(function(a, b) {
// sort results by date
var aTs = Date.fromLocaleString(Qt.locale(), a["date"], "yyyy-MM-dd").getTime()
var bTs = Date.fromLocaleString(Qt.locale(), b["date"], "yyyy-MM-dd").getTime()
return bTs - aTs;
});
return bestResults
}
function getAllResults() {
var allResults = control.widgetData["results"]
allResults.sort(function(a, b) {
// sort results by date
var aTs = Date.fromLocaleString(Qt.locale(), a["date"], "yyyy-MM-dd").getTime()
var bTs = Date.fromLocaleString(Qt.locale(), b["date"], "yyyy-MM-dd").getTime()
return bTs - aTs;
});
return allResults
}
width: parent.width
model: bestResults.length
delegate: Row {
id: bestResultRow
property var thisData: bestResultsRep.bestResults[index]
width: parent.width
height: 50
opacity: 0
scale: 0.9
onThisDataChanged: {
fadeInPa.start()
}
ParallelAnimation {
id: fadeInPa
NumberAnimation { target: bestResultRow; property: "opacity"; from: 0; to: 1.0; duration: 400 }
NumberAnimation { target: bestResultRow; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
}
Label {
id: bestResultRankLa
width: parent.width * 0.2
height: parent.height
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
fontSizeMode: Text.Fit
minimumPixelSize: 1
font.pixelSize: height * 0.6
text: bestResultsRep.bestResults[index]["rank"]
}
Label {
id: bestResultCompLa
width: parent.width * 0.6
height: parent.height
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
fontSizeMode: Text.Fit
minimumPixelSize: height * 0.3
font.pixelSize: height * 0.3
elide: "ElideRight"
text: "<html><a href='#'>" + bestResultsRep.bestResults[index]["name"] + "</a></html>"
onLinkActivated: {
app.openResults( bestResultsRep.bestResults[index]["WetId"], bestResultsRep.bestResults[index]["GrpId"], 1 )
}
}
Label {
id: bestResultDateLa
width: parent.width * 0.2
height: parent.height
scale: 0.8
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
fontSizeMode: Text.Fit
minimumPixelSize: 1
font.pixelSize: height * 0.6
text: bestResultsRep.bestResults[index]["date"]
}
}
}
}
}
}

View file

@ -0,0 +1,104 @@
/*
blueROCK - for digital rock
Copyright (C) 2019 Dorian Zedler
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.9
import QtQuick.Controls 2.4
import "../Components"
DataListView {
id: control
property var widgetData: ({})
model: widgetData[ 'athletes' ] === undefined ? 0:widgetData[ 'athletes' ].length
onRefresh: {
updateData({}, false)
}
delegate: ItemDelegate {
id: partDel
property var thisData: widgetData[ "athletes" ][index]
width: parent.width
height: parseInt(thisData.cat) === parseInt(params.cat) ? undefined:0
opacity: 0
scale: 0.9
onThisDataChanged: {
fadeInPa.start()
}
onClicked: {
app.openAthlete(thisData["PerId"])
}
ParallelAnimation {
id: fadeInPa
NumberAnimation { target: partDel; property: "opacity"; from: 0; to: 1.0; duration: 400 }
NumberAnimation { target: partDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
}
text: ""
Label {
anchors.fill: parent
anchors.leftMargin: parent.width * 0.05
verticalAlignment: Text.AlignVCenter
fontSizeMode: Text.Fit
font.bold: true
font.pixelSize: Math.abs( height * 0.4 )
minimumPixelSize: height * 0.3
elide: "ElideRight"
text: control.getText(index)
}
}
function getText(index){
// ----------------------------
// if we have registration data
var fedName // federation name
if(widgetData["federations"] !== undefined){
// not an international competition -> get name of federation
for(var i = 0; i < widgetData["federations"].length; i ++ ){
//console.log("checking " + i + ": cat: " + parseInt(widgetData["categorys"][i]["GrpId"]) + " searched cat: " + root.catId)
if(widgetData["federations"][i]["fed_id"] === widgetData[ 'athletes' ][index]["reg_fed_id"]){
fedName = widgetData["federations"][i]["shortcut"]
}
}
}
else {
// an international competition -> get nation
fedName = widgetData[ 'athletes' ][index]["nation"]
}
return widgetData[ "athletes" ][index]["firstname"] + " " + widgetData[ "athletes" ][index]["lastname"] + " (" + fedName + ")"
}
}

View file

@ -0,0 +1,100 @@
/*
blueROCK - for digital rock
Copyright (C) 2019 Dorian Zedler
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.9
import QtQuick.Controls 2.4
import "../Components"
DataListView {
id: control
property var widgetData: ({})
model: widgetData[ root.listKey ] === undefined ? 0:widgetData[ root.listKey ].length
delegate: ItemDelegate {
id: partDel
property var thisData: widgetData[ "athletes" ][index]
width: parent.width
height: parseInt(thisData.cat) === root.catId ? undefined:0
opacity: 0
scale: 0.9
onThisDataChanged: {
fadeInPa.start()
}
onClicked: {
app.openAthlete(thisData["PerId"])
}
ParallelAnimation {
id: fadeInPa
NumberAnimation { target: partDel; property: "opacity"; from: 0; to: 1.0; duration: 400 }
NumberAnimation { target: partDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
}
text: ""
Label {
anchors.fill: parent
anchors.leftMargin: parent.width * 0.05
verticalAlignment: Text.AlignVCenter
fontSizeMode: Text.Fit
font.bold: true
font.pixelSize: Math.abs( height * 0.4 )
minimumPixelSize: height * 0.3
elide: "ElideRight"
text: control.getText(index)
}
}
function getText(index){
// ----------------------------
// if we have registration data
var fedName // federation name
if(widgetData["federations"] !== undefined){
// not an international competition -> get name of federation
for(var i = 0; i < widgetData["federations"].length; i ++ ){
//console.log("checking " + i + ": cat: " + parseInt(widgetData["categorys"][i]["GrpId"]) + " searched cat: " + root.catId)
if(widgetData["federations"][i]["fed_id"] === widgetData[ root.listKey ][index]["reg_fed_id"]){
fedName = widgetData["federations"][i]["shortcut"]
}
}
}
else {
// an international competition -> get nation
fedName = widgetData[ root.listKey ][index]["nation"]
}
return widgetData[ "athletes" ][index]["firstname"] + " " + widgetData[ "athletes" ][index]["lastname"] + " (" + fedName + ")"
}
}

View file

@ -36,15 +36,10 @@ Window {
property int errorCode: -1 property int errorCode: -1
property var competitionCategoryColors: { // comp cats source:
"61": "lightgrey", // - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/icc_calendar.php
"58": "lightgreen", // - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/dav_calendar.php
// - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/sac_calendar.php
"69": "#B8C8FF",
"70": "#F0F0F0",
"71": "#D8E8FF",
"256": "#D8E8FF"
}
property var compCats: { property var compCats: {
'int' : { 'int' : {
@ -165,8 +160,10 @@ Window {
Component.onCompleted: { Component.onCompleted: {
//app.openAthlete(53139) // dorian: 53139 , rustam: 6933 , helen: 53300 //app.openAthlete(53139) // dorian: 53139 , rustam: 6933 , helen: 53300
openWidget({nation:'GER'})
} }
Shortcut { Shortcut {
sequences: ["Esc", "Back"] sequences: ["Esc", "Back"]
enabled: mainStack.depth > 1 enabled: mainStack.depth > 1
@ -179,10 +176,6 @@ Window {
ServerConn { ServerConn {
id: serverConn id: serverConn
Component.onCompleted: {
//serverConn.refreshFoodplan()
}
} }
StackView { StackView {
@ -524,6 +517,21 @@ Window {
return app.height < app.width return app.height < app.width
} }
function openWidget(params){
loadingDl.open()
var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params})
app.errorCode = calComp.status
if(calComp.ready){
mainStack.push(calComp)
}
else {
delete(calComp)
}
loadingDl.close()
}
function openCalendar(nation){ function openCalendar(nation){
loadingDl.open() loadingDl.open()
@ -542,7 +550,7 @@ Window {
function openResults(comp, cat, status){ function openResults(comp, cat, status){
// comp: (int) competiotion ID // comp: (int) competiotion ID
// cat: (int) category ID // cat: (int) category ID
// reg: (int) 0: result imported into ranking; 1: result-service result available; 2: result-service startlist available; 3: old non result-server startlist (no longer used); 4: starters / registration data // status: (int) 0: result imported into ranking; 1: result-service result available; 2: result-service startlist available; 3: old non result-server startlist (no longer used); 4: starters / registration data
loadingDl.open() loadingDl.open()
@ -560,6 +568,11 @@ Window {
loadingDl.close() loadingDl.close()
} }
function hasParam(url, param, value){
if (typeof value == 'undefined') value = '';
return url.indexOf(param+'='+value) !== -1;
}
function openAthlete(perId) { function openAthlete(perId) {
loadingDl.open() loadingDl.open()
@ -605,7 +618,7 @@ Window {
case 500: case 500:
infoLevel = 2 infoLevel = 2
errorString = "Internal server error" errorString = "Internal server error"
errorDescription = "The server was unable to process this request, this is probaply the servers vault. Please try again later." errorDescription = "The server was unable to process this request, this is probaply the servers fault. Please try again later."
break break
case 900: case 900:
infoLevel = 2 infoLevel = 2

View file

@ -16,5 +16,10 @@
<file>Components/PullRefresher.qml</file> <file>Components/PullRefresher.qml</file>
<file>Pages/AthleteProfilePage.qml</file> <file>Pages/AthleteProfilePage.qml</file>
<file>Components/FadeAnimation.qml</file> <file>Components/FadeAnimation.qml</file>
<file>Pages/WidgetPage.qml</file>
<file>Widgets/CalendarWidget.qml</file>
<file>Widgets/ProfileWidget.qml</file>
<file>Widgets/ResultWidget.qml</file>
<file>Widgets/RegistrationWidget.qml</file>
</qresource> </qresource>
</RCC> </RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View file

@ -7,5 +7,7 @@
<file>icons/backDark.png</file> <file>icons/backDark.png</file>
<file>icons/calendar.png</file> <file>icons/calendar.png</file>
<file>icons/cup.png</file> <file>icons/cup.png</file>
<file>Banner.png</file>
<file>icons/more_black.png</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -23,7 +23,10 @@ ServerConn::ServerConn(QObject *parent) : QObject(parent)
} }
QVariant ServerConn::getCalendar(QString nation, int year){ QVariant ServerConn::getCalendar(QString nation, int year){
QVariantMap ret = this->senddata(QUrl("http://egw.ifsc-climbing.org/egw/ranking/json.php?year=" + QString::number(year) + "&nation=" + nation)); QString requestUrl = "http://egw.ifsc-climbing.org/egw/ranking/json.php?year=" + (year == 0 ? "":QString::number(year)) + "&nation=" + nation;
qDebug() << requestUrl;
QVariantMap ret = this->senddata(QUrl(requestUrl));
if(ret["status"] != 200){ if(ret["status"] != 200){
// request was a failure // request was a failure
@ -76,6 +79,29 @@ QVariant ServerConn::getAthlete(int perId){
return data; return data;
} }
QVariant ServerConn::getWidgetData(QVariantMap params){
QString requestUrl = "http://egw.ifsc-climbing.org/egw/ranking/json.php?";
for(QVariantMap::const_iterator iter = params.begin(); iter != params.end(); ++iter){
requestUrl += iter.key() + "=" + iter.value().toString() + "&";
}
qDebug() << requestUrl;
QVariantMap ret = this->senddata(QUrl(requestUrl));
if(ret["status"] != 200){
// request was a failure
return QVariantMap({{"status", ret["status"]}, {"data", ""}});
}
QJsonDocument jsonReply = QJsonDocument::fromJson(ret["text"].toString().toUtf8());
QVariantMap data = {{"status", 200}, {"data", jsonReply.toVariant()}};
return data;
}
// ------------------------ // ------------------------
// --- Helper functions --- // --- Helper functions ---
// ------------------------ // ------------------------