Conflicts:
	pubspec.lock
	pubspec.yaml
This commit is contained in:
Ady
2021-12-14 13:55:42 +01:00
141 changed files with 1217 additions and 458 deletions

View File

@@ -1,8 +1,8 @@
name: Google Play release
on:
push:
branches:
- 'release/*'
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
jobs:
deploy_android:
@@ -14,45 +14,78 @@ jobs:
- name: Setup Java
uses: actions/setup-java@v1
with:
java-version: 12.x
java-version: 11.x
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '2.7'
- name: Decrypt config files
run: cd ./android/fastlane/envfiles && chmod +x ./decrypt_secrets.sh && ./decrypt_secrets.sh
env:
DECRYPTKEY_PLAYSTORE: ${{ secrets.DECRYPTKEY_PLAYSTORE }}
DECRYPTKEY_PLAYSTORE_SIGNING_KEY: ${{ secrets.DECRYPTKEY_PLAYSTORE_SIGNING_KEY }}
DECRYPTKEY_PROPERTIES: ${{ secrets.DECRYPTKEY_PROPERTIES }}
ruby-version: '3'
- name: Setup Flutter
uses: subosito/flutter-action@v1
with:
channel: 'stable'
flutter-version: '2.2.x'
flutter-version: '2.5.x'
- run: dart --version
- run: flutter --version
- name: Decrypt config files
run: |
cd ./fastlane/android/envfiles
chmod +x ./decrypt_secrets.sh
./decrypt_secrets.sh
env:
DECRYPTKEY_PLAYSTORE: ${{ secrets.DECRYPTKEY_PLAYSTORE }}
DECRYPTKEY_PLAYSTORE_SIGNING_KEY: ${{ secrets.DECRYPTKEY_PLAYSTORE_SIGNING_KEY }}
DECRYPTKEY_PROPERTIES: ${{ secrets.DECRYPTKEY_PROPERTIES }}
- name: Flutter info
run: |
dart --version
flutter --version
- name: Install Flutter dependencies
run: flutter pub get
- name: Extract version information
id: get_version
run: |
echo ::set-output name=VERSION_V::$(echo $GITHUB_REF | cut -d / -f 3)
echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3 | cut -c 2-)
echo ::set-output name=BUILD::$(flutter pub run cider version | cut -d '+' -f 2)
# Note: the original tag that triggered the workflow is in the form vX.Y.Z
# but the pubspec.yaml is committed in the commit after that one.
# Since we need the tag to point to the correct commit for other workflows
# such as f-droid we need a way to correct it. Only moving the tag
# would not work, as it would trigger this workflow again. So as
# a workaround, we use the v-tag to trigger this workflow, add a new
# one without the v and push it.
- name: Bump version
uses: maierj/fastlane-action@v2.0.0
with:
lane: setVersion
subdirectory: android
run: |
flutter pub run cider version ${{ steps.get_version.outputs.VERSION }}+${{ steps.get_version.outputs.BUILD }}
flutter pub run cider bump build
git config user.name Github-actions
git config user.email github-actions@github.com
git add .
git commit -m "Bump version to $( flutter pub run cider version )"
git tag ${{ steps.get_version.outputs.VERSION }}
git push origin HEAD:master --tags
git push origin --delete ${{ steps.get_version.outputs.VERSION_V }}
- name: Build AAB
run: flutter build appbundle --release
env:
WGER_API_KEY: ${{ secrets.WGER_API_KEY }}
- name: Run Fastlane
uses: maierj/fastlane-action@v2.0.0
- name: Upload build to Play Store
uses: maierj/fastlane-action@v2.1.0
with:
lane: production
subdirectory: android
- name: Make Github release
uses: softprops/action-gh-release@v1
with:
files: build/app/outputs/bundle/release/app-release.aab
tag_name: ${{ steps.get_version.outputs.VERSION }}
body_path: CHANGELOG.md

View File

@@ -2,8 +2,12 @@ name: Continous Integration
on:
push:
branches: [ master ]
paths:
- '**.dart'
pull_request:
branches: [ master ]
paths:
- '**.dart'
jobs:
test:
runs-on: ubuntu-20.04
@@ -14,7 +18,7 @@ jobs:
uses: subosito/flutter-action@v1
with:
channel: 'stable'
flutter-version: '2.2.x'
flutter-version: '2.5.x'
- run: dart --version
- run: flutter --version

View File

@@ -4,6 +4,8 @@ on:
push:
branches:
- "master"
paths:
- '**.dart'
jobs:

10
.gitignore vendored
View File

@@ -40,8 +40,10 @@ app.*.symbols
# Obfuscation related
app.*.map.json
# Exceptions to above rules.
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
/android/fastlane/envfiles/playstore.json
/android/fastlane/envfiles/wger.properties
/android/fastlane/envfiles/keys.jks
/fastlane/metadata/android/envfiles/playstore.json
/fastlane/metadata/android/envfiles/wger.properties
/fastlane/metadata/android/envfiles/keys.jks
/fastlane/metadata/android/envfiles/key.properties

25
CHANGELOG.md Normal file
View File

@@ -0,0 +1,25 @@
🚀 Features:
* Allow users to give meals a description #89
* Allow users to delete workout log entries #97
* Allow users to log individual ingredients/products #114
* New loading animation during first run #99
* Added reference from log to meal #105
🐛 Bug Fixes:
* Improve usability for workout logs #91
* Preselect correct time in session form #93
* Fixed infinite loader bug during auth #96
* Fix error not showing up in auth screen #102
* Fix for chart legend bug #112
* Fix for RiR slider #113
🧰 Maintenance:
* Better linting #87 #98
* Improve automatic build system for publication on play store
* Notify the user when saving entries in gym mode #92
* Consistenly display nutritional values #94
* Add minimum required server version #29
* Make order field of sets required #109

10
CHANGELOG.template.md Normal file
View File

@@ -0,0 +1,10 @@
🚀 Features:
*
🐛 Bug Fixes:
*
🧰 Maintenance:
*

View File

@@ -33,7 +33,7 @@ Alternatively, you can use one of our test servers, just ask us for access.
Install Flutter, all its dependencies and create a new virtual device:
<https://flutter.dev/docs/get-started/install>.
The app currently uses flutter 2.2
The app currently uses flutter 2.5
### 3
Create a new file ``wger.properties`` in ``android/fastlane/envfiles``:

View File

@@ -65,7 +65,7 @@ linter:
empty_statements: true
exhaustive_cases: true
file_names: true
flutter_style_todos: true
flutter_style_todos: false
hash_and_equals: true
implementation_imports: true
iterable_contains_unrelated_type: true

4
android/.gitignore vendored
View File

@@ -5,7 +5,3 @@ gradle-wrapper.jar
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
fastlane/envfiles/key.properties

View File

@@ -27,14 +27,14 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
// Keys for the android play store
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('fastlane/envfiles/key.properties')
def keystorePropertiesFile = rootProject.file('../fastlane/metadata/android/envfiles/key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
// Key for wger.de REST API
def wgerProperties = new Properties()
def localMapsPropertiesFile = rootProject.file('fastlane/envfiles/wger.properties')
def localMapsPropertiesFile = rootProject.file('../fastlane/metadata/android/envfiles/wger.properties')
if (localMapsPropertiesFile.exists()) {
project.logger.info('Load maps properties from local file')
localMapsPropertiesFile.withReader('UTF-8') { reader ->
@@ -55,7 +55,7 @@ if(wgerApiKey == null){
}
android {
compileSdkVersion 29
compileSdkVersion 31
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
@@ -69,7 +69,7 @@ android {
// Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "de.wger.flutter"
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
manifestPlaceholders = [WGER_API_KEY: wgerApiKey]

View File

@@ -26,6 +26,7 @@
android:icon="@mipmap/ic_launcher">
<meta-data android:name="wger.api_key" android:value="${WGER_API_KEY}" />
<meta-data android:name="wger.check_min_app_version" android:value="true" />
<activity
android:name=".MainActivity"

View File

@@ -1,2 +0,0 @@
json_key_file("fastlane/envfiles/playstore.json")
package_name("de.wger.flutter")

2
fastlane/Appfile Normal file
View File

@@ -0,0 +1,2 @@
json_key_file("fastlane/metadata/android/envfiles/playstore.json")
package_name("de.wger.flutter")

View File

@@ -25,25 +25,13 @@ end
platform :android do
desc "Sets the version name and code in pubspec.yaml"
lane :setVersion do
desc "Check playstore configuration"
lane :test_configuration do
begin
old_version_code = google_play_track_version_codes(
package_name: "de.wger.flutter",
track: "production",
json_key: "./fastlane/envfiles/playstore.json",
)
puts "old_version_code: " + old_version_code.to_s
new_version_code = old_version_code.last().to_i + 1
puts "new_version_code: " + new_version_code.to_s
new_version_name = get_version_number_from_git_branch(pattern: 'release/#')
puts new_version_name.to_s
flutter_set_version(
path_to_yaml: "../pubspec.yaml",
version_name: new_version_name.to_s,
version_code: new_version_code.to_s,
upload_to_play_store(
track: 'production',
validate_only: true,
aab: './build/app/outputs/bundle/release/app-release.aab',
)
end
end
@@ -54,7 +42,7 @@ platform :android do
begin
upload_to_play_store(
track: 'production',
aab: '../build/app/outputs/bundle/release/app-release.aab',
aab: './build/app/outputs/bundle/release/app-release.aab',
skip_upload_metadata: false,
skip_upload_images: false,
skip_upload_screenshots: false,
@@ -69,7 +57,7 @@ platform :android do
begin
upload_to_play_store(
track: 'alpha',
aab: '../build/app/outputs/bundle/release/app-release.aab',
aab: './build/app/outputs/bundle/release/app-release.aab',
skip_upload_metadata: true,
skip_upload_images: true,
skip_upload_screenshots: true,

View File

@@ -3,4 +3,3 @@
# Ensure this file is checked in to source control!
gem 'fastlane-plugin-versioning'
gem 'fastlane-plugin-flutter_dart_version_manager'

View File

@@ -24,6 +24,11 @@ fastlane playstore
----
## Android
### android test_configuration
```
fastlane android test_configuration
```
Check configuration
### android production
```
fastlane android production
@@ -34,14 +39,9 @@ Upload app to production
fastlane android update_alpha
```
Upload closed alpha app and update store entry
### android deploy
```
fastlane android deploy
```
Deploy a new version to the Google Play
----
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -3,6 +3,9 @@
# --batch to prevent interactive command
# --yes to assume "yes" for questions
# To encrypt a new version of the keys:
# gpg -c filename.json
echo "decrypting playstore API keys"
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPTKEY_PLAYSTORE" \
--output ./playstore.json playstore.json.gpg

Binary file not shown.

View File

@@ -0,0 +1,39 @@
From fitness lovers to fitness lovers get your health organized with WGER, your Workout Manager!
Have you already found your #1 fitness app and do you love to create your own sports routines? No matter what type of sporty beast you are we all have something in common: We love to keep track of our health data <3
So we dont judge you for still managing your fitness journey with your handy little workout log book but welcome to 2021!
We have developed a 100% free digital health and fitness tracker app for you, sized down to the most relevant features to make your life easier. Get started, keep training and celebrate your progress!
wger is an Open Source project and all about:
* Your Body
* Your Workouts
* Your Progress
* Your Data
Your Body:
No need to google for the ingredients of your favourite treats choose your daily meals from more than 78000 products and see the nutritional values. Add meals to the nutritional plan and keep an overview of your diet in the calendar.
Your Workouts:
You know what is best for your body. Create your own workouts out of a growing variety from 200 different exercises. Then, use the Gym Mode to guide you through the training while you log your weights with one tap.
Your Progress:
Never lose sight of your goals. Track your weight and keep your statistics.
Your Data:
wger is your personalized fitness diary but you own your data. Use the REST API to access and do amazing things with it.
Please note: This free app is not based on additional fundings and we dont ask you to donate money. More than that it is a community project which is growing constantly. So be prepared for new features anytime!
#OpenSource what does that mean?
Open Source means that the whole source code for this app and the server it talks to is free and available to anybody:
* Do you want to run wger on your own server for you or your local gym? Go ahead!
* Do you miss a feature and want to implement it? Start now!
* Do you want to check that nothing is being sent anywhere? You can!
Join our community and become a part of sport enthusiasts and IT geeks from all over the world. We keep working on adjusting and optimizing the app customized to our needs. We love your input so feel free to jump in anytime and contribute your wishes and ideas!
-> find the source code on https://github.com/wger-project
-> ask your questions or just say hello on our discord Server https://discord.gg/rPWFv6W

View File

@@ -0,0 +1 @@
wger Workout Manager

View File

@@ -0,0 +1,39 @@
Fitness tutkunlarından fitness severlere - Egzersiz Yöneticiniz WGER ile sağlığınızı organize edin!
1 numaralı fitness uygulamanızı zaten buldunuz mu ve kendi spor rutinlerinizi oluşturmayı seviyor musunuz? Ne tür bir sportif canavar olursanız olun - hepimizin ortak bir yanı var: Sağlık verilerimizi takip etmeyi seviyoruz <3
Bu nedenle, fitness yolculuğunuzu kullanışlı küçük egzersiz kayıt defterinizle hala yönettiğiniz için sizi yargılamıyoruz, ancak 2021'e hoş geldiniz!
Sizin için hayatınızı kolaylaştırmak için en alakalı özelliklere göre boyutlandırılmış %100 ücretsiz bir dijital sağlık ve fitness takip uygulaması geliştirdik. Başlayın, antrenmana devam edin ve ilerlemenizi kutlayın!
wger bir Açık Kaynak projesidir ve her şey:
* Vucüdun
* Antrenmanlarınız
* Senin ilerlemen
* Verileriniz
Vucüdun:
En sevdiğiniz ikramların içeriğini Google'da aramanıza gerek yok - 78000'den fazla ürün arasından günlük öğünlerinizi seçin ve besin değerlerini görün. Beslenme planına yemek ekleyin ve takvimde diyetinizin bir özetini tutun.
Antrenmanlarınız:
Vücudunuz için en iyisini siz bilirsiniz. 200 farklı egzersizden artan çeşitlilikten kendi egzersiz programlarınızı oluşturun. Ardından, ağırlıklarınızı tek dokunuşla kaydederken egzersiz boyunca size rehberlik etmesi için Spor Salonu Modunu kullanın.
Senin ilerlemen:
Hedeflerinizi asla gözden kaçırmayın. Kilonuzu takip edin ve istatistiklerinizi saklayın.
Verileriniz:
wger, kişiselleştirilmiş fitness günlüğünüzdür - ancak verilerinizin sahibi sizsiniz. Erişmek ve onunla harika şeyler yapmak için REST API'yi kullanın.
Lütfen dikkat: Bu ücretsiz uygulama ek fonlara dayalı değildir ve sizden para bağışlamanızı istemiyoruz. Dahası, sürekli büyüyen bir topluluk projesidir. Bu yüzden her zaman yeni özelliklere hazır olun!
#OpenSource bu ne anlama geliyor?
ık Kaynak, bu uygulamanın ve konuştuğu sunucunun tüm kaynak kodunun ücretsiz ve herkes tarafından kullanılabilir olduğu anlamına gelir:
* Kendi sunucunuzda veya yerel spor salonunuz için wger çalıştırmak ister misiniz? Devam etmek!
* Bir özelliği özlüyor ve uygulamak istiyor musunuz? Şimdi başla!
* Hiçbir şeyin hiçbir yere gönderilmediğini kontrol etmek ister misiniz? Yapabilirsiniz!
Topluluğumuza katılın ve dünyanın her yerinden spor meraklılarının ve BT meraklılarının bir parçası olun. İhtiyaçlarımıza göre özelleştirilmiş uygulamayı ayarlamak ve optimize etmek için çalışmaya devam ediyoruz. Katkılarınızı seviyoruz, bu yüzden istediğiniz zaman atlamaktan ve dilekleriniz ve fikirlerinizle katkıda bulunmaktan çekinmeyin!
-> https://github.com/wger-project adresinde kaynak kodunu bulun
-> sorularınızı sorun veya sadece discord sunucumuzda merhaba deyin https://discord.gg/rPWFv6W

View File

@@ -0,0 +1 @@
Fitness/egzersiz, beslenme ve kilo takibi

View File

@@ -0,0 +1 @@
wger Workout Manager

View File

@@ -0,0 +1,39 @@
每一个健身爱好者 - 与您的健身教练 WGER 一起,让你更健康!
你是否已经找到了排名第一的健身应用程序,并且喜欢创建自己的运动习惯? 无论你是哪种运动野兽——我们都有一个共同点:我们喜欢跟踪我们的健康数据
因此,我们不会因为你仍然使用小笔记本来管理你的健身旅程而评判你,但欢迎来到 2021 年!
我们为你开发了 100% 免费的数字健康和健身追踪器应用程序,压缩到最相关的功能,让你的生活更轻松。开始吧,继续训练并庆祝你的进步!
wger 是一个开源项目,关于:
* 你的身体
* 你的锻炼
* 你的进步
* 你的数据
你的身体:
无需在谷歌上搜索你最喜欢的食物的成分——从 78000 多种产品中选择你的日常膳食并查看营养价值。将膳食添加到营养计划中,并在日历中概述你的饮食。
你的锻炼:
你知道什么对你的身体最好。从 200 种不同的练习中选择越来越多的练习来创建你自己的锻炼。然后,使用健身房模式指导你完成训练,同时一键记录你的体重。
你的进步:
永远不要忘记你的目标。跟踪你的体重并保留你的统计数据。
你的数据:
wger 是你的个性化健身日记——但你拥有自己的数据。使用 REST API 来访问它并用它做惊人的事情。
请注意:这个免费的应用程序不是基于额外的资金,我们不要求你捐款。不仅如此,它还是一个不断发展的社区项目。因此,请随时为新功能做好准备!
#OpenSource 这是什么意思?
开源意味着这个应用程序的整个源代码和它与之通信的服务器都是免费的,任何人都可以使用:
* 你想在自己的服务器上为你还是为你当地的健身房运行 wger都可以
* 你是否错过了某个功能并想要实现它?现在开始!
* 你想检查任何地方都没有发送任何东西吗?你可以!
加入我们的社区,成为来自世界各地的体育爱好者和 IT 极客的一份子。我们一直致力于调整和优化根据我们的需求定制的应用程序。我们喜欢你的意见,因此请随时加入并贡献你的愿望和想法!
-> 在 https://github.com/wger-project 上找到源代码
-> 在我们的 Discord 服务器上提问或打个招呼 https://discord.gg/rPWFv6W

View File

@@ -0,0 +1 @@
健身/锻炼、营养和体重追踪器

View File

@@ -26,6 +26,10 @@ const double ICON_SIZE_SMALL = 20;
/// Default wger server during login
const DEFAULT_SERVER = 'https://wger.de';
/// Keys used in the android manifest
const MANIFEST_KEY_API = 'wger.api_key';
const MANIFEST_KEY_CHECK_UPDATE = 'wger.check_min_app_version';
/// Default weight unit is "kg"
const DEFAULT_WEIGHT_UNIT = 1;
@@ -70,6 +74,3 @@ const ENERGY_CARBOHYDRATES = 4;
/// kcal per gram of fat (approx)
const ENERGY_FAT = 9;
/// Flag to check for updates to the new version.
const ENABLED_UPDATE = false;

View File

@@ -19,7 +19,6 @@
/// Calculates the number of plates needed to reach a specific weight
List<num> plateCalculator(num totalWeight, num barWeight, List<num> plates) {
final List<num> ans = [];
final platesCount = plates.length;
// Weight is less than the bar
if (totalWeight < barWeight) {

View File

@@ -96,6 +96,6 @@ void launchURL(String url, BuildContext context) async {
await canLaunch(url)
? await launch(url)
: ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Could not open $url.")),
SnackBar(content: Text('Could not open $url.')),
);
}

View File

@@ -531,5 +531,11 @@
"recentlyUsedIngredients": "Kürzlich hinzugefügte Zutaten",
"@recentlyUsedIngredients": {
"description": "A message when a user adds a new ingredient to a meal."
}
},
"searchIngredient": "Zutat suchen",
"@searchIngredient": {
"description": "Label on ingredient search form"
},
"logIngredient": "Im Nährwerttagebuch speichern",
"@logIngredient": {}
}

View File

@@ -242,6 +242,12 @@
"@logMeal": {},
"addIngredient": "Add ingredient",
"@addIngredient": {},
"logIngredient": "Save to nutrition diary",
"@logIngredient": {},
"searchIngredient": "Search ingredient",
"@searchIngredient": {
"description": "Label on ingredient search form"
},
"nutritionalPlan": "Nutritional plan",
"@nutritionalPlan": {},
"nutritionalDiary": "Nutritional diary",

View File

@@ -526,5 +526,11 @@
"recentlyUsedIngredients": "Ingrédients récemment ajoutés",
"@recentlyUsedIngredients": {
"description": "A message when a user adds a new ingredient to a meal."
}
},
"searchIngredient": "Rechercher un ingrédient",
"@searchIngredient": {
"description": "Label on ingredient search form"
},
"logIngredient": "Sauvegarder dans le journal nutritionnel",
"@logIngredient": {}
}

View File

@@ -514,5 +514,11 @@
"recentlyUsedIngredients": "Nedavno dodani sastojci",
"@recentlyUsedIngredients": {
"description": "A message when a user adds a new ingredient to a meal."
},
"logIngredient": "Spremi u dnevnik prehrane",
"@logIngredient": {},
"searchIngredient": "Traži sastojak",
"@searchIngredient": {
"description": "Label on ingredient search form"
}
}

View File

@@ -164,5 +164,199 @@
"placeholders": {
"nr": {}
}
}
},
"sameRepetitions": "Eğer tüm setler için aynıırlıklarla aynı setleri yapıyorsanız, sadece bir satır doldurabilirsiniz. Örneğin, 4 set için sadece 10 girin, bu otomatik olarak \"4 x 10\"a dönüşüyor.",
"@sameRepetitions": {},
"selectExercises": "Eğer bir süper set yapmak istiyorsanız, birkaç egzersiz için arama yapabilirsiniz, birlikte gruplanacaklardır",
"@selectExercises": {},
"logHelpEntries": "Tek bir günde, aynı sayıda tekrara sahip ancak farklıırlıklara sahip birden fazla giriş varsa, diyagramda yalnızca daha yüksek ağırlığa sahip giriş gösterilir.",
"@logHelpEntries": {},
"addSet": "Set ekle",
"@addSet": {
"description": "Label for the button that adds a set (to a workout day)"
},
"date": "Tarih",
"@date": {
"description": "The date of a workout log or body weight entry"
},
"time": "Zaman",
"@time": {
"description": "The time of a meal or workout"
},
"logHelpEntriesUnits": "Yalnızca ağırlık birimi (kg veya lb) ve tekrarları olan girişlerin çizelgelendirildiğini, zaman veya arızaya kadar olan diğer kombinasyonların burada göz ardı edildiğini unutmayın.",
"@logHelpEntriesUnits": {},
"nutritionalPlans": "Beslenme planları",
"@nutritionalPlans": {},
"energyShort": "E",
"@energyShort": {
"description": "The first letter or short name of the word 'Energy', used in overviews"
},
"total": "Toplam",
"@total": {
"description": "Label used for total sums of e.g. calories or similar"
},
"kJ": "kilo joule",
"@kJ": {
"description": "Energy in a meal in kilo joules, kJ"
},
"anErrorOccurred": "Bir hata oluştu!",
"@anErrorOccurred": {},
"plateCalculator": "Tabaklar",
"@plateCalculator": {
"description": "Label used for the plate calculator in the gym mode"
},
"logIngredient": "Beslenme günlüğüne kaydet",
"@logIngredient": {},
"comment": "Yorum",
"@comment": {
"description": "Comment, additional information"
},
"notes": "Notlar",
"@notes": {
"description": "Personal notes, e.g. for a workout session"
},
"workoutSession": "Egzersiz Seansı",
"@workoutSession": {
"description": "A (logged) workout session"
},
"newDay": "Yeni gün",
"@newDay": {},
"newSet": "Yeni set",
"@newSet": {
"description": "Header when adding a new set to a workout day"
},
"gymMode": "Spor salonu modu",
"@gymMode": {
"description": "Label when starting the gym mode"
},
"plateCalculatorNotDivisible": "Varolan tabaklar ile istenilen ağırlığa ulaşmak mümkün değil",
"@plateCalculatorNotDivisible": {
"description": "Error message when the current weight is not reachable with plates (e.g. 33.1 kg)"
},
"pause": "Duraklat",
"@pause": {
"description": "Noun, not an imperative! Label used for the pause when using the gym mode"
},
"jumpTo": "Buraya git",
"@jumpTo": {
"description": "Imperative. Label used in popup allowing the user to jump to a specific exercise while in the gym mode"
},
"todaysWorkout": "Günlük antrenmanınız",
"@todaysWorkout": {},
"description": "Açıklama",
"@description": {},
"name": "İsim",
"@name": {
"description": "Name for a workout or nutritional plan"
},
"ingredient": "Bileşen",
"@ingredient": {},
"save": "Kaydet",
"@save": {},
"addMeal": "Yemek ekle",
"@addMeal": {},
"mealLogged": "Günlüğe kaydedilen yemek",
"@mealLogged": {},
"logMeal": "Bu yemeği kaydet",
"@logMeal": {},
"addIngredient": "Malzeme ekle",
"@addIngredient": {},
"measurements": "Ölçümler",
"@measurements": {
"description": "Categories for the measurements such as biceps size, body fat, etc."
},
"measurement": "Ölçüm",
"@measurement": {},
"searchIngredient": "İçerik ara",
"@searchIngredient": {
"description": "Label on ingredient search form"
},
"nutritionalPlan": "Beslenme planı",
"@nutritionalPlan": {},
"nutritionalDiary": "Beslenme Günlüğü",
"@nutritionalDiary": {},
"noNutritionalPlans": "Beslenme planınız yok",
"@noNutritionalPlans": {
"description": "Message shown when the user has no nutritional plans"
},
"weight": "Ağırlık",
"@weight": {
"description": "The weight of a workout log or body weight entry"
},
"measurementCategoriesHelpText": "\"Pazı\" veya \"vücut yağı\" gibi ölçüm kategorisi",
"@measurementCategoriesHelpText": {},
"measurementEntriesHelpText": "'cm' veya '%' gibi kategoriyi ölçmek için kullanılan birim",
"@measurementEntriesHelpText": {},
"value": "Değer",
"@value": {
"description": "The value of a measurement entry"
},
"start": "Başlangıç",
"@start": {
"description": "Label on button to start the gym mode (i.e., an imperative)"
},
"timeStart": "Başlangıç zamanı",
"@timeStart": {
"description": "The starting time of a workout"
},
"timeEnd": "Bitiş zamanı",
"@timeEnd": {
"description": "The end time of a workout"
},
"timeStartAhead": "Başlangıç zamanı, bitiş zamanından önce olamaz",
"@timeStartAhead": {},
"energy": "Enerji",
"@energy": {
"description": "Energy in a meal, ingredient etc. e.g. in kJ"
},
"kcal": "kilo kalori",
"@kcal": {
"description": "Energy in a meal in kilocalories, kcal"
},
"macronutrients": "Makrobesinler",
"@macronutrients": {},
"planned": "Planlı",
"@planned": {
"description": "Header for the column of 'planned' nutritional values, i.e. what should be eaten"
},
"logged": "Kaydedildi",
"@logged": {
"description": "Header for the column of 'logged' nutritional values, i.e. what was eaten"
},
"difference": "Fark",
"@difference": {},
"percentEnergy": "Enerji Yüzdesi",
"@percentEnergy": {},
"gPerBodyKg": "vücut kg başına düşen g",
"@gPerBodyKg": {
"description": "Label used for total sums of e.g. calories or similar in grams per Kg of body weight"
},
"g": "gram",
"@g": {
"description": "Abbreviation for gram"
},
"protein": "Protein",
"@protein": {},
"proteinShort": "P",
"@proteinShort": {
"description": "The first letter or short name of the word 'Protein', used in overviews"
},
"carbohydrates": "Karbonhidratlar",
"@carbohydrates": {},
"carbohydratesShort": "carb",
"@carbohydratesShort": {
"description": "The first letter or short name of the word 'Carbohydrates', used in overviews"
},
"sugars": "Şekerler",
"@sugars": {},
"fat": "Yağ",
"@fat": {},
"fatShort": "fat",
"@fatShort": {
"description": "The first letter or short name of the word 'Fat', used in overviews"
},
"saturatedFat": "Doymuş yağ",
"@saturatedFat": {},
"fibres": "Lif",
"@fibres": {}
}

View File

@@ -105,7 +105,7 @@ class MyApp extends StatelessWidget {
theme: wgerTheme,
home: auth.isAuth
? FutureBuilder(
future: auth.neededApplicationUpdate(),
future: auth.applicationUpdateRequired(),
builder: (ctx, snapshot) =>
snapshot.connectionState == ConnectionState.done && snapshot.data == true
? UpdateAppScreen()

View File

@@ -7,7 +7,10 @@ part of 'weight_entry.dart';
// **************************************************************************
WeightEntry _$WeightEntryFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'weight', 'date']);
$checkKeys(
json,
requiredKeys: const ['id', 'weight', 'date'],
);
return WeightEntry(
id: json['id'] as int?,
weight: stringToNum(json['weight'] as String?),

View File

@@ -7,7 +7,10 @@ part of 'category.dart';
// **************************************************************************
ExerciseCategory _$ExerciseCategoryFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'name']);
$checkKeys(
json,
requiredKeys: const ['id', 'name'],
);
return ExerciseCategory(
id: json['id'] as int,
name: json['name'] as String,

View File

@@ -7,7 +7,10 @@ part of 'comment.dart';
// **************************************************************************
Comment _$CommentFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'comment']);
$checkKeys(
json,
requiredKeys: const ['id', 'comment'],
);
return Comment(
id: json['id'] as int,
comment: json['comment'] as String,

View File

@@ -7,7 +7,10 @@ part of 'equipment.dart';
// **************************************************************************
Equipment _$EquipmentFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'name']);
$checkKeys(
json,
requiredKeys: const ['id', 'name'],
);
return Equipment(
id: json['id'] as int,
name: json['name'] as String,

View File

@@ -7,19 +7,22 @@ part of 'exercise.dart';
// **************************************************************************
Exercise _$ExerciseFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const [
'id',
'uuid',
'creation_date',
'name',
'description',
'category',
'muscles',
'muscles_secondary',
'equipment',
'images',
'comments'
]);
$checkKeys(
json,
requiredKeys: const [
'id',
'uuid',
'creation_date',
'name',
'description',
'category',
'muscles',
'muscles_secondary',
'equipment',
'images',
'comments'
],
);
return Exercise(
id: json['id'] as int,
uuid: json['uuid'] as String,

View File

@@ -7,7 +7,10 @@ part of 'image.dart';
// **************************************************************************
ExerciseImage _$ExerciseImageFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'uuid', 'exercise_base', 'image']);
$checkKeys(
json,
requiredKeys: const ['id', 'uuid', 'exercise_base', 'image'],
);
return ExerciseImage(
id: json['id'] as int,
uuid: json['uuid'] as String,

View File

@@ -7,7 +7,10 @@ part of 'muscle.dart';
// **************************************************************************
Muscle _$MuscleFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'name', 'is_front']);
$checkKeys(
json,
requiredKeys: const ['id', 'name', 'is_front'],
);
return Muscle(
id: json['id'] as int,
name: json['name'] as String,

View File

@@ -7,7 +7,10 @@ part of 'image.dart';
// **************************************************************************
Image _$ImageFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'date', 'image']);
$checkKeys(
json,
requiredKeys: const ['id', 'date', 'image'],
);
return Image(
id: json['id'] as int?,
date: DateTime.parse(json['date'] as String),

View File

@@ -7,7 +7,10 @@ part of 'measurement_category.dart';
// **************************************************************************
MeasurementCategory _$MeasurementCategoryFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'name', 'unit']);
$checkKeys(
json,
requiredKeys: const ['id', 'name', 'unit'],
);
return MeasurementCategory(
id: json['id'] as int?,
name: json['name'] as String,

View File

@@ -7,7 +7,10 @@ part of 'measurement_entry.dart';
// **************************************************************************
MeasurementEntry _$MeasurementEntryFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'category', 'date', 'value', 'notes']);
$checkKeys(
json,
requiredKeys: const ['id', 'category', 'date', 'value', 'notes'],
);
return MeasurementEntry(
id: json['id'] as int?,
category: json['category'] as int,

View File

@@ -7,19 +7,22 @@ part of 'ingredient.dart';
// **************************************************************************
Ingredient _$IngredientFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const [
'id',
'name',
'creation_date',
'energy',
'carbohydrates',
'carbohydrates_sugar',
'protein',
'fat',
'fat_saturated',
'fibres',
'sodium'
]);
$checkKeys(
json,
requiredKeys: const [
'id',
'name',
'creation_date',
'energy',
'carbohydrates',
'carbohydrates_sugar',
'protein',
'fat',
'fat_saturated',
'fibres',
'sodium'
],
);
return Ingredient(
id: json['id'] as int,
name: json['name'] as String,

View File

@@ -7,7 +7,10 @@ part of 'ingredient_weight_unit.dart';
// **************************************************************************
IngredientWeightUnit _$IngredientWeightUnitFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'weight_unit', 'ingredient', 'grams', 'amount']);
$checkKeys(
json,
requiredKeys: const ['id', 'weight_unit', 'ingredient', 'grams', 'amount'],
);
return IngredientWeightUnit(
id: json['id'] as int,
weightUnit: WeightUnit.fromJson(json['weight_unit'] as Map<String, dynamic>),

View File

@@ -7,8 +7,10 @@ part of 'log.dart';
// **************************************************************************
Log _$LogFromJson(Map<String, dynamic> json) {
$checkKeys(json,
requiredKeys: const ['id', 'plan', 'datetime', 'ingredient', 'weight_unit', 'amount']);
$checkKeys(
json,
requiredKeys: const ['id', 'plan', 'datetime', 'ingredient', 'weight_unit', 'amount'],
);
return Log(
id: json['id'] as int?,
mealId: json['meal'] as int?,

View File

@@ -6,13 +6,11 @@ part of 'meal.dart';
// JsonSerializableGenerator
// **************************************************************************
Meal _$MealFromJson(Map<String, dynamic> json) {
return Meal(
id: json['id'] as int?,
time: stringToTime(json['time'] as String?),
name: json['name'] as String?,
)..planId = json['plan'] as int;
}
Meal _$MealFromJson(Map<String, dynamic> json) => Meal(
id: json['id'] as int?,
time: stringToTime(json['time'] as String?),
name: json['name'] as String?,
)..planId = json['plan'] as int;
Map<String, dynamic> _$MealToJson(Meal instance) => <String, dynamic>{
'id': instance.id,

View File

@@ -7,7 +7,10 @@ part of 'meal_item.dart';
// **************************************************************************
MealItem _$MealItemFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'amount']);
$checkKeys(
json,
requiredKeys: const ['id', 'amount'],
);
return MealItem(
id: json['id'] as int?,
mealId: json['meal'] as int?,

View File

@@ -7,7 +7,10 @@ part of 'nutritional_plan.dart';
// **************************************************************************
NutritionalPlan _$NutritionalPlanFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'description', 'creation_date']);
$checkKeys(
json,
requiredKeys: const ['id', 'description', 'creation_date'],
);
return NutritionalPlan(
id: json['id'] as int?,
description: json['description'] as String,

View File

@@ -7,7 +7,10 @@ part of 'weight_unit.dart';
// **************************************************************************
WeightUnit _$WeightUnitFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'name']);
$checkKeys(
json,
requiredKeys: const ['id', 'name'],
);
return WeightUnit(
id: json['id'] as int,
name: json['name'] as String,

View File

@@ -7,7 +7,10 @@ part of 'day.dart';
// **************************************************************************
Day _$DayFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'training', 'description', 'day']);
$checkKeys(
json,
requiredKeys: const ['id', 'training', 'description', 'day'],
);
return Day()
..id = json['id'] as int?
..workoutId = json['training'] as int

View File

@@ -7,16 +7,19 @@ part of 'log.dart';
// **************************************************************************
Log _$LogFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const [
'id',
'exercise',
'workout',
'reps',
'repetition_unit',
'weight',
'weight_unit',
'date'
]);
$checkKeys(
json,
requiredKeys: const [
'id',
'exercise',
'workout',
'reps',
'repetition_unit',
'weight',
'weight_unit',
'date'
],
);
return Log(
id: json['id'] as int?,
exerciseId: json['exercise'] as int,

View File

@@ -7,7 +7,10 @@ part of 'repetition_unit.dart';
// **************************************************************************
RepetitionUnit _$RepetitionUnitFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'name']);
$checkKeys(
json,
requiredKeys: const ['id', 'name'],
);
return RepetitionUnit(
id: json['id'] as int,
name: json['name'] as String,

View File

@@ -7,8 +7,10 @@ part of 'session.dart';
// **************************************************************************
WorkoutSession _$WorkoutSessionFromJson(Map<String, dynamic> json) {
$checkKeys(json,
requiredKeys: const ['id', 'workout', 'date', 'impression', 'time_start', 'time_end']);
$checkKeys(
json,
requiredKeys: const ['id', 'workout', 'date', 'impression', 'time_start', 'time_end'],
);
return WorkoutSession()
..id = json['id'] as int?
..workoutId = json['workout'] as int

View File

@@ -7,7 +7,10 @@ part of 'set.dart';
// **************************************************************************
Set _$SetFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'sets', 'order', 'comment']);
$checkKeys(
json,
requiredKeys: const ['id', 'sets', 'order', 'comment'],
);
return Set(
day: json['exerciseday'] as int,
sets: json['sets'] as int,

View File

@@ -7,18 +7,21 @@ part of 'setting.dart';
// **************************************************************************
Setting _$SettingFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const [
'id',
'set',
'order',
'exercise',
'repetition_unit',
'reps',
'weight',
'weight_unit',
'comment',
'rir'
]);
$checkKeys(
json,
requiredKeys: const [
'id',
'set',
'order',
'exercise',
'repetition_unit',
'reps',
'weight',
'weight_unit',
'comment',
'rir'
],
);
return Setting(
id: json['id'] as int?,
setId: json['set'] as int,

View File

@@ -7,7 +7,10 @@ part of 'weight_unit.dart';
// **************************************************************************
WeightUnit _$WeightUnitFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'name']);
$checkKeys(
json,
requiredKeys: const ['id', 'name'],
);
return WeightUnit(
id: json['id'] as int,
name: json['name'] as String,

View File

@@ -7,7 +7,10 @@ part of 'workout_plan.dart';
// **************************************************************************
WorkoutPlan _$WorkoutPlanFromJson(Map<String, dynamic> json) {
$checkKeys(json, requiredKeys: const ['id', 'creation_date', 'name', 'description']);
$checkKeys(
json,
requiredKeys: const ['id', 'creation_date', 'name', 'description'],
);
return WorkoutPlan(
id: json['id'] as int?,
creationDate: DateTime.parse(json['creation_date'] as String),

View File

@@ -39,34 +39,38 @@ class AuthProvider with ChangeNotifier {
String? serverUrl;
String? serverVersion;
PackageInfo? applicationVersion;
Map<String, String> metadata = {};
static const MIN_APP_VERSION_URL = 'min-app-version';
static const SERVER_VERSION_URL = 'version';
static const REGISTRATION_URL = 'register';
static const LOGIN_URL = 'login';
late http.Client client;
AuthProvider([http.Client? client, bool? checkMetadata]) {
this.client = client ?? http.Client();
// TODO: this is a workaround since AndroidMetadata doesn't work while running tests
if (checkMetadata ?? true) {
try {
AndroidMetadata.metaDataAsMap.then((value) => metadata = value!);
} on PlatformException {
throw Exception('An error occurred reading the metadata from AndroidManifest');
} catch (error) {}
}
}
/// flag to indicate that the application has successfully loaded all initial data
bool dataInit = false;
// DateTime _expiryDate;
// String _userId;
// Timer _authTimer;
bool get isAuth {
return token != null;
}
String? get token2 {
// if (_expiryDate != null &&
// _expiryDate.isAfter(DateTime.now()) &&
// _token != null) {
return token;
// }
// return null;
}
// String get userId {
// return _userId;
// }
/// Server application version
Future<void> setServerVersion() async {
final response = await http.get(makeUri(serverUrl!, 'version'));
final response = await client.get(makeUri(serverUrl!, SERVER_VERSION_URL));
final responseData = json.decode(response.body);
serverVersion = responseData;
}
@@ -78,15 +82,19 @@ class AuthProvider with ChangeNotifier {
}
/// Checking if there is a new version of the application.
Future<bool> neededApplicationUpdate() async {
if (!ENABLED_UPDATE) {
Future<bool> applicationUpdateRequired([String? version]) async {
if (metadata.containsKey('wger.check_min_app_version') ||
metadata['wger.check_min_app_version'] == 'false') {
return false;
}
final response = await http.get(makeUri(serverUrl!, 'min-app-version'));
final applicationLatestVersion = json.decode(response.body);
final currentVersion = Version.parse(applicationVersion!.version);
final latestAppVersion = Version.parse(applicationLatestVersion);
return latestAppVersion > currentVersion;
final applicationCurrentVersion = version ?? applicationVersion!.version;
final response = await client.get(makeUri(serverUrl!, MIN_APP_VERSION_URL));
final currentVersion = Version.parse(applicationCurrentVersion);
final requiredAppVersion = Version.parse(response.body);
return requiredAppVersion >= currentVersion;
}
/// Registers a new user
@@ -95,27 +103,18 @@ class AuthProvider with ChangeNotifier {
required String password,
required String email,
required String serverUrl}) async {
final uri = Uri.parse('$serverUrl/api/v2/register/');
Map<String, String>? metadata = {};
// Read the api key from the manifest file
try {
metadata = await AndroidMetadata.metaDataAsMap;
} on PlatformException {
throw Exception('An error occurred reading the API key');
}
// Register
try {
final Map<String, String> data = {'username': username, 'password': password};
if (email != '') {
data['email'] = email;
}
final response = await http.post(
uri,
final response = await client.post(
makeUri(serverUrl, REGISTRATION_URL),
headers: {
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
HttpHeaders.authorizationHeader: "Token ${metadata!['wger.api_key']}"
HttpHeaders.authorizationHeader: 'Token ${metadata[MANIFEST_KEY_API]}',
HttpHeaders.userAgentHeader: getAppNameHeader(),
},
body: json.encode(data),
);
@@ -133,14 +132,14 @@ class AuthProvider with ChangeNotifier {
/// Authenticates a user
Future<void> login(String username, String password, String serverUrl) async {
final uri = Uri.parse('$serverUrl/api/v2/login/');
await logout(shouldNotify: false);
try {
final response = await http.post(
uri,
final response = await client.post(
makeUri(serverUrl, LOGIN_URL),
headers: <String, String>{
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
HttpHeaders.userAgentHeader: getAppNameHeader(),
},
body: json.encode({'username': username, 'password': password}),
);
@@ -154,13 +153,6 @@ class AuthProvider with ChangeNotifier {
this.serverUrl = serverUrl;
token = responseData['token'];
// _userId = responseData['localId'];
// _expiryDate = DateTime.now().add(
// Duration(
// seconds: int.parse(responseData['expiresIn']),
// ),
// );
notifyListeners();
// store login data in shared preferences
@@ -168,7 +160,6 @@ class AuthProvider with ChangeNotifier {
final userData = json.encode({
'token': token,
'serverUrl': this.serverUrl,
// 'expiryDate': _expiryDate.toIso8601String(),
});
final serverData = json.encode({
'serverUrl': this.serverUrl,
@@ -201,16 +192,9 @@ class AuthProvider with ChangeNotifier {
return false;
}
final extractedUserData = json.decode(prefs.getString('userData')!);
// final expiryDate = DateTime.parse(extractedUserData['expiryDate']);
// if (expiryDate.isBefore(DateTime.now())) {
// return false;
// }
token = extractedUserData['token'];
serverUrl = extractedUserData['serverUrl'];
// _userId = extractedUserData['userId'];
// _expiryDate = expiryDate;
log('autologin successful');
setApplicationVersion();
@@ -225,12 +209,6 @@ class AuthProvider with ChangeNotifier {
token = null;
serverUrl = null;
dataInit = false;
// _userId = null;
// _expiryDate = null;
// if (_authTimer != null) {
// _authTimer.cancel();
// _authTimer = null;
// }
if (shouldNotify) {
notifyListeners();
@@ -240,24 +218,16 @@ class AuthProvider with ChangeNotifier {
prefs.remove('userData');
}
// void _autoLogout() {
// if (_authTimer != null) {
// _authTimer.cancel();
// }
// final timeToExpiry = _expiryDate.difference(DateTime.now()).inSeconds;
// _authTimer = Timer(Duration(seconds: timeToExpiry), logout);
// }
/// Returns the application name and version
///
/// This is used in the headers when talking to the API
String getAppNameHeader() {
String out = '';
if (applicationVersion != null) {
out = '${applicationVersion!.version} '
out = '/${applicationVersion!.version} '
'(${applicationVersion!.packageName}; '
'build: ${applicationVersion!.buildNumber})';
}
return 'wger App/$out';
return 'wger App$out';
}
}

View File

@@ -19,7 +19,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
@@ -65,8 +64,7 @@ class ExercisesProvider extends WgerBaseProvider with ChangeNotifier {
}
Future<void> fetchAndSetCategories() async {
final response = await client.get(makeUrl(categoriesUrlPath));
final categories = json.decode(response.body) as Map<String, dynamic>;
final categories = await fetch(makeUrl(categoriesUrlPath));
try {
for (final category in categories['results']) {
_categories.add(ExerciseCategory.fromJson(category));
@@ -77,8 +75,7 @@ class ExercisesProvider extends WgerBaseProvider with ChangeNotifier {
}
Future<void> fetchAndSetMuscles() async {
final response = await client.get(makeUrl(musclesUrlPath));
final muscles = json.decode(response.body) as Map<String, dynamic>;
final muscles = await fetch(makeUrl(musclesUrlPath));
try {
for (final muscle in muscles['results']) {
_muscles.add(Muscle.fromJson(muscle));
@@ -89,8 +86,7 @@ class ExercisesProvider extends WgerBaseProvider with ChangeNotifier {
}
Future<void> fetchAndSetEquipment() async {
final response = await client.get(makeUrl(equipmentUrlPath));
final equipments = json.decode(response.body) as Map<String, dynamic>;
final equipments = await fetch(makeUrl(equipmentUrlPath));
try {
for (final equipment in equipments['results']) {
_equipment.add(Equipment.fromJson(equipment));
@@ -142,16 +138,7 @@ class ExercisesProvider extends WgerBaseProvider with ChangeNotifier {
await fetchAndSetCategories();
await fetchAndSetMuscles();
await fetchAndSetEquipment();
final response = await client.get(
makeUrl(
exerciseInfoUrlPath,
query: {'limit': '1000'},
),
headers: {
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
});
final exercisesData = json.decode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>;
final exercisesData = await fetch(makeUrl(exerciseInfoUrlPath, query: {'limit': '1000'}));
try {
// Load exercises

View File

@@ -330,7 +330,7 @@ class NutritionPlansProvider extends WgerBaseProvider with ChangeNotifier {
Future<void> logMealToDiary(Meal meal) async {
for (final item in meal.mealItems) {
final plan = findById(meal.planId);
final Log log = Log.fromMealItem(item, plan.id!, meal.id!);
final Log log = Log.fromMealItem(item, plan.id!, meal.id);
final data = await post(log.toJson(), makeUrl(_nutritionDiaryPath));
log.id = data['id'];
@@ -339,6 +339,18 @@ class NutritionPlansProvider extends WgerBaseProvider with ChangeNotifier {
notifyListeners();
}
/// Log custom ingredient to nutrition diary
Future<void> logIngredentToDiary(MealItem mealItem, int planId, [DateTime? dateTime]) async {
final plan = findById(planId);
mealItem.ingredientObj = await fetchIngredient(mealItem.ingredientId);
final Log log = Log.fromMealItem(mealItem, plan.id!, null, dateTime);
final data = await post(log.toJson(), makeUrl(_nutritionDiaryPath));
log.id = data['id'];
plan.logs.add(log);
notifyListeners();
}
/// Deletes a log entry
Future<void> deleteLog(int logId, int planId) async {
await deleteRequest(_nutritionDiaryPath, logId);

View File

@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:android_metadata/android_metadata.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -119,11 +118,10 @@ class _AuthCardState extends State<AuthCard> {
//
// If not, the user will not be able to register via the app
try {
AndroidMetadata.metaDataAsMap.then((data) {
if (!data!.containsKey('wger.api_key') || data['wger.api_key'] == '') {
_canRegister = false;
}
});
final metadata = Provider.of<AuthProvider>(context, listen: false).metadata;
if (metadata.containsKey(MANIFEST_KEY_API) || metadata[MANIFEST_KEY_API] == '') {
_canRegister = false;
}
} on PlatformException {
_canRegister = false;
}

View File

@@ -129,12 +129,12 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
const Center(
child: SizedBox(
height: 70,
child: RiveAnimation.asset(
'assets/animations/wger_logo.riv',
animations: const ['idle_loop2'],
animations: ['idle_loop2'],
),
),
),

View File

@@ -44,7 +44,20 @@ class NutritionalPlanScreen extends StatelessWidget {
return Scaffold(
//appBar: getAppBar(nutritionalPlan),
//drawer: AppDrawer(),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.history_edu),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).logIngredient,
IngredientLogForm(_nutritionalPlan),
hasListView: true,
),
);
},
),
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(

View File

@@ -58,8 +58,8 @@ final ThemeData wgerTheme = ThemeData(
// Show icons in the system's bar in light colors
appBarTheme: const AppBarTheme(
brightness: Brightness.dark,
systemOverlayStyle: SystemUiOverlayStyle.dark,
color: wgerPrimaryColor,
),
/*

View File

@@ -1,58 +0,0 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* Copyright (C) 2020, 2021 wger Team
*
* wger Workout Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* wger Workout Manager 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:wger/providers/auth.dart';
import 'package:wger/providers/body_weight.dart';
import 'package:wger/providers/gallery.dart';
import 'package:wger/providers/nutrition.dart';
import 'package:wger/providers/workout_plans.dart';
import 'package:wger/widgets/core/about.dart';
class AppDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
AppBar(
title: const Text('wger'),
automaticallyImplyLeading: false,
),
ListTile(
//dense: true,
leading: const Icon(Icons.exit_to_app),
title: Text(AppLocalizations.of(context).logout),
onTap: () {
Provider.of<AuthProvider>(context, listen: false).logout();
Provider.of<WorkoutPlansProvider>(context, listen: false).clear();
Provider.of<NutritionPlansProvider>(context, listen: false).clear();
Provider.of<BodyWeightProvider>(context, listen: false).clear();
Provider.of<GalleryProvider>(context, listen: false).clear();
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed('/');
},
),
WgerAboutListTile()
],
),
);
}
}

Some files were not shown because too many files have changed in this diff Show More