Compare commits

...

55 commits
v0.6.1 ... main

Author SHA1 Message Date
Dorian Zedler bf617ba72c
Chore: don't show 0.000 in speed results
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-15 10:15:15 +02:00
Dorian Zedler 5694d61c4b
Feat: show route_quota 2023-07-15 10:11:26 +02:00
Dorian Zedler cd0500c40b
Feat: show both results in speed quali 2023-07-05 16:27:15 +02:00
Dorian Zedler 12185337b9
Merge branch 'main' of ssh://itsblue.dev/blueROCK/app 2023-05-14 22:56:33 +02:00
Dorian Zedler fe453cd5ac
Fix: fix for new Android API 2023-05-14 22:55:49 +02:00
Dorian Zedler 6644d16a92 Chore: update qt-andrioid version
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-02-06 08:57:07 +01:00
Dorian Zedler f42d8fb8d7
Chore: update changelog
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2022-10-20 06:42:18 +02:00
Dorian Zedler e6569694e4
Chore: update permissions 2022-10-20 06:41:37 +02:00
Dorian Zedler 38dbbb3b32 Merge pull request 'Fix: release CI' (#38) from fix/release-ci into main
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/tag Build is passing
continuous-integration/drone Build is passing
Reviewed-on: #38
2022-08-14 18:08:56 +02:00
Dorian Zedler ce7fc3d367 Fix: release CI
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/pr Build was killed
2022-08-14 18:08:42 +02:00
Dorian Zedler 8e4e135f9c Merge pull request 'release: 0.7.0' (#37) from release/0.7.0 into main
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/tag Build is passing
Reviewed-on: #37
2022-08-14 17:55:44 +02:00
Dorian Zedler c13d108cff
Chore: update release date
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/pr Build was killed
2022-08-14 17:55:26 +02:00
Dorian Zedler 367807b90d
Chore: prepare release 0.7.0
Some checks are pending
continuous-integration/drone/push Build is running
continuous-integration/drone/pr Build is running
2022-08-14 17:53:56 +02:00
Dorian Zedler 6ca5b81f6d
Fix: Font size on Android 2022-08-14 17:53:35 +02:00
Dorian Zedler 05bce5e97d Merge pull request 'Chore: refactor speed flowchart (fixes #26)' (#36) from fix/speed-flowchart into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #36
2022-08-14 17:50:58 +02:00
Dorian Zedler a238a68638
Chore: remove log
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-08-14 17:46:19 +02:00
Dorian Zedler 6d7009b183
Chore: refactor speed flowchart (fixes #26)
Some checks are pending
continuous-integration/drone/push Build is running
continuous-integration/drone/pr Build is running
2022-08-14 17:43:38 +02:00
Dorian Zedler 0ca2725bf3 Merge pull request 'Fix: send file on android api 30 (fixes #32)' (#33) from fix/share-on-android-api-30 into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #33
2022-08-11 12:49:41 +02:00
Dorian Zedler 447c1135dd
Fix: send file on android api 30 (fixes #32)
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-08-11 12:44:39 +02:00
Dorian Zedler 38bdf15f40 Merge pull request 'Feat: add start numbers to speed flowchart (fixes #30)' (#31) from feat/startnumbers-in-speed-tree into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #31
2022-08-11 12:15:17 +02:00
Dorian Zedler 0920ee6f77
Fix: don't go to comp on start
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-08-11 08:51:05 +02:00
Dorian Zedler a1ea8a4c65
Fix: don't unlock speed flowchart
Some checks reported errors
continuous-integration/drone/push Build is running
continuous-integration/drone/pr Build was killed
2022-08-11 08:49:37 +02:00
Dorian Zedler 21c479cd55
Feat: add start numbers to speed flowchart (fixes #30)
Some checks reported errors
continuous-integration/drone/push Build is running
continuous-integration/drone/pr Build was killed
2022-08-11 08:47:22 +02:00
Dorian Zedler 766da23e26
Merge branch 'main' of ssh://itsblue.dev/blueROCK/app
All checks were successful
continuous-integration/drone/push Build is passing
2022-08-10 09:32:35 +02:00
Dorian Zedler 496d66a521
Chore: update screenshots 2022-08-10 09:32:11 +02:00
Dorian Zedler 43d98bd43e Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-08-08 23:48:26 +02:00
Dorian Zedler 30471725d0 Update '.drone.yml'
Some checks are pending
continuous-integration/drone/push Build is running
2022-08-08 23:48:09 +02:00
Dorian Zedler 88ebde27f2
Chore: update version and changelog
All checks were successful
continuous-integration/drone/push Build is passing
2022-08-06 22:00:37 +02:00
Dorian Zedler 4bf2e2b3b3
Merge branch 'master' of ssh://itsblue.dev/blueROCK/app
Some checks are pending
continuous-integration/drone/push Build is running
2022-08-06 21:56:17 +02:00
Dorian Zedler d234474233
Chore: remove app.bluerock.dev from domains 2022-08-06 21:55:59 +02:00
Dorian Zedler 78659fe769 Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-08-03 12:52:55 +02:00
Dorian Zedler 99f49e0798
Merge branch 'master' of ssh://itsblue.dev/blueROCK/app
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-08-03 12:52:08 +02:00
Dorian Zedler 455c4044c2
Merge branch 'getcommit' 2022-08-03 12:51:57 +02:00
Dorian Zedler ce67df3416
Chore: update openssl lib path 2022-08-03 12:50:15 +02:00
Dorian Zedler 53460b2091
Add fastlane and ressources 2022-08-01 14:30:57 +02:00
Dorian Zedler 2f3f0488da Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-08-01 12:00:43 +02:00
Dorian Zedler a026912929 Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-30 14:35:59 +02:00
Dorian Zedler 551c6ffb6d Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-30 14:22:04 +02:00
Dorian Zedler 55a5aac4e1 Update '.drone.yml'
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-07-30 13:52:34 +02:00
Dorian Zedler 21b88575fb Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-29 14:40:16 +02:00
Dorian Zedler fb17ec52cd Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-29 14:35:31 +02:00
Dorian Zedler 6b91988ac5 Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-29 14:30:09 +02:00
Dorian Zedler 75d7212fd9 Update '.drone.yml'
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2022-07-29 14:29:52 +02:00
Dorian Zedler 3a735e2cf4 Update '.drone.yml'
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2022-07-29 14:29:01 +02:00
Dorian Zedler c92362f07a Update '.drone.yml'
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2022-07-29 14:26:45 +02:00
Dorian Zedler 3492e6c511 Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-29 14:19:01 +02:00
Dorian Zedler 5cfad624a3 Update '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-29 14:09:49 +02:00
Dorian Zedler dabb2473b3
Feat: Add fastlande stuff 2022-07-29 09:23:41 +02:00
Dorian Zedler d33748a06d Update '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-29 09:06:39 +02:00
Dorian Zedler 1cd7ad6649 Update '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-29 08:49:02 +02:00
Dorian Zedler d881b203fa Add '.drone.yml'
Some checks reported errors
continuous-integration/drone Build encountered an error
2022-07-29 08:48:09 +02:00
Dorian Zedler 175c2ea264
Merge branch 'version/0.5.1' 2021-10-20 21:41:28 +02:00
Dorian Zedler a2e6cdccb9
Version 0.5.1 2021-07-06 09:17:05 +02:00
Dorian Zedler f1d6915c52 Merge branch 'fix/in-appPurchase' into 'master'
Fix in-app purchase

Closes #25

See merge request dorian/blueROCK!2
2021-07-06 07:12:44 +00:00
Dorian Zedler f422aecb77
Fix in-app purchase 2021-07-06 09:11:15 +02:00
39 changed files with 1202 additions and 617 deletions

30
.drone.yml Normal file
View file

@ -0,0 +1,30 @@
kind: pipeline
name: default
steps:
- name: submodules
image: alpine/git
commands:
- git submodule update --init --recursive
- name: build app
image: itsblue.dev/plugins/qt-android:5.15.5-4
settings:
qmake_arguments: "CONFIG+=release"
androiddeployqt_arguments: "--android-platform android-31 --aab"
android_keystore_data:
from_secret: android-release-keystore
android_keystore_alias: "bmca"
android_keystore_store_pass:
from_secret: android-release-keystore-password
- name: release
image: plugins/gitea-release
settings:
base_url: https://itsblue.dev
api_key:
from_secret: gitea-token
files:
- outputs/apk/release/android-build-release-signed.apk
- outputs/bundle/release/android-build-release.aab
when:
event: tag

3
.gitignore vendored
View file

@ -1,2 +1,5 @@
*.pro.user*
*.DS_Store
.bundle
vendor
outputs

View file

@ -3,8 +3,38 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
# [0.8.0] - 2023-06-05
### Added
- Show both results in speed quali
# [0.6.0] - UNRELEASED
# [0.7.3] - 2023-05-02
### Removed
- Im-App purchase on android
# [0.7.1] - 2022-08-22
### Fixed
- remove unused ACCESS_FINE_LOCATION permission
- missing openssl libraries which lead to an error in the https connection
# [0.7.0] - 2022-08-14
### Added
- Startnumbers in speed flowchart
### Fixed
- Sharing the poster on Android
- Rendering the speed flowchart when starting at 1/2-Final
### Changed
- The qualification rank is now smaller and grayed out in the speed flowchart
### Removed
- URL handler for app.bluerock.dev, it is not needed
# [0.6.1] - 2021-10-20
### Added
- Privacy policy link to comply with google play policies
# [0.6.0] - 2021-08-07
### Changed
- The subtitle in results and startlists is now the route name instead of the category name
@ -19,6 +49,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Fixed
- Rare issue with missing background in boulder result rect
# [0.5.1] - 2021-07-06
### Fixed
- In-app purchase
# [0.05] - 2021-06-07
### Changed
- the boulder result rect doesn't have a background if there is no result now

3
Gemfile Normal file
View file

@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "fastlane"

218
Gemfile.lock Normal file
View file

@ -0,0 +1,218 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.5)
rexml
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.612.0)
aws-sdk-core (3.131.5)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.58.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.1)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
declarative (0.0.20)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.8.1)
emoji_regex (3.2.3)
excon (0.92.4)
faraday (1.10.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.208.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.25.0)
google-apis-core (>= 0.7, < 2.a)
google-apis-core (0.7.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.13.0)
google-apis-core (>= 0.7, < 2.a)
google-apis-playcustomapp_v1 (0.10.0)
google-apis-core (>= 0.7, < 2.a)
google-apis-storage_v1 (0.18.0)
google-apis-core (>= 0.7, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0)
google-cloud-storage (1.37.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.2.0)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.5)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.1)
json (2.6.2)
jwt (2.4.1)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.1.2)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
naturally (2.2.1)
optparse (0.1.1)
os (1.1.4)
plist (3.6.0)
public_suffix (4.0.7)
rake (13.0.6)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.5)
rouge (2.0.7)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.17.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.8)
CFPropertyList
naturally
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
webrick (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.22.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
x86_64-linux
DEPENDENCIES
fastlane
BUNDLED WITH
2.3.19

View file

@ -2,7 +2,7 @@
<manifest package="com.itsblue.blueROCK" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
@ -10,7 +10,7 @@
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="de.itsblue.blueROCK.MainActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleInstance">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="de.itsblue.blueROCK.MainActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleInstance" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@ -75,8 +75,6 @@
<category android:name="android.intent.category.BROWSABLE"/>
<!-- Accepts URIs that begin with "https://l.bluerock.dev/” -->
<data android:scheme="https" android:host="l.bluerock.dev" android:pathPattern=".*"/>
<!-- Accepts URIs that begin with "https://app.bluerock.dev” -->
<data android:scheme="https" android:host="app.bluerock.dev" android:pathPattern=".*"/>
</intent-filter>
</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
@ -87,4 +85,9 @@
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
</manifest>

View file

@ -260,9 +260,9 @@ public class QShareUtils
sendIntent.setType(mimeType);
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
sendIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
return createCustomChooserAndStartActivity(sendIntent, title, requestId, uri);
QtNative.activity().startActivity(Intent.createChooser(sendIntent, null));
return true;
}
public static boolean viewFile(String filePath, String title, String mimeType, int requestId) {

View file

@ -1,7 +1,7 @@
QT += quick qml quickcontrols2 purchasing
QT += quick qml quickcontrols2
CONFIG += c++11
VERSION = 0.6.1
VERSION = 0.8.0
TARGET = blueROCK
# The following define makes your compiler emit warnings if you use
@ -89,13 +89,15 @@ android {
ANDROID_VERSION_NAME = $$VERSION
ANDROID_VERSION_CODE = $$droidVersionCode($$ANDROID_VERSION_NAME)
ANDROID_TARGET_SDK_VERSION = 29
ANDROID_TARGET_SDK_VERSION = 31
include(/home/dorian/Android/Sdk/android_openssl/openssl.pri)
ANDROID_HOME = $$(ANDROID_HOME)
include($$ANDROID_HOME/android_openssl/openssl.pri)
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
}
ios {
QT += purchasing
OBJECTIVE_SOURCES += sources/shareUtils/ios/iosshareutils.mm \
sources/iospermissionutils.mm \
sources/shareUtils/ios/docviewcontroller.mm

2
fastlane/Appfile.android Normal file
View file

@ -0,0 +1,2 @@
json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
package_name("com.itsblue.speedclimbing_stopwatch") # e.g. com.krausefx.app

38
fastlane/Fastfile Normal file
View file

@ -0,0 +1,38 @@
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
# https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
# https://docs.fastlane.tools/plugins/available-plugins
#
# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane
default_platform(:android)
platform :android do
desc "Runs all the tests"
lane :test do
gradle(task: "test")
end
desc "Submit a new Beta Build to Crashlytics Beta"
lane :beta do
gradle(task: "clean assembleRelease")
crashlytics
# sh "your_script.sh"
# You can also use other beta testing services here
end
desc "Deploy a new version to the Google Play"
lane :deploy do
gradle(task: "clean assembleRelease")
upload_to_play_store
end
end

View file

@ -4,8 +4,7 @@
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:l.bluerock.dev</string>
<string>applinks:app.bluerock.dev</string>
<string>applinks:l.bluerock.dev</string>
</array>
</dict>
</plist>

2
qzxing

@ -1 +1 @@
Subproject commit cfc728583b867e157bd27e8b7c239c05a081e562
Subproject commit 6ea2b31e26db9d43db027ba207f5c73dc9d759fc

View file

@ -0,0 +1,262 @@
import QtQuick 2.10
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
Row {
id: multiResRow
property bool active: parseInt(widgetData[ "route_order" ]) > -1 && boulderResRep.model > 0
height: parent.height
width: active ? parent.width * 0.75:0
enabled: parseInt(widgetData[ "route_order" ]) > -1 && boulderResRep.model > 0
Repeater {
id: boulderResRep
model: parseInt(widgetData[ "route_num_problems" ])
function getDataForIcon(index){
// TODO: clean
var resultString = widgetData[ "participants" ][partDel.thisIndex]["boulder"+(index+1)]
var numTrys = widgetData[ "participants" ][partDel.thisIndex]["try"+(index+1)]
var resultList = []
if (resultString !== undefined) {
resultString = resultString.replace("t", "")
resultString = resultString.replace("z", "")
resultString = resultString.replace("b", "")
resultList = resultString.split(" ")
while (resultList.length < 2){
resultList.unshift(0)
}
}
else {
resultList = [-1,-1]
}
if (numTrys !== undefined) {
resultList.push(numTrys)
}
else {
resultList.push(-1)
}
return resultList
}
delegate: Item {
id: boulderResItm
anchors.verticalCenter: parent.verticalCenter
width: parent.width / ( boulderResRep.model )
height: parent.height
Canvas {
id: boulderResCv
property var resultData: boulderResRep.getDataForIcon(index)
onResultDataChanged: {
boulderResCv.requestPaint()
}
anchors.centerIn: parent
height: parent.height > parent.width ? parent.width * 0.9:parent.height * 0.9
width: height
onPaint: {
var width = 24//boulderResCv.width * 0.9
var height = width
var radius = width * 0.3
var offsetX = width * 0.05
var offsetY = height * 0.05
//console.log("drawing result rect with width: " + width + " and height: " + height)
var context = getContext("2d");
// clear all remainings from other routes
context.clearRect(0, 0, width, height);
context.beginPath();
context.moveTo(0 + offsetX + radius, 0 + offsetY);
// top line
context.lineTo(width - radius + offsetX, 0 + offsetY);
// top right corner
context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 0);
// right line
context.lineTo(width + offsetX, height - radius + offsetY);
// bottom right corner
context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI);
// bottom line
context.lineTo(0 + radius + offsetX, height + offsetY);
// bottom left corner
context.arc(radius + offsetY, height - radius + offsetY, radius, 0.5 * Math.PI, Math.PI);
// left line
context.lineTo(0 + offsetX, radius + offsetY);
// top left corner
context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI);
// fill
if(resultData[0] !== -1 || resultData[2] !== -1) {
// if there is a result available -> draw background
context.fillStyle = "#b7b7b7";
}
else {
context.fillStyle = "transparent";
}
context.fill();
// outline
context.lineWidth = 1;
context.strokeStyle = Material.primaryTextColor;
context.stroke();
if(resultData[1] > 0){
// the first triangle
context.beginPath();
// top right corner
context.arc(width-radius + offsetX, radius + offsetY, radius, 1.75 * Math.PI, 0);
// right line
context.lineTo(width + offsetX, height - radius + offsetY);
// bottom right corner
context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI);
// bottom line
context.lineTo(0 + radius + offsetX, height + offsetY);
// bottom left corner
context.arc(radius + offsetX, height - radius + offsetY, radius, 0.5 * Math.PI, 0.75 * Math.PI);
context.closePath();
context.fillStyle = "#44ed38";
context.fill();
// outline
context.lineWidth = 1;
context.strokeStyle = Material.primaryTextColor;
context.stroke();
if(resultData[0] > 0){
// the second triangle
context.beginPath();
// bottom left corner
context.arc(radius + offsetX, height - radius + offsetY, radius, 0.75 * Math.PI, 1 * Math.PI);
// left line
context.lineTo(0 + offsetX, radius + offsetY);
// top left corner
context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI);
// top line
context.lineTo(width - radius + offsetX, 0 + offsetY);
// top right corner
context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 1.75 * Math.PI);
context.closePath();
context.fillStyle = "#44ed38";
context.fill();
// outline
context.lineWidth = 1;
context.strokeStyle = Material.primaryTextColor;
context.stroke();
}
}
}
Label {
id: boulderResTrysLa
anchors.centerIn: parent
height: parent.height / 2
width: parent.width / 2
visible: !boulderResZoneLa.visible && boulderResCv.resultData[2] > 0
fontSizeMode: Text.Fit
font.pixelSize: height
minimumPixelSize: 1
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: "#dd000000"
text: boulderResCv.resultData[2]
}
Label {
id: boulderResZoneLa
anchors {
right: parent.right
bottom: parent.bottom
margins: boulderResCv.height * 0.05
}
height: parent.height / 2
width: parent.width / 2
visible: parseInt(text) > 0
fontSizeMode: Text.Fit
font.pixelSize: height
minimumPixelSize: 1
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: "#dd000000"
text: boulderResCv.resultData[1]
}
Label {
id: boulderResTopLa
anchors {
left: parent.left
top: parent.top
margins: boulderResCv.height * 0.05
}
height: parent.height / 2
width: parent.width / 2
visible: parseInt(text) > 0
fontSizeMode: Text.Fit
font.pixelSize: height
minimumPixelSize: 1
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: "#dd000000"
text: boulderResCv.resultData[0]
}
}
}
}
}

View file

@ -0,0 +1,97 @@
import QtQuick 2.10
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
Row {
id: multiGenResRow
property bool active: (parseInt(widgetData[ "route_order" ]) === -1) && (generalResRep.model > 0)
height: parent.height
width: active ? parent.width - resultLa.width:0
enabled: active
Repeater {
id: generalResRep
property var routes: getRoutes()
model: routes.length
function getRoutes() {
var obj = widgetData["route_names"]
var routes = []
for(var prop in obj) {
// go through the whole array and search for data keys
if (obj.hasOwnProperty(prop) && prop > -1) {
routes.push([prop, obj[prop]])
//console.log("found " + obj[prop] + " at index " + prop)
}
}
routes.sort(function(a, b) {
return a[0] - b[0];
});
return routes
}
delegate: Item {
id: boulderGenResItm
anchors.verticalCenter: parent.verticalCenter
width: parent.width / ( generalResRep.model )
height: parent.height
visible: multiGenResRow.active
Rectangle {
anchors {
left: parent.left
}
width: 1
height: parent.height
visible: index === 0
color: Material.primaryTextColor
}
Rectangle {
anchors {
right: parent.right
}
width: 1
height: parent.height
color: Material.primaryTextColor
}
Label {
id: boulderGenResLa
anchors.centerIn: parent
height: parent.height
width: parent.width * 0.9
fontSizeMode: Text.Fit
font.pixelSize: Math.abs( height * 0.6 )
minimumPixelSize: 1
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: widgetData["participants"][partDel.thisIndex]["result"+(generalResRep.routes[index][0])] === undefined ?
"":
widgetData[ "participants" ][partDel.thisIndex]["result"+(generalResRep.routes[index][0])]
}
}
}
}

View file

@ -33,6 +33,19 @@ ColoredItemDelegate {
NumberAnimation { target: partDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
}
Rectangle {
visible: widgetData["route_quota"] !== null && parseInt(widgetData["route_quota"]) - 1 === thisIndex
anchors {
left: parent.left
right: parent.right
verticalCenter: parent.bottom
}
height: 3
color: Material.primaryTextColor
}
GridLayout {
id: partDelCol
@ -107,370 +120,29 @@ ColoredItemDelegate {
Layout.preferredWidth: partDelCol.showSideBySide ? parent.width * 0.5 : parent.width
Layout.preferredHeight: partDelCol.showSideBySide ? parent.height : parent.height * 0.5
visible: multiResRow.active || multiGenResRow.active || resultLa.acitve
Row {
id: multiResRow
property bool active: parseInt(widgetData[ "route_order" ]) > -1 && boulderResRep.model > 0
height: parent.height
width: active ? parent.width * 0.75:0
enabled: parseInt(widgetData[ "route_order" ]) > -1 && boulderResRep.model > 0
Repeater {
id: boulderResRep
model: parseInt(widgetData[ "route_num_problems" ])
function getDataForIcon(index){
// TODO: clean
var resultString = widgetData[ "participants" ][partDel.thisIndex]["boulder"+(index+1)]
var numTrys = widgetData[ "participants" ][partDel.thisIndex]["try"+(index+1)]
var resultList = []
if (resultString !== undefined) {
resultString = resultString.replace("t", "")
resultString = resultString.replace("z", "")
resultString = resultString.replace("b", "")
resultList = resultString.split(" ")
while (resultList.length < 2){
resultList.unshift(0)
}
}
else {
resultList = [-1,-1]
}
if (numTrys !== undefined) {
resultList.push(numTrys)
}
else {
resultList.push(-1)
}
return resultList
}
delegate: Item {
id: boulderResItm
anchors.verticalCenter: parent.verticalCenter
width: parent.width / ( boulderResRep.model )
height: parent.height
Canvas {
id: boulderResCv
property var resultData: boulderResRep.getDataForIcon(index)
onResultDataChanged: {
boulderResCv.requestPaint()
}
anchors.centerIn: parent
height: parent.height > parent.width ? parent.width * 0.9:parent.height * 0.9
width: height
onPaint: {
var width = 24//boulderResCv.width * 0.9
var height = width
var radius = width * 0.3
var offsetX = width * 0.05
var offsetY = height * 0.05
//console.log("drawing result rect with width: " + width + " and height: " + height)
var context = getContext("2d");
// clear all remainings from other routes
context.clearRect(0, 0, width, height);
context.beginPath();
context.moveTo(0 + offsetX + radius, 0 + offsetY);
// top line
context.lineTo(width - radius + offsetX, 0 + offsetY);
// top right corner
context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 0);
// right line
context.lineTo(width + offsetX, height - radius + offsetY);
// bottom right corner
context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI);
// bottom line
context.lineTo(0 + radius + offsetX, height + offsetY);
// bottom left corner
context.arc(radius + offsetY, height - radius + offsetY, radius, 0.5 * Math.PI, Math.PI);
// left line
context.lineTo(0 + offsetX, radius + offsetY);
// top left corner
context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI);
// fill
if(resultData[0] !== -1 || resultData[2] !== -1) {
// if there is a result available -> draw background
context.fillStyle = "#b7b7b7";
}
else {
context.fillStyle = "transparent";
}
context.fill();
// outline
context.lineWidth = 1;
context.strokeStyle = Material.primaryTextColor;
context.stroke();
if(resultData[1] > 0){
// the first triangle
context.beginPath();
// top right corner
context.arc(width-radius + offsetX, radius + offsetY, radius, 1.75 * Math.PI, 0);
// right line
context.lineTo(width + offsetX, height - radius + offsetY);
// bottom right corner
context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI);
// bottom line
context.lineTo(0 + radius + offsetX, height + offsetY);
// bottom left corner
context.arc(radius + offsetX, height - radius + offsetY, radius, 0.5 * Math.PI, 0.75 * Math.PI);
context.closePath();
context.fillStyle = "#44ed38";
context.fill();
// outline
context.lineWidth = 1;
context.strokeStyle = Material.primaryTextColor;
context.stroke();
if(resultData[0] > 0){
// the second triangle
context.beginPath();
// bottom left corner
context.arc(radius + offsetX, height - radius + offsetY, radius, 0.75 * Math.PI, 1 * Math.PI);
// left line
context.lineTo(0 + offsetX, radius + offsetY);
// top left corner
context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI);
// top line
context.lineTo(width - radius + offsetX, 0 + offsetY);
// top right corner
context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 1.75 * Math.PI);
context.closePath();
context.fillStyle = "#44ed38";
context.fill();
// outline
context.lineWidth = 1;
context.strokeStyle = Material.primaryTextColor;
context.stroke();
}
}
}
Label {
id: boulderResTrysLa
anchors.centerIn: parent
height: parent.height / 2
width: parent.width / 2
visible: !boulderResZoneLa.visible && boulderResCv.resultData[2] > 0
fontSizeMode: Text.Fit
font.pixelSize: height
minimumPixelSize: 1
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: "#dd000000"
text: boulderResCv.resultData[2]
}
Label {
id: boulderResZoneLa
anchors {
right: parent.right
bottom: parent.bottom
margins: boulderResCv.height * 0.05
}
height: parent.height / 2
width: parent.width / 2
visible: parseInt(text) > 0
fontSizeMode: Text.Fit
font.pixelSize: height
minimumPixelSize: 1
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: "#dd000000"
text: boulderResCv.resultData[1]
}
Label {
id: boulderResTopLa
anchors {
left: parent.left
top: parent.top
margins: boulderResCv.height * 0.05
}
height: parent.height / 2
width: parent.width / 2
visible: parseInt(text) > 0
fontSizeMode: Text.Fit
font.pixelSize: height
minimumPixelSize: 1
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: "#dd000000"
text: boulderResCv.resultData[0]
}
}
}
}
visible: boulderResRow.active || generalResRow.active || speedQualificationResRow.active || resultLa.active
BoulderResultRow {
id: boulderResRow
}
Row {
id: multiGenResRow
GeneralResultRow {
id: generalResRow
}
property bool active: ((parseInt(widgetData[ "route_order" ]) === -1) && (generalResRep.model > 0)) ? true:false
height: parent.height
width: active ? parent.width - resultLa.width:0
enabled: ((parseInt(widgetData[ "route_order" ]) === -1) && (generalResRep.model > 0)) ? true:false
Repeater {
id: generalResRep
property var routes: getRoutes()
model: routes.length
function getRoutes() {
var obj = widgetData["route_names"]
var routes = []
for(var prop in obj) {
// go through the whole array and search for data keys
if (obj.hasOwnProperty(prop) && prop > -1) {
routes.push([prop, obj[prop]])
//console.log("found " + obj[prop] + " at index " + prop)
}
}
routes.sort(function(a, b) {
return a[0] - b[0];
});
return routes
}
delegate: Item {
id: boulderGenResItm
anchors.verticalCenter: parent.verticalCenter
width: parent.width / ( generalResRep.model )
height: parent.height
visible: multiGenResRow.active
Rectangle {
anchors {
left: parent.left
}
width: 1
height: parent.height
visible: index === 0
color: Material.primaryTextColor
}
Rectangle {
anchors {
right: parent.right
}
width: 1
height: parent.height
color: Material.primaryTextColor
}
Label {
id: boulderGenResLa
anchors.centerIn: parent
height: parent.height
width: parent.width * 0.9
fontSizeMode: Text.Fit
font.pixelSize: Math.abs( height * 0.6 )
minimumPixelSize: 1
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: widgetData["participants"][partDel.thisIndex]["result"+(generalResRep.routes[index][0])] === undefined ?
"":
widgetData[ "participants" ][partDel.thisIndex]["result"+(generalResRep.routes[index][0])]
}
}
}
SpeedQualificationResultRow {
id: speedQualificationResRow
}
Label {
id: resultLa
property bool acitve: ( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1
property bool active: ( parseInt(widgetData[ "route_num_problems" ]) > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1 && !speedQualificationResRow.active
width: enabled ? parent.width * 0.25:0
height: enabled ? parent.height:0
enabled: ( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1
enabled: active
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter

View file

@ -1,7 +1,7 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QZXing 3.1
import QZXing 3.3
import QtGraphicalEffects 1.0
Dialog {

View file

@ -0,0 +1,289 @@
.pragma library
/**
* Function to extract results from PartiipantFromApi
* @param {ParticipantFromApi} fromApi
* @return {Result[]}
*/
function _extractResults(fromApi) {
const results = Array(7);
const existingResults = Object.keys(fromApi).filter(key =>
key.match(/result[0-9]/),
);
for (const result of existingResults) {
let roundNumber = 0;
const match = result.match(/result([0-9])/);
if (match !== undefined && match !== null) {
roundNumber = parseInt(match[1]);
}
if (roundNumber < 0) {
continue;
}
results[roundNumber] = {
rank: parseInt(fromApi[`result_rank${roundNumber}`] as string),
result: fromApi[`result${roundNumber}`] as string,
};
}
return results;
}
/**
* Function to clean up participants from the api
* @param {ParticipantFromApi} fromApi
* @return {Participant}
*/
function participantFromApiParticipant(
fromApi,
) {
const results = _extractResults(fromApi);
return {
id: fromApi.PerId,
firstName: fromApi.firstname,
lastName: fromApi.lastname,
results: results,
overallRank: fromApi.result_rank,
startNumber: parseInt(fromApi.start_number),
};
}
/**
*
* @param {number} roundNumber
* @param {RouteNames} routeNames
* @return {string | undefined}
*/
function getRoundName(
roundNumber,
routeNames,
) {
if (roundNumber < 2 || roundNumber > 6) return undefined;
return routeNames[roundNumber];
}
/**
*
* @param {string} name
* @return {number}
*/
function getRoundRank(name) {
const match = name.match(/1\/([842])/);
if (match === undefined || match === null || match.length !== 2) {
return 2;
}
return parseInt(match[1]);
}
/**
*
* @param {SpeedRoundPair} pair
* @param {number} roundIndex
*/
function computeWinnerOfPair(pair, roundIndex) {
if (
!(pair.laneA && pair.laneA.participant.results[roundIndex] && pair.laneA.participant.results[roundIndex].rank) ||
!(pair.laneB && pair.laneB.participant.results[roundIndex] && pair.laneB.participant.results[roundIndex].rank)
)
return;
pair.laneA.result = pair.laneA.participant.results[roundIndex];
pair.laneB.result = pair.laneB.participant.results[roundIndex];
if (pair.winner === undefined) {
pair.winner =
pair.laneA.participant.results[roundIndex].rank >
pair.laneB.participant.results[roundIndex].rank
? 'B'
: 'A';
}
}
/**
*
* @param {SpeedRoundPair} pair
* @param {number} roundNumber
* @return {Participant | undefined}
*/
function getWinnerOfPair(
pair,
roundNumber,
) {
computeWinnerOfPair(pair, roundNumber);
return {
['A']: pair.laneA ? pair.laneA.participant : undefined,
['B']: pair.laneB ? pair.laneB.participant : undefined,
['']: undefined,
}[pair.winner ? pair.winner:''];
}
/**
*
* @param {SpeedRoundPair} pair
* @param {number} roundNumber
* @return {Participant | undefined}
*/
function getLooserOfPair(
pair,
roundNumber,
) {
computeWinnerOfPair(pair, roundNumber);
return {
['A']: pair.laneB ? pair.laneB.participant : undefined,
['B']: pair.laneA ? pair.laneA.participant : undefined,
['']: undefined,
}[pair.winner ? pair.winner:''];
}
/**
*
* @param {number} roundIndex index of the new round
* @param {string} roundName name of the new round
* @param {SpeedRound} previousRound
* @param {number} roundRank
* @param {boolean} takeLooser
* @return {SpeedRound}
*/
function computeRoundFromPreviousRound(
roundIndex,
roundName,
previousRound,
roundRank,
takeLooser = false,
) {
const getAdvancingParticipant = takeLooser
? getLooserOfPair
: getWinnerOfPair;
const nextRoundPairs = new Array(roundRank / 2).fill(0).map((_, i) => {
const laneAParticipant = getAdvancingParticipant(
previousRound.pairs[i * 2],
previousRound.roundIndex,
);
const laneBParticipant = getAdvancingParticipant(
previousRound.pairs[i * 2 + 1],
previousRound.roundIndex,
);
return {
laneA:
laneAParticipant === undefined
? undefined
: {
participant: laneAParticipant,
},
laneB:
laneBParticipant === undefined
? undefined
: {
participant: laneBParticipant,
},
};
});
return {
pairs: nextRoundPairs,
roundIndex: roundIndex,
roundName: roundName,
};
}
/**
*
* @param {SpeedCompetitionCategoryResult} result The result to process
* @return {SpeedFlowchartResult}
*/
function convertResultsToSpeedFlowchartResult(
result,
) {
const rounds = [];
const convertedParticipants = result.participants
.map(fromApi => participantFromApiParticipant(fromApi))
// sort by qualification result
.sort((a, b) => a.results[0].rank - b.results[0].rank);
const roundIndices = Object.keys(result.route_names)
.map(number => parseInt(number))
.filter(number => number > 0);
// process first round
const firstRoundName = getRoundName(roundIndices[0], result.route_names);
const tmpRoundName = getRoundName(roundIndices[0], result.route_names);
const firstRoundRank = getRoundRank(
tmpRoundName ? tmpRoundName:'',
);
const getOpponent = (ofRank) => {
return convertedParticipants[firstRoundRank * 2 - 1 - ofRank];
};
// Should be:
// 0, 1, 2, 3, 4, 5, 6, 7
// - 1,16, 8, 9, 4,13, 5,12, 2,15, 7,10, 3,14, 6,11
// - 1, 8, 4, 5, 2, 7, 3, 6 for firstRoundNumber=8
// - 1, 4, 2, 3 for firstRoundNumber=4
// - 1, 2 for firstRoundNumber=2
// TODO: come up with a proper alogorithm maybe
const ranksOfLaneAInOrder = [
[1, 2],
[1, 4, 2, 3],
[1, 8, 4, 5, 2, 7, 3, 6],
][Math.floor(firstRoundRank / 4)];
const firstRoundPairs = ranksOfLaneAInOrder.map(rank => {
return {
laneA: { participant: convertedParticipants[rank - 1] },
laneB: { participant: getOpponent(rank - 1) },
};
});
const firstRound = {
pairs: firstRoundPairs,
roundIndex: roundIndices[0],
roundName: firstRoundName,
};
rounds.push(firstRound);
// compute following rounds
let roundIndex = roundIndices[1];
for (let roundRank = firstRoundRank; roundRank > 2; roundRank /= 2) {
rounds.push(
computeRoundFromPreviousRound(
roundIndex,
result.route_names[roundIndex] ? result.route_names[roundIndex]:'',
rounds[rounds.length - 1],
roundRank,
),
);
roundIndex++;
}
// compute final and semi final
const semifinalRoundIndex = roundIndex++;
const semifinal = computeRoundFromPreviousRound(
semifinalRoundIndex,
result.route_names[semifinalRoundIndex] ? result.route_names[semifinalRoundIndex]:'',
rounds[rounds.length - 1],
2,
true,
);
computeWinnerOfPair(semifinal.pairs[0], semifinalRoundIndex);
rounds.push(semifinal);
const finalRoundIndex = roundIndex;
const final = computeRoundFromPreviousRound(
finalRoundIndex,
result.route_names[finalRoundIndex] ? result.route_names[finalRoundIndex]:'',
rounds[rounds.length - 2],
2,
);
computeWinnerOfPair(final.pairs[0], finalRoundIndex);
rounds.push(final);
return {
rounds: rounds,
};
}

View file

@ -22,6 +22,8 @@ import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.1
import QtGraphicalEffects 1.0
import "SpeedFlowChart.js" as SpeedFlowChart
Item {
id: control
@ -32,8 +34,6 @@ Item {
property int refreshes: 0
property int roundRefreshes: 1
property int roundCount: 0
onFlowchartDataChanged: {
prepareData()
}
@ -42,187 +42,26 @@ Item {
if(!control.enabled || control.flowchartData === undefined || control.flowchartData['route_names'] === undefined)
return
/*refreshes += 1
if(refreshes > 2){
roundRefreshes += 1
}
console.log("refreshes: " + refreshes + " rounds: " + roundRefreshes)
// create competition like data (just testing)
for(var part in flowchartData['participants']){
if(flowchartData['participants'].hasOwnProperty(part)){
for(var r = 2 + roundRefreshes; r < 7; r++){
delete flowchartData['participants'][part]["result"+r]
delete flowchartData['participants'][part]["result_rank"+r]
}
if(parseInt(flowchartData['participants'][part]["result_rank0"]) > 14 + refreshes) {
delete flowchartData['participants'][part]["result_rank2"]
}
}
}
delete flowchartData['route_names'][2]
delete flowchartData['route_names'][3]
delete flowchartData['route_names'][4]
delete flowchartData['route_names'][5]
delete flowchartData['route_names'][6]
*/
//flowchartData['route_names'] = flowchartData['route_names'].slice(0,)
// array to store the restructured data
var allData = []
control.allFlowchartData = []
control.rounds = Object.keys(control.flowchartData['route_names']).length > 2 ? control.flowchartData['route_names']["2"].includes("8") ? 2:1 : 0
//console.log(JSON.stringify(flowchartData['route_names']))
for(var round in flowchartData['route_names']){
if(flowchartData['route_names'].hasOwnProperty(round) && parseInt(round) >= 0){
//console.log(round)
if(parseInt(round) === 0){
// this is the first round
// find pairs (always wors vs. best (1-16; 1-15; ...)) (they are sorted by the rank of the better athlete (1-2-3-4-5-6-7-8)
var qualificationResults = []
for(var x = 0; x < flowchartData['participants'].length; x++){
qualificationResults.push(flowchartData['participants'][x])
}
qualificationResults.sort(function(a, b) {
return parseInt(a["result_rank0"]) - parseInt(b["result_rank0"]);
});
var nextRoundPairs = []
var totalMatches = (parseInt(Object.keys(control.flowchartData['route_names']).length > 2 ? control.flowchartData['route_names']["2"].includes("8") ? 2:1 : 0) + 2)
var nextRoundMatches = Math.pow(2, totalMatches-1)
for(var i = 0; i < nextRoundMatches; i++){
nextRoundPairs.push([qualificationResults[i], qualificationResults[nextRoundMatches*2-i-1]])
}
// build second round pairs (sorted by the rank of the better athlete and worst vs. best (1-8; 2-7; ... ))
var sortedFirstRoundPairs = []
for(i = 0; i < nextRoundMatches; i += 1){
sortedFirstRoundPairs.push([nextRoundPairs.shift(), nextRoundPairs.pop()])
}
// sort these pairs (containing two pairs of athletes) by the rank of the better athlete (1-4;2-3)
var finalSortedFirstRoundPairs = []
for(i=0; i < nextRoundMatches/4; i++){
finalSortedFirstRoundPairs.push(sortedFirstRoundPairs[i])
finalSortedFirstRoundPairs.push(sortedFirstRoundPairs[nextRoundMatches/2-i-1])
}
// convert the list of pairs of pairs of athletes back to a single list of pairs of athletes
var finalFirstRoundPairs = []
for(i=0; i < finalSortedFirstRoundPairs.length; i++){
finalFirstRoundPairs.push(finalSortedFirstRoundPairs[i][0])
finalFirstRoundPairs.push(finalSortedFirstRoundPairs[i][1])
}
// push the first round to all data
finalFirstRoundPairs.push(2)
finalFirstRoundPairs.push(flowchartData['route_names'][2])
allData.push(finalFirstRoundPairs)
}
else if(parseInt(round) > 0 ){
// this is not the first round
var nextRound = []
// only used when the current round is the 1/2 final
var smallFinal = []
var Final = []
for(var i = 0; i < allData[allData.length-1].length-2; i+=1){
var thisPair = allData[allData.length-1][i]
var thisWinner
var thisLooser
var thisWinnerIsFirstOfNewPair = i%2 === 0
if(thisPair[0] === undefined || thisPair[1] === undefined){
continue
}
if(thisWinnerIsFirstOfNewPair){
nextRound.push([])
}
//thisPair[0]["result_rank"] = thisPair[0]["result_rank"+round]
//thisPair[1]["result_rank"] = thisPair[1]["result_rank"+round]
if(parseInt(thisPair[0]["result_rank"+round]) < parseInt(thisPair[1]["result_rank"+round])){
thisWinner = thisPair[0]
thisLooser = thisPair[1]
}
else if(parseInt(thisPair[0]["result_rank"+round]) > parseInt(thisPair[1]["result_rank"+round])) {
thisWinner = thisPair[1]
thisLooser = thisPair[0]
}
else {
// no result yet!!
//console.log("got no winner yet, rank 0: " + thisPair[0]["result_rank"+round] + " rank 1: " + thisPair[1]["result_rank"+round])
continue
}
//console.log(thisWinner['firstname']+" has won in round " + round)
if(round - control.rounds === 2){
// if we are in the 1/2 final
Final.push(thisWinner)
smallFinal.push(thisLooser)
}
else {
nextRound[nextRound.length-1].push(thisWinner)
}
}
if(smallFinal.length > 0 && Final.length > 0){
// Final
allData.push([Final, parseInt(round)+2, flowchartData['route_names'][String(parseInt(round)+2)] + " / " + flowchartData['route_names'][String(parseInt(round)+1)] ])
// small Final
allData.push([smallFinal, parseInt(round)+1])
//break
}
else {
nextRound.push(parseInt(round) + 1 )
nextRound.push(flowchartData['route_names'][parseInt(round) + 1])
allData.push(nextRound)
}
}
}
}
control.allFlowchartData = allData
control.roundCount = (parseInt(Object.keys(control.flowchartData['route_names']).length > 2 ? control.flowchartData['route_names']["2"].includes("8") ? 2:1 : 0) + 2)
//console.log(JSON.stringify(allData))
var flowchartResult = SpeedFlowChart.convertResultsToSpeedFlowchartResult(control.flowchartData)
const l = flowchartResult.rounds.length;
const dummy = { dummy: true };
flowchartResult.rounds[l - 2].pairs = [
dummy,
...flowchartResult.rounds[l - 1].pairs,
...flowchartResult.rounds[l - 2].pairs,
];
flowchartResult.rounds[l - 2].roundName = `${
flowchartResult.rounds[l - 1].roundName
} / ${flowchartResult.rounds[l - 2].roundName}`;
flowchartResult.rounds.pop();
control.allFlowchartData = flowchartResult
}
ListView {
id: roundListView
property int columnWidth: height * 0.3
property int columnWidth: height * 0.4
property int columnHeight: height
anchors {
@ -238,19 +77,18 @@ Item {
orientation: ListView.LeftToRight
boundsBehavior: ListView.StopAtBounds
model: control.roundCount
model: control.allFlowchartData.rounds.length
delegate: Item {
id: roundItem
property int thisIndex: index
property int thisRound: thisRoundIsValid ? control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-2]:-1
property bool thisRoundIsValid: control.allFlowchartData !== undefined
&& control.allFlowchartData[roundItem.thisIndex] !== undefined
&& control.allFlowchartData[roundItem.thisIndex].length > 2
property var thisData: control.allFlowchartData.rounds[thisIndex]
property bool thisIsLastRound: thisIndex === control.roundCount - 1
property bool thisIsSemiFinal: thisIndex === control.roundCount - 2 && rectRep.model === 2
property bool thisRoundIsValid: true
property bool thisIsLastRound: thisIndex === roundListView.model - 1
property bool thisIsSemiFinal: thisIndex === roundListView.model - 2
property int tileSize: (roundItem.height / 8 - roundNameLa.height) * 1.45
@ -272,50 +110,26 @@ Item {
font.pixelSize: height * 0.6
minimumPixelSize: 1
font.bold: true
text: roundItem.thisRoundIsValid
&& control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-1] !== undefined ?
control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-1] :
text: roundItem.thisData.roundName ?
roundItem.thisData.roundName :
"-"
}
Repeater {
id: rectRep
model: Math.max( Math.pow(2, control.roundCount-1) * Math.pow(0.5, (roundItem.thisIndex)), 2)
model: roundItem.thisData.pairs.length
delegate: Item {
id: matchItm
property bool lowerPart: (index%2 > 0)
property var matchData: roundItem.thisRoundIsValid ?
control.allFlowchartData[
thisIsSmallFinal ?
roundItem.thisIndex+1 :
roundItem.thisIndex
][ thisIsSmallFinal ? 0:matchItm.thisIndex]:
undefined
property var thisData: roundItem.thisData.pairs[index]
property int thisIndex: index
property int thisRound: parseInt(roundItem.thisRound) - (thisIsSmallFinal ? 1:0)
property var thisMatchData: thisMatchDataIsValid ? matchData:[]
property bool thisMatchDataIsValid: (matchData !== undefined && matchData !== null && typeof matchData === "object" && matchData.length > 0)
property bool thisMatchIsOver: thisMatchDataIsValid && thisMatchData[0]['result_rank'+thisRound] !== undefined && thisMatchData[1]['result_rank'+thisRound] !== undefined
property bool thisIsFinal: roundItem.thisIsLastRound && thisIndex === rectRep.model - 2
property bool thisIsSmallFinal: roundItem.thisIsLastRound && thisIndex === rectRep.model - 1
property int winnerIndex: thisMatchIsOver ? (parseInt(thisMatchData[0]['result_rank'+thisRound]) < parseInt(thisMatchData[1]['result_rank'+thisRound]) ? 0:1) : -1
height: !roundItem.thisIsLastRound ?
(roundItem.height - roundNameLa.height) / rectRep.model - roundCol.spacing :
(thisIsFinal ?
(roundItem.height - roundNameLa.height) * 0.5 + roundItem.tileSize * 0.5 :
(roundItem.height - roundNameLa.height) - (roundItem.height - roundNameLa.height) * 0.5 + roundItem.tileSize * 0.5
)
height: (roundItem.height - roundNameLa.height) / rectRep.model - roundCol.spacing
width: roundItem.width
onMatchDataChanged: {
onThisDataChanged: {
fadeInPa.start()
}
@ -365,6 +179,7 @@ Item {
RectangularGlow {
id: effect
visible: matchRect.visible
anchors.fill: matchRect
glowRadius: 0
spread: 0
@ -381,13 +196,12 @@ Item {
bottom: matchItm.thisIsFinal ? parent.bottom:undefined
}
//anchors.verticalCenterOffset: matchItm.lowerPart ? 10:10
visible: !matchItm.thisData.dummy
width: parent.width
height: roundItem.tileSize
//scale: 0.9
color: Material.dialogColor
border.color: "lightgrey"
border.width: 0
radius: height * 0.2
Column {
@ -401,6 +215,13 @@ Item {
model: 2
delegate: RowLayout {
id: laneRow
property var thisData: index === 0 ? matchItm.thisData.laneA : matchItm.thisData.laneB
property var participant: thisData ? thisData.participant : undefined
property var result: thisData ? thisData.result : undefined
property var lane: index === 0 ? "A":"B"
property bool isWinner: matchItm.thisData.winner === laneRow.lane
height: parent.height / 2 - parent.spacing
width: parent.width
@ -409,53 +230,90 @@ Item {
Label {
Layout.preferredHeight: parent.height
Layout.preferredWidth: parent.width * 0.15
Layout.preferredWidth: parent.width * 0.08
height: parent.height
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.pixelSize: height * 0.7
fontSizeMode: Text.Fit
minimumPixelSize: 1
font.pixelSize: height * 0.4
font.bold: true
opacity: 0.7
text: matchItm.thisMatchData[index] !== undefined ?
text: laneRow.participant ?
(
parseInt(matchItm.thisMatchData[index]['result_rank0']) < 10 ?
matchItm.thisMatchData[index]['result_rank0'] + " ":
matchItm.thisMatchData[index]['result_rank0']
laneRow.participant.results[0].rank < 10 ?
laneRow.participant.results[0].rank + " ":
laneRow.participant.results[0].rank
):
""
}
Label {
Layout.preferredHeight: parent.height
Layout.fillWidth: true
Layout.maximumWidth: parent.width * 0.6
height: parent.height
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.pixelSize: height * 0.5
font.bold: matchItm.winnerIndex === index
font.bold: laneRow.isWinner
elide: "ElideRight"
color: matchItm.winnerIndex === index ? Material.color(Material.Green):Material.primaryTextColor
color: laneRow.isWinner ? Material.color(Material.Green):Material.primaryTextColor
text: matchItm.thisMatchData[index] !== undefined ? matchItm.thisMatchData[index]['firstname'] + " " + matchItm.thisMatchData[index]['lastname'] :"-"
text: laneRow.participant ? laneRow.participant.firstName + " " + laneRow.participant.lastName :"-"
}
Rectangle {
Layout.preferredHeight: parent.height * 0.8
Layout.preferredWidth: parent.width * 0.13
visible: laneRow.participant && laneRow.participant.startNumber ? true:false
radius: height / 2
border.width: parent.height * 0.03
border.color: Material.frameColor
color: "transparent"
Label {
anchors.fill: parent
padding: parent.parent.height * 0.1
leftPadding: padding * 2
rightPadding: leftPadding
font.pixelSize: parent.height * 0.5
font.bold: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: laneRow.participant ? laneRow.participant.startNumber:""
}
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
Label {
Layout.preferredHeight: parent.height
Layout.preferredWidth: parent.width * 0.15
height: parent.height
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignRight
font.pixelSize: height * 0.5
font.bold: laneRow.isWinner
text: matchItm.thisMatchData[index] !== undefined && matchItm.thisMatchData[index]['result'+matchItm.thisRound] !== undefined ?
( parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]) ?
(parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]).toFixed(2))
: matchItm.thisMatchData[index]['result'+matchItm.thisRound] )
: "-"
color: laneRow.isWinner ? Material.color(Material.Green):Material.primaryTextColor
text: laneRow.result ? laneRow.result.result : ""
}
}
}
@ -478,7 +336,7 @@ Item {
id: blueRockBadgeComponent
BlueRockBadge {
width: roundItem.width
width: roundItem.width * 0.8
height: width * 0.25
}
}

View file

@ -9,7 +9,7 @@ Rectangle {
// always unlock in debug mode
//property bool unlocked: QT_DEBUG || appSettings.read("speedBackendPurchase") === "1"
property bool unlocked: appSettings.read("speedBackendPurchase") === "1"
property bool unlocked: Qt.platform.os !== "iso" || appSettings.read("speedBackendPurchase") === "1"
Component.onCompleted: {
console.warn("unlocked:", appSettings.read("speedBackendPurchase"))

View file

@ -0,0 +1,79 @@
import QtQuick 2.10
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
Row {
id: control
property bool active: (parseInt(widgetData[ "route_order" ]) === 0) && (widgetData["discipline"] === "speed") && (thisData["result_l"] !== undefined || thisData["result_r"] !== undefined)
property var thisData: widgetData["participants"][partDel.thisIndex]
height: parent.height
width: active ? parent.width - resultLa.width:0
enabled: active
Repeater {
id: generalResRep
model: 2
delegate: Item {
id: boulderGenResItm
anchors.verticalCenter: parent.verticalCenter
width: parent.width / ( generalResRep.model )
height: parent.height
visible: control.active
Rectangle {
anchors {
left: parent.left
}
width: 1
height: parent.height
visible: index === 0
color: Material.primaryTextColor
}
Rectangle {
anchors {
right: parent.right
}
width: 1
height: parent.height
color: Material.primaryTextColor
}
Label {
id: boulderGenResLa
property string thisKey: "result_"+(["l", "r"][index])
anchors.centerIn: parent
height: parent.height
width: parent.width * 0.9
fontSizeMode: Text.Fit
font.pixelSize: Math.abs( height * 0.6 )
minimumPixelSize: 1
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.bold: thisData["result_l"] !== undefined && thisData["result_r"] !== undefined && thisData[thisKey] === thisData["result"]
text: (control.thisData[thisKey] !== undefined && parseInt(control.thisData[thisKey]) > 0) ? control.thisData[thisKey] : ""
}
}
}
}

View file

@ -1,7 +1,7 @@
import QtQuick 2.0
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QZXing 3.1
import QZXing 3.3
import QtMultimedia 5.12
import QtQuick.Shapes 1.12
import QtQuick.Controls.Material 2.12

View file

@ -171,7 +171,7 @@ Page {
id: aboutBluerockDisclaimerDialog
Material.theme: root.Material.theme
//% "privacy policy"
title: "blueROCK v" + APP_VERSION + "<br>By <a href=\"https://itsblue.de\">Itsblue Development</a>, <a href=\"https://itsblue.de/apps/bluerock\">" + qsTrId("#privacyPolicy") + "</a>"
title: "blueROCK v" + APP_VERSION + "<br>By <a href=\"https://itsblue.de\">Itsblue Development</a>, <a href=\"https://itsblue.de/apps/bluerock/privacypolicy.html\">" + qsTrId("#privacyPolicy") + "</a>"
//% "This app was built using the <a href='https://qt.io'>Qt Framework</a> licensed under the <a href='https://www.gnu.org/licenses/lgpl-3.0.en.html'>GNU lgplV3 license</a>.<br><br>This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>, the source code can be found <a href='https://itsblue.dev/blueROCK/app'>here</a>.<br><br>Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>."
content: qsTrId("#aboutBluerockDisclaimer")
}

View file

@ -190,17 +190,6 @@ Window {
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=11501&cat=GER_M")
}
FontLoader {
id: fa5solid
source: "qrc:/fonts/fa5solid.otf"

View file

@ -31,5 +31,9 @@
<file>Components/SharePopup.qml</file>
<file>Pages/QrCodeScanPage.qml</file>
<file>Components/MovingLabel.qml</file>
<file>Components/SpeedFlowChart.js</file>
<file>Components/BoulderResultRow.qml</file>
<file>Components/GeneralResultRow.qml</file>
<file>Components/SpeedQualificationResultRow.qml</file>
</qresource>
</RCC>

BIN
resources/shared/badge.xcf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View file

@ -33,6 +33,9 @@ int main(int argc, char *argv[])
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QGuiApplication app(argc, argv);
QFont f = app.font();
f.setPixelSize(16);
app.setFont(f);
// translation
QString localeName = QLocale::system().bcp47Name();