app/resources/qml/main.qml

693 lines
20 KiB
QML

/*
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.Window 2.2
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.12
import QtPurchasing 1.12
import de.itsblue.blueROCK 1.0
import "./Pages"
import "./Components"
import "./Widgets"
Window {
visible: true
width: 540
height: 960
title: "blueROCK"
Page {
id: app
property int errorCode: -1
property var colorShade: Material.theme === Material.Light ? Material.Shade300 : Material.Shade700
property color nationalAdultsColor: Material.color(Material.Green, colorShade)
property color nationalYouthColor: Material.color(Material.LightGreen, colorShade)
property color federalColor: Material.color(Material.Grey, colorShade)
// comp cats source:
// - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/dav_calendar.php
// - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/sac_calendar.php
property var compCats: {
// --- ICC ---
/*'int' : {
'label' : 'International',
'nation' : 'ICC',
'wettk_reg' : '^[0-9]{2,2}[_^E]{1}[^YJ]{1,1}.*',
'serie_reg' : '^[0-9]{2,2}_(WC|TR){1,1}.*',
'rang_title': 'CUWR continuously updated WORLDRANKING',
'bgcolor' : '#B8C8FF',
'nat_team_ranking' : '',
'cat_id' : [68,86]//[68,69,70,86,259]
},*/
'worldcup': {
'label' : 'World Cups',
'nation' : 'ICC',
'bgcolor' : '#B8C8FF',
'sort_rank': 1,
'cat_id' : [69]
},
'youth' : {
'label' : 'Youth Events',
'nation' : 'ICC',
'wettk_reg' : '^[0-9]{2,2}(EYC|_J|_Y){1,1}.*',
'serie_reg' : '^[0-9]{2,2}_EYC',
'rang_title': '',
'bgcolor' : '#D8E8FF',
'sort_rank': 2,
'cat_id' : [71,258]
},
'cont': {
'label' : 'Continental Events',
'nation' : 'ICC',
'bgcolor' : '#B8C8FF',
'sort_rank': 3,
'cat_id' : [262]
},
'masters' : {
'label' : 'Masters and Promo Events',
'nation' : 'ICC',
'wettk_reg' : '^[0-9]{2,2}_[^PWERASL]{1}.*',
// 'serie_reg' : '^[0-9]{2,2}_(WC|TR){1,1}.*',
// 'rang_title': 'CUWR continuously updated WORLDRANKING',
'bgcolor' : '#F0F0F0',
'sort_rank': 4,
'cat_id' : [70]
},
'para' : {
'label' : 'Paraclimbing Events',
'nation' : 'ICC',
'wettk_reg' : '^[0-9]{2,2}_PE.*',
'bgcolor' : '#F0F0F0',
'sort_rank': 5,
'cat_id' : [256,259]
},
'games': {
'label': 'Games',
'nation': 'ICC',
'bgcolor' : '#B8C8FF',
'sort_rank': 6,
'cat_id': [68,86]
},
// --- GER ---
'ger_meisterschaft' : {
'label' : 'Deutsche Meisterschaft',
'nation' : 'GER',
'bgcolor' : app.nationalAdultsColor,//'#A8F0A8',
'sort_rank': 1,
'cat_id' : [57, 59, 60]
},
'ger_jugend' : {
'label' : 'Deutscher Jugendcup',
'nation' : 'GER',
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}[^WL]+.*',
'serie_reg' : '^[0-9]{2,2}_JC',
// 'rang_title': 'Deutsche Jugend RANGLISTE',
'bgcolor' : app.nationalYouthColor,//'#D8FFD8',
'sort_rank': 2,
'cat_id' : [58]
},
'ger_state' : {
'label' : 'Landesmeisterschaft',
'nation' : 'GER',
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}LM.*',
'serie_reg' : '^[0-9]{2,2}[_J]{1,1}LM.*',
'rang_title': '',
'bgcolor' : app.federalColor, //'#F0F0F0',
'sort_rank': 3,
'cat_id' : [61,56]
},
// --- SUI ---
'sui' : {
'label' : 'Erwachsene',
'nation' : 'SUI',
'wettk_reg' : '^[0-9]{2,2}_[^R].*',
'serie_reg' : '.*',
'rang_title': 'SWISS RANKING',
'bgcolor' : app.nationalAdultsColor, //'#A8F0A8',
'sort_rank': 1,
'cat_id' : [62,63]
},
'sui_jugend' : {
'label' : 'Jugend',
'nation' : 'SUI',
'wettk_reg' : '^[0-9]{2,2}_[^R].*',
'serie_reg' : '.*',
'rang_title': 'SWISS RANKING',
'bgcolor' : app.nationalYouthColor, //'#D8FFD8',
'sort_rank': 2,
'cat_id' : [65]
},
'sui_local' : {
'label' : 'RegioCups',
'nation' : 'SUI',
'wettk_reg' : '^[0-9]{2,2}_RG_.*',
'rang_title': '',
'bgcolor' : app.federalColor, //'#F0F0F0',
'sort_rank': 3,
'cat_id' : [64]
},
'sui_ice' : {
'label' : 'Iceclimbing',
'nation' : 'SUI',
'wparams["valid"]ettk_reg' : '^[0-9]{2,2}_RC_.*',
'rang_title': '',
'bgcolor' : app.federalColor, //'#F0F0F0',
'sort_rank': 4,
'cat_id' : [84]
}
}
anchors.fill: parent
Material.theme: appSettings.read("darkTheme") === "true" ? Material.Dark:Material.Light
Component.onCompleted: {
//loadingDl.open()
//app.openAthlete() // dorian: 53139 , rustam: 6933 , helen: 53300
//openWidget({nation:'GER'})
//mainStack.push("Pages/AthleteSearchPage.qml")
//openWidget({comp: 11651, cat: 26})
//openWidget({person: 6623})
//console.log(JSON.stringify(serverConn.getParamsFromUrl("")))
//openWidgetFromUrl("https://l.bluerock.dev/?comp=11601&type=starters")
}
FontLoader {
id: fa5solid
source: "qrc:/fonts/fa5solid.otf"
}
FontLoader {
id: fa5regular
source: "qrc:/fonts/fa5regular.otf"
}
Shortcut {
sequences: ["Esc", "Back"]
enabled: mainStack.depth > 1
onActivated: app.goBack()
}
BlueRockBackend {
id: serverConn
onOpenedViaUrl: {
app.openWidgetFromUrl(url)
}
}
AppSettings {
id: appSettings
}
StackView {
id: mainStack
anchors {
top: toolBar.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
}
initialItem: startPgComp
Component {
id: startPgComp
StartPage {}
}
popEnter: Transition {
XAnimator {
from: (mainStack.mirrored ? -1 : 1) * -mainStack.width
to: 0
duration: 500
easing.type: Easing.OutCubic
}
}
popExit: Transition {
XAnimator {
from: 0
to: (mainStack.mirrored ? -1 : 1) * mainStack.width
duration: 500
easing.type: Easing.OutCubic
}
}
pushEnter: Transition {
XAnimator {
from: (mainStack.mirrored ? -1 : 1) * mainStack.width
to: 0
duration: 500
easing.type: Easing.OutCubic
}
}
pushExit: Transition {
XAnimator {
from: 0
to: (mainStack.mirrored ? -1 : 1) * -mainStack.width
duration: 500
easing.type: Easing.OutCubic
}
}
}
AppToolBar {
id: toolBar
anchors {
top: parent.top
left: parent.left
right: parent.right
topMargin: -height
}
height: 50
showErrorBar: true
RowLayout {
anchors.fill: parent
spacing: width * 0.02
ToolButton {
id:toolButton
height: parent.height
onClicked: app.goBack()
text: "\uf053"
font.family: fa5solid.name
}
Column {
Layout.fillWidth: true
height: childrenRect.height
width: parent.width - extraComponentLoader.width - toolButton.width - 3 * parent.spacing
MovingLabel {
id: toolBarTitleLa
syncWithLabel: toolBarSubTitleLa
width: parent.width
scale: 1
font.bold: true
verticalAlignment: Text.AlignVCenter
text: getText()
function getText(){
var titleString = "";
if(!mainStack.currentItem.titleIsPageTitle){
for(var i=1; i<mainStack.depth; i++){
if(i > 1){
titleString += " > "
}
titleString += mainStack.get(i).title
}
}
else {
titleString = mainStack.currentItem.title
}
return(titleString)
}
Behavior on text {
FadeAnimation {
target: toolBarTitleLa
}
}
}
MovingLabel {
id: toolBarSubTitleLa
visible: text !== ""
syncWithLabel: toolBarTitleLa
width: parent.width
font.bold: false
text: getText()
function getText(){
var titleString = "";
if(mainStack.currentItem.subTitle !== undefined){
titleString = mainStack.currentItem.subTitle
}
return titleString
}
Behavior on text {
FadeAnimation {
target: toolBarSubTitleLa
}
}
}
}
Loader {
id: extraComponentLoader
property int maximumWidth: parent.width * 0.4 - toolButton.width - 3
height: parent.height
onItemChanged: {
if(item === null) {
extraComponentLoader.Layout.preferredWidth = 0
return
}
extraComponentLoader.item.width = extraComponentLoader.item.implicitWidth
extraComponentLoader.Layout.preferredWidth = extraComponentLoader.item.width
widthChangedCon.target = extraComponentLoader.item
}
Connections {
target: mainStack
function onCurrentItemChanged() {
secondCon.target = mainStack.currentItem
disappearNa.start()
}
}
Connections {
id: secondCon
target: null
function onHeaderComponentChanged() {
disappearNa.start()
}
}
Connections {
id: widthChangedCon
function onImplicitWidthChanged() {
extraComponentLoader.item.width = extraComponentLoader.item.implicitWidth
extraComponentLoader.Layout.preferredWidth = extraComponentLoader.item.implicitWidth
}
}
ParallelAnimation {
id: appearNa
NumberAnimation {
target: extraComponentLoader
property: "opacity"
from: 0
to: 1
duration: 150
}
NumberAnimation {
target: extraComponentLoader
property: "scale"
from: 0.9
to: 1
duration: 150
}
}
ParallelAnimation {
id: disappearNa
NumberAnimation {
target: extraComponentLoader
property: "opacity"
from: 1
to: 0
duration: 150
}
NumberAnimation {
target: extraComponentLoader
property: "scale"
from: 1
to: 0.9
duration: 150
}
onRunningChanged: {
if(!running){
extraComponentLoader.sourceComponent = mainStack.currentItem.headerComponent
appearNa.start()
}
}
}
}
}
Behavior on anchors.topMargin {
NumberAnimation {
duration: 500
easing.type: Easing.OutCubic
}
}
states: [
State {
name: "closed"
when: mainStack.depth === 1
PropertyChanges {
target: toolBar
anchors.topMargin: -height
}
},
State {
name: "open"
when: mainStack.depth > 1
PropertyChanges {
target: toolBar
anchors.topMargin: 0
}
}
]
}
Popup {
id: loadingDl
x: ( app.width - width ) / 2
y: ( app.height - height ) / 2
modal: true
closePolicy: Dialog.NoAutoClose
contentItem: Column {
spacing: 50
FancyBusyIndicator {
running: true
}
Label {
anchors.horizontalCenter: parent.horizontalCenter
font.bold: true
color: "white"
//% "Loading"
text: qsTrId("#loading") + "..."
}
}
background: Rectangle {
color: "transparent"
}
}
Store {
id: inAppProductStore
Product {
id: speedFlowChartProduct
identifier: "speed_flowchart"
//identifier: "android.test.purchased"
//identifier: "android.test.canceled"
//identifier: "android.test.refunded"
type: Product.Unlockable
onPurchaseRestored: {
appSettings.write("speedBackendPurchase", 1)
}
onPurchaseSucceeded: {
appSettings.write("speedBackendPurchase", 1)
}
onPurchaseFailed: {
appSettings.write("speedBackendPurchase", 0)
}
}
}
function landscape() {
return app.height < app.width
}
function toggleDarkMode() {
var dark = app.Material.theme === Material.Light
app.Material.theme = dark ? Material.Dark : Material.Light
appSettings.write("darkTheme", dark)
}
function largeScreen() {
return Math.min(app.width, app.height) > 750
}
function openWidget(params) {
loadingDl.open()
console.log("Opening widget: ", JSON.stringify(params))
var result = false
if(Object.keys(params).length) {
var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params})
app.errorCode = calComp.status
if(calComp.ready) {
mainStack.push(calComp)
result = true
}
else {
delete(calComp)
}
}
loadingDl.close()
return result
}
function openWidgetFromUrl(url) {
var result = serverConn.getParamsFromUrl(url)
if(result["valid"]) {
openWidget(result["params"])
return app.errorCode !== 906
}
return result["valid"]
}
function defaultString(string, defaultString) {
if(string === undefined || string === null) {
return defaultString
}
else {
return string
}
}
function goBack() {
if(!mainStack.currentItem.hasOwnProperty('onBackRequested') || mainStack.currentItem.onBackRequested())
mainStack.pop()
}
function getErrorInfo(errorCode) {
var infoLevel
// 0 - ok
// 1 - info
// 2 - error
var errorString
switch(errorCode) {
case 0:
infoLevel = 2
//% "No connection to server"
errorString = qsTrId("#noConnectionError")
break
case 404:
infoLevel = 2
//% "Not found"
errorString = qsTrId("#notFoundError")
break
case 901:
infoLevel = 1
//% "No Data"
errorString = qsTrId("#noDataError")
break
case 906:
infoLevel = 2
//% "Invalid Request"
errorString = qsTrId("#invalidRequestError")
errorDescription = "Invalid Request"
break
default:
infoLevel = 2
//% "Unexpected error"
errorString = qsTrId("#unexpectedError") + " ("+errorCode+")"
}
return([infoLevel, errorString])
}
}
}