diff --git a/.env.vault b/.env.vault
new file mode 100644
index 00000000..bda31c82
--- /dev/null
+++ b/.env.vault
@@ -0,0 +1,25 @@
+#/-------------------.env.vault---------------------/
+#/ cloud-agnostic vaulting standard /
+#/ [how it works](https://dotenv.org/env-vault) /
+#/--------------------------------------------------/
+
+# development
+DOTENV_VAULT_DEVELOPMENT="3A5tZDO2VDHgpFhdmANOWg4UBJGceN5vWXWY+TnaLc4VUP99gDwEG9OaRC8UOZDtdnuY83xfmvc3fwLcMQiufIuRC8us/CVmG7P8OvNYMk84t58ixNi4656PpSmUvl/dIEtC4XP1g2O2su93EGnahp+w8Pg77TcupFJU6UEJNd4OgvBXk6Mujr0EXRvMRuEXp0WAnCpkypWOKj99Jsutkh3GE9geloGy"
+DOTENV_VAULT_DEVELOPMENT_VERSION=12
+
+# ci
+DOTENV_VAULT_CI="l4QaivRahkpJqtkiZw+71Jw2wuX77buN+a0ICh5YGoeaXHJ8AXdJE7XQoAjN+cHzLMwMqqKO8QF5r69oVLVbeHo0Do/Jg4wwWtgrCLUJHw1evVrM5z0JWyqEh56wnp7puVMu14DBTqQ5kA=="
+DOTENV_VAULT_CI_VERSION=5
+
+# staging
+DOTENV_VAULT_STAGING="vJOG+kCLhGQuU5S9zzlIfNs6qQUwgZ5JUrVx+fNO2lS0PKbBdMIo6I3MpHv6Aaiuw0W7GFfKUP7zd2NKisRf6OT9QjJW5pq4pXaSTXCFmJQJtZAi+vWiuS9bhppSwhzW8W173RR8XDjtja+VTGIr1KFoDniYtq3uYiY6736CJ+3emssiLq0xnpO5p74EMldx5Xx4Xjs3jm47CrNqJHkj84dQWekDJB+F6SNXxkNpZLMWfTQYxmtpYOqL/ipXKGWk9wd5EIEgLT2Y01RSZNu9THsJ0MI/Cjhg3ZfpehtyFGHIUtpA7bjpZdVllEH8//M22vVZvs3iEEcIw2FyKzcv5g=="
+DOTENV_VAULT_STAGING_VERSION=8
+
+# production
+DOTENV_VAULT_PRODUCTION="yX+vDD9X8QtDIbnXKTmezSoKSnQg3LbbdtCaVAZM+jzvxj5YKFsH8dHhyLilxv2y5f+VlQVfIaMmRT/Vz5ty3PsPBHBR+gx7sJAJv4juI8WAX19GCibqSNwRrWv1Z1G9+iFcFAKXL/BIXjo+AseecqQuHBCktvTK8bayHLpMq67N1HjzXtZPfDwlQASsh/Py6HkNMg2jxZbvLi9n6lfce8JCMeSLr8pF5q0QMDS8cw28c3E/aZF8tC/FU02fraDwVtRcEU9blb9s"
+DOTENV_VAULT_PRODUCTION_VERSION=6
+
+#/----------------settings/metadata-----------------/
+DOTENV_VAULT="vlt_15ab51d92700564bd249eb93c98979af499b09328bf016b631d6dceefea6df19"
+DOTENV_API_URL="https://vault.dotenv.org"
+DOTENV_CLI="npx dotenv-vault@latest"
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..a11e0de7
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,102 @@
+### LINUM - CODE OWNERS MASTER FILE ###
+
+# TL;DR: If you want to add ownership to a new file, scroll ALL THE WAY DOWN
+
+## How to use ##
+
+# Each line is a file pattern followed by one or more owners.
+# When creating new files for the app, please consider adding
+# yourself or at least one of your teammates as a code owner.
+
+# A code owner is someone who takes responsibility and is
+# accountable for the classes and files they are assigned to.
+# They also act as a point of contact in case of questions.
+
+# Order is important; the last matching pattern takes the most
+# precedence. For example, when someone opens a pull request that
+# only modifies JS files, only @js-owner and not the global
+# owner(s) will be requested for a review.
+# *.js @js-owner
+
+# You can also use email addresses if you prefer. They'll be
+# used to look up users just like we do for commit author
+# emails.
+# *.go docs@example.com
+
+## Usage Examples ##
+
+# In this example, @doctocat owns any files in the build/logs
+# directory at the root of the repository and any of its
+# subdirectories.
+# /build/logs/ @doctocat
+
+# The `docs/*` pattern will match files like
+# `docs/getting-started.md` but not further nested files like
+# `docs/build-app/troubleshooting.md`.
+# docs/* docs@example.com
+
+# In this example, @octocat owns any file in an apps directory
+# ANYWHERE in your repository.
+# apps/ @octocat
+
+# In this example, @doctocat owns any file in the `/docs`
+# directory in the root of your repository and any of its
+# subdirectories.
+# /docs/ @doctocat
+
+# In this example, @octocat owns any file in a `/logs` directory such as
+# `/build/logs`, `/scripts/logs`, and `/deeply/nested/logs`. Any changes
+# in a `/logs` directory will require approval from @octocat.
+# **/logs @octocat
+
+# In this example, @octocat owns any file in the `/apps`
+# directory in the root of your repository except for the `/apps/github`
+# subdirectory, as its owners are left empty.
+# /apps/ @octocat
+# /apps/github
+
+# In this example, @octocat owns any file in the `/apps`
+# directory in the root of your repository except for the `/apps/github`
+# subdirectory, as this subdirectory has its own owner @doctocat
+# /apps/ @octocat
+# /apps/github @doctocat
+
+
+### PART A: GENERAL CODE OWNERS ###
+
+# These owners will be the default owners for everything in
+# the repo. Unless a later match takes precedence,
+# @NightmindOfficial and @damattl will be requested for
+# review when someone opens a pull request.
+* @NightmindOfficial @damattl
+
+### PART B: FLUTTER BASE FILES ###
+
+# These files shouldn't really be edited by anyone.
+# Edits require maintainer approval.
+
+
+
+### PART B: DANGER ZONE - MANUAL SPECIFIC APPROVAL ###
+
+# Editing this CODEOWNER File can be done by anybody, but the
+# final change for our default branch has to be approved by:
+/.github/CODEOWNERS @NightmindOfficial
+
+
+
+### PART C: The SHIT Files (SHIT - Submit Hastily, Ignore Traditions) ###
+
+# These files are mostly non-code or at least cannot break the code, so no
+# specific approval is needed beside the mandatory one from PR restrictions.
+
+### PART D: CONCEPTS & FEATURES ### <---------------------- add your ownership here if you are designing an entire feature
+
+### PART E: EVERYTHING ELSE ### <-------------------------- add your ownership here if you are the owner of a file not related to a specific feature
+
+
+
+
+
+
+
diff --git a/.github/assets/folder_init b/.github/assets/folder_init
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/.github/assets/folder_init
@@ -0,0 +1 @@
+
diff --git a/.github/workflows/cd-staging.yaml b/.github/workflows/cd-staging.yaml
index 59b0fbbb..e4b9bac8 100644
--- a/.github/workflows/cd-staging.yaml
+++ b/.github/workflows/cd-staging.yaml
@@ -8,24 +8,25 @@ name: Stage
on:
pull_request:
- types: [opened]
+ types: [opened, reopened]
branches: [staging]
workflow_dispatch:
env:
FLUTTER_CHANNEL: 'stable' # 'stable', 'beta', or 'master', default to: 'stable'
- FLUTTER_VERSION: '3.13.8'
XCODE_VERSION: '15.0'
PATH_TO_AAB: 'build/app/outputs/bundle/release/app-release.aab'
ANDROID_PACKAGE_NAME: 'de.investitacademy.linum'
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
+ DOTENV_KEY: ${{ secrets.DOTENV_KEY }}
jobs:
build-project-artifact:
name: Build Project Artifact
runs-on: ubuntu-latest
timeout-minutes: 30
+ environment: Staging-Prep
steps:
- uses: geekyeggo/delete-artifact@v2
with:
@@ -40,15 +41,38 @@ jobs:
with:
channel: ${{ env.FLUTTER_CHANNEL }}
+ - name: Set up node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 18
+
+ - name: Install npm
+ run: npm install
+
+ - name: Run build file
+ run: npm run build
+ env:
+ DOTENV_KEY: ${{ secrets.DOTENV_KEY }}
+
- name: Prepare Firebase Option Configuration
run: |
- curl -sL https://firebase.tools | bash
+ npm install -g firebase-tools
dart pub global activate flutterfire_cli
- ls
flutterfire --version
- flutterfire configure -o lib/firebase/firebase_options.g.dart -p linum-5d9f6 -y --platforms="android,ios" -i de.investitacademy.linum -a de.investitacademy.linum -m de.investitacademy.linum
-
-
+ flutterfire configure --out=lib/firebase/firebase_options.g.dart \
+ --project=linum-5d9f6 -y --platforms="android,ios" \
+ -i de.investitacademy.linum \
+ -a de.investitacademy.linum \
+ -w de.investitacademy.linum \
+ -m de.investitacademy.linum \
+ --windows-app-id de.investitacademy.linum \
+ --overwrite-firebase-options
+
+ - name: Check if files exist
+ run: |
+ ls
+ ls ./ios
+ ls ./ios/Runner
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
@@ -56,7 +80,7 @@ jobs:
path: ./
code-analysis-and-testing:
- name: Code Analysis and Testing
+ name: Pre-CD Code Analysis and Testing
needs: build-project-artifact
runs-on: ubuntu-latest
timeout-minutes: 30
@@ -95,6 +119,12 @@ jobs:
ruby-version: '3.0'
bundler-cache: true
+ - name: Set up Java Version
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'zulu'
+ java-version: '17'
+
- name: Set up Flutter SDK
uses: subosito/flutter-action@v2
with:
@@ -151,8 +181,8 @@ jobs:
- name: Run iOS CD script
env:
- GIT_USER_NAME: ${{ secrets.GIT_USER_NAME }}
- GIT_ACCESS_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
+ GOOGLE_SERVICE_INFO: ${{secrets.GOOGLE_SERVICE_INFO}}
+ FASTLANE_CERTS_REPO_KEY: ${{ secrets.FASTLANE_CERTS_REPO_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_KEYCHAIN_NAME: ${{ secrets.MATCH_KEYCHAIN_NAME }}
MATCH_KEYCHAIN_PASSWORD: ${{ secrets.MATCH_KEYCHAIN_PASSWORD }}
diff --git a/.github/workflows/ci-core.yaml b/.github/workflows/ci-core.yaml
index 7c3befff..81e410d0 100644
--- a/.github/workflows/ci-core.yaml
+++ b/.github/workflows/ci-core.yaml
@@ -17,13 +17,14 @@ env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
jobs:
+
code-analysis-and-testing:
name: Code Analysis and Testing
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout Repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
# - name: Set up Dart SDK
# uses: dart-lang/setup-dart@v1.3
@@ -35,13 +36,32 @@ jobs:
with:
channel: 'stable'
+ - name: Set up node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 16
+
+ - name: Install npm
+ run: npm install
+
+ - name: Run build file
+ run: npm run build
+ env:
+ DOTENV_KEY: ${{ secrets.DOTENV_KEY }}
+
- name: Prepare Firebase Option Configuration
run: |
curl -sL https://firebase.tools | bash
dart pub global activate flutterfire_cli
ls
flutterfire --version
- flutterfire configure -o lib/firebase/firebase_options.g.dart -p linum-5d9f6 -y --platforms="android,ios" -i de.investitacademy.linum -a de.investitacademy.linum -m de.investitacademy.linum
+ flutterfire configure -o lib/firebase/firebase_options.g.dart \
+ -p linum-5d9f6 -y --platforms="android,ios" \
+ -i de.investitacademy.linum \
+ -a de.investitacademy.linum \
+ -w de.investitacademy.linum \
+ -m de.investitacademy.linum \
+ --windows-app-id de.investitacademy.linum
- name: Delete previous Builds
run: flutter clean
diff --git a/.gitignore b/.gitignore
index aec69a25..1c4de21c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,6 +62,7 @@ app.*.map.json
/ios/Podfile.lock
/ios/Runner.ipa
/ios/Runner.app.dSYM.zip
+/ios/build
#Fastlane
/android/fastlane/metadata
@@ -79,3 +80,13 @@ firebase_app_id_file.json
# Mocks for Testing
# TODO ignore mocks again
#*.mocks.dart
+
+# DOTENV Integration
+.env*
+.flaskenv*
+!.env.project
+!.env.vault
+
+
+# Don't upload Node Modules
+/node_modules/
\ No newline at end of file
diff --git a/.metadata b/.metadata
index 56bfc2c4..8973d6ca 100644
--- a/.metadata
+++ b/.metadata
@@ -4,7 +4,27 @@
# This file should be version controlled and should not be manually edited.
version:
- revision: f4abaa0735eba4dfd8f33f73363911d63931fe03
- channel: stable
+ revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
+ channel: "stable"
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
+ base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
+ - platform: android
+ create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
+ base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/README.md b/README.md
index 7112b566..289e319b 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,41 @@
-# linum
+# Linum - Budgeting by Invest it!
+![License](https://img.shields.io/github/license/invest-it/linum)
+![Latest Release](https://img.shields.io/github/v/release/invest-it/linum)
[![CI Core Checks](https://github.com/invest-it/linum/actions/workflows/ci-core.yaml/badge.svg)](https://github.com/invest-it/linum/actions/workflows/ci-core.yaml)
[![Stage](https://github.com/invest-it/linum/actions/workflows/cd-staging.yaml/badge.svg?branch=development)](https://github.com/invest-it/linum/actions/workflows/cd-staging.yaml)
+![Play Store Availability](https://img.shields.io/badge/Play_Store-Available-3aaa35)
+![App Store Availability](https://img.shields.io/badge/App_Store-Available-lightblue)
+![Beta Tesging Availability](https://img.shields.io/badge/Beta_Testing-Open_on_Android,_Closed_on_iOS-white)
+
+
Get your money under control before your money has you under control.
This budget tracker is designed to teach young people how to keep track of spendings and how to budget effectively.
-## Setup (for intern Developers)
+## Using Linum
+
+Using Linum is very straightforward. All you need is a valid e-mail address or a Google Account, or an Apple ID to log in, and you can get started immediately using our mobile apps. Depending on your OS, you can get the apps here:
+
+| Android | iOS | Web | Other OS |
+| --------| --- | --- | -------- |
+|[](https://play.google.com/store/apps/details?id=de.investitacademy.linum)|[](https://play.google.com/store/apps/details?id=de.investitacademy.linum)| not supported | not supported |
-Follow
-[this](https://docs.flutter.dev/get-started/install)
-tutorial to setup flutter on your machine.
-Before running the application for the first time you'll need to generate the *firebase_options.dart* file.
+## Docs
-To do this you'll need to install **firebase-tools**:
-With npm:
`npm install -g firebase-tools`
+View the [project wiki](https://github.com/invest-it/linum/wiki) for details on how to contribute if you are new to the team or if you are unsure about certain processes and conventions used throughout the project.
-With brew (MacOS):
`brew install firebase-cli`
+## License
-Afterwards run:
-`firebase login`
+This project is currently under the [GPL 3.0 License](https://github.com/invest-it/linum/blob/development/LICENSE).
-A login screen will appear, please register with your InvestIt Account.
+## Contributors
-Then run:
-```shell
-dart pub global activate flutterfire_cli
-```
-and
-```shell
-flutterfire configure -o lib/firebase/firebase_options.g.dart -p linum-dev -y --platforms="android,ios"
-```
-alternatively use the _get-firebase-options.sh_ or _get-firebase-options.bat_
+Special thanks for everyone that have contributed to this project over the last years!
-Now you can start the application with `flutter run`
+
+
+
-For everything else, please turn to our [wiki](https://github.com/invest-it/linum/wiki).
+Made with [contrib.rocks](https://contrib.rocks).
diff --git a/SECURITY.md b/SECURITY.md
index 8b117f3a..b8f0b7c7 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,24 +2,27 @@
## Supported Versions
-| Version | Supported |
-| ------- | ------------------ |
-| 0.9.x | :x: |
-| 1.0.0 | :x: |
-| 1.0.1 | :x: |
-| 1.0.2 | :x: |
-| 1.0.3 | :x: | !! It is highly important to update to the latest version from this point to avoid data loss!
-| 1.0.4 | :x: |
-| 1.1.0 | :heavy_check_mark: |
-| 1.1.1 | :white_check_mark: |
-| 1.2.0 | :white_check_mark: |
-| 1.2.1 | :white_check_mark: | Apple
-| 1.2.2 | :white_check_mark: | Android
+| Version | Supported | Remarks |
+| ------- | ------------------ | ------- |
+| 0.9.x | :x: | |
+| 1.0.x | :x: | Update to the latest version ASAP to avoid data loss! |
+| 1.1.x | :heavy_check_mark: | |
+| 1.2.0 | :heavy_check_mark: | |
+| 1.2.1 | :heavy_check_mark: | Apple |
+| 1.2.2 | :heavy_check_mark: | Android |
+| 1.2.3 | :white_check_mark: | |
+| 1.3.0 | :white_check_mark: | |
+
+Symbol Key: Legacy Support ✔️ | Full Support _(recommended)_ ✅ | Not supported ❌
+
+> **07/2024 IMPORTANT NOTICE**: Towards the end of 2024, the dev team at Linum will implement a new database structure. When this will eventually go live, support for all previous versions will end, and all users will be forced to update.
## Reporting a Vulnerability
-Please contact support@investit-academy.de if you have identified security vulnerabilities within the latest app package. We will do our best to fix such errors ASAP.
+Please contact support@investit-academy.de if you have identified security vulnerabilities within the latest app package. We will do our best to fix such errors ASAP. You can also create an advisory on this repository to let us know. **Do not report security vulnerabilities via regular issues.**
## Deleting your account
-Currently, requesting deletion of your account is only possible by writing an e-mail to support@investit-academy.de - after we receive a request, your data will be permanently removed from our servers within 3-5 business days.
+> **Linum Version 1.2.3 or higher:** You can trigger an account deletion request directly in the app. Navigate to the settings screen, click on "Delete Account" and confirm your intent to delete the account. For security you might be asked to log out and sign in again.
+
+Requesting deletion of your account is possible by writing an e-mail to support@investit-academy.de - after we receive a request, your data will be permanently removed from our servers within 3-5 business days.
diff --git a/android/.gitignore b/android/.gitignore
index 0a741cb4..6f568019 100644
--- a/android/.gitignore
+++ b/android/.gitignore
@@ -9,3 +9,5 @@ GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
+**/*.keystore
+**/*.jks
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 4656c48f..29fb05e2 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,31 +1,31 @@
+plugins {
+ id "com.android.application"
+ // START: FlutterFire Configuration
+ id 'com.google.gms.google-services'
+ // END: FlutterFire Configuration
+ id "kotlin-android"
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id "dev.flutter.flutter-gradle-plugin"
+}
+
def localProperties = new Properties()
-def localPropertiesFile = rootProject.file('local.properties')
+def localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
- localPropertiesFile.withReader('UTF-8') { reader ->
+ localPropertiesFile.withReader("UTF-8") { reader ->
localProperties.load(reader)
}
}
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
-def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
- flutterVersionCode = '1'
+ flutterVersionCode = "1"
}
-def flutterVersionName = localProperties.getProperty('flutter.versionName')
+def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
- flutterVersionName = '1.0'
+ flutterVersionName = "1.0"
}
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply plugin: 'com.google.gms.google-services'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@@ -33,43 +33,44 @@ if (keystorePropertiesFile.exists()) {
}
android {
- compileSdkVersion 33
+ namespace = "de.investitacademy.linum"
+ compileSdk = 34
+ ndkVersion = flutter.ndkVersion
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
}
defaultConfig {
- applicationId "de.investitacademy.linum"
- minSdkVersion 30
- targetSdkVersion 33
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
- multiDexEnabled true
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId = "de.investitacademy.linum"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
+ minSdk = 30
+ targetSdk = 34
+ versionCode = flutterVersionCode.toInteger()
+ versionName = flutterVersionName
}
signingConfigs {
- release {
- keyAlias keystoreProperties['keyAlias']
- keyPassword keystoreProperties['keyPassword']
- storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
- storePassword keystoreProperties['storePassword']
- }
- }
- buildTypes {
- release {
- signingConfig signingConfigs.release
- }
- }
-}
+ release {
+ keyAlias keystoreProperties['keyAlias']
+ keyPassword keystoreProperties['keyPassword']
+ storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
+ storePassword keystoreProperties['storePassword']
+ }
+ }
-flutter {
- source '../..'
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.release
+ }
+ }
}
-dependencies {
- implementation platform('com.google.firebase:firebase-bom:31.2.3')
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation 'com.android.support:multidex:1.0.3'
- implementation "io.grpc:grpc-okhttp:1.48.1"
+flutter {
+ source = "../.."
}
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index 4ad2f8ad..399f6981 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -1,6 +1,6 @@
-
-
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 92f7a76e..6f39d273 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,8 +1,8 @@
-
-
-
+
+
+
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
-
-
-
-
-
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/de/investitacademy/linum/MainActivity.kt b/android/app/src/main/kotlin/de/investitacademy/linum/MainActivity.kt
index bbb64d1f..0bc3f487 100644
--- a/android/app/src/main/kotlin/de/investitacademy/linum/MainActivity.kt
+++ b/android/app/src/main/kotlin/de/investitacademy/linum/MainActivity.kt
@@ -2,5 +2,4 @@ package de.investitacademy.linum
import io.flutter.embedding.android.FlutterActivity
-class MainActivity: FlutterActivity() {
-}
+class MainActivity: FlutterActivity()
diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml
index 3fe6b2e8..f74085f3 100644
--- a/android/app/src/main/res/drawable-v21/launch_background.xml
+++ b/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -1,9 +1,12 @@
+
- -
-
-
- -
-
-
-
\ No newline at end of file
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
index 3fe6b2e8..304732f8 100644
--- a/android/app/src/main/res/drawable/launch_background.xml
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -1,9 +1,12 @@
+
- -
-
-
- -
-
-
-
\ No newline at end of file
+
+
+
+
+
diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml
index 411ccd30..06952be7 100644
--- a/android/app/src/main/res/values-night/styles.xml
+++ b/android/app/src/main/res/values-night/styles.xml
@@ -3,17 +3,16 @@
-
\ No newline at end of file
+
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
index a937a0bd..cb1ef880 100644
--- a/android/app/src/main/res/values/styles.xml
+++ b/android/app/src/main/res/values/styles.xml
@@ -3,17 +3,16 @@
-
\ No newline at end of file
+
diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml
index 4ad2f8ad..399f6981 100644
--- a/android/app/src/profile/AndroidManifest.xml
+++ b/android/app/src/profile/AndroidManifest.xml
@@ -1,6 +1,6 @@
-
-
diff --git a/android/build.gradle b/android/build.gradle
index 65cbc224..d2ffbffa 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,17 +1,3 @@
-buildscript {
- ext.kotlin_version = '1.8.0'
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:7.4.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath 'com.google.gms:google-services:4.3.15'
- }
-}
-
allprojects {
repositories {
google()
@@ -19,10 +5,12 @@ allprojects {
}
}
-rootProject.buildDir = '../build'
+rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
- project.evaluationDependsOn(':app')
+}
+subprojects {
+ project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
diff --git a/android/gradle.properties b/android/gradle.properties
index 94adc3a3..3b5b324f 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,3 +1,3 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 6b665338..3fa8f862 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
-#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
index 44e62bcf..7fb86d70 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -1,11 +1,28 @@
-include ':app'
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
-def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
-def properties = new Properties()
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
-assert localPropertiesFile.exists()
-localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
-def flutterSdkPath = properties.getProperty("flutter.sdk")
-assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
-apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "7.3.0" apply false
+ // START: FlutterFire Configuration
+ id "com.google.gms.google-services" version "4.3.15" apply false
+ // END: FlutterFire Configuration
+ id "org.jetbrains.kotlin.android" version "1.7.10" apply false
+}
+
+include ":app"
diff --git a/assets/lang/base.json b/assets/lang/base.json
index 3bfa36d2..f08f00ee 100644
--- a/assets/lang/base.json
+++ b/assets/lang/base.json
@@ -100,8 +100,9 @@
"delete-transaction": {
"dialog-button-cancel": "Abbrechen",
"dialog-button-delete": "Löschen",
- "dialog-label-delete": "Möchtest du diesen Eintrag wirklich löschen?",
- "dialog-label-title": "Bestätigen"
+ "dialog-message": "Möchtest du diesen Eintrag wirklich löschen?",
+ "dialog-message-serial": "Möchtest du diesen Vertrag wirklich löschen? Dadurch werden alle über den Vertrag gebuchten Transaktionen gelöscht.",
+ "dialog-label-title": "Löschen bestätigen"
}
},
"auth": {
@@ -151,6 +152,7 @@
"user-disabled": "Der Benutzer wurde vom System deaktiviert. Falls du denkst, dass dies ein Fehler ist, wende dich bitte an support@investit-academy.de",
"user-deleted": "Der Benutzer wurde erfolgreich gelöscht!",
"user-not-found": "Deine E-Mail oder dein Passwort sind nicht korrekt. Vielleicht hast du dich vertippt?",
+ "email-not-found": "Wir konnten keinen Benutzer mit der von dir angegebenen E-Mail-Adresse finden. Vielleicht hast du dich vertippt?",
"weak-password": "Das eingegebene Passwort ist zu schwach. Bitte wähle ein stärkeres Passwort.",
"wrong-password": "Deine E-Mail oder dein Passwort sind nicht korrekt. Vielleicht hast du dich vertippt?"
},
diff --git a/assets/lang/de-DE.json b/assets/lang/de-DE.json
index 600297a0..f7ecf590 100644
--- a/assets/lang/de-DE.json
+++ b/assets/lang/de-DE.json
@@ -100,8 +100,9 @@
"delete-transaction": {
"dialog-button-cancel": "Abbrechen",
"dialog-button-delete": "Löschen",
- "dialog-label-delete": "Möchtest du diesen Eintrag wirklich löschen?",
- "dialog-label-title": "Bestätigen"
+ "dialog-label-title": "Löschen bestätigen",
+ "dialog-message": "Möchtest du diesen Eintrag wirklich löschen?",
+ "dialog-message-serial": "Möchtest du diesen Vertrag wirklich löschen? Dadurch werden alle über den Vertrag gebuchten Transaktionen gelöscht."
}
},
"auth": {
@@ -152,7 +153,8 @@
"weak-password": "Das eingegebene Passwort ist zu schwach. Bitte wähle ein stärkeres Passwort.",
"wrong-password": "Deine E-Mail oder dein Passwort sind nicht korrekt. Vielleicht hast du dich vertippt?",
"user-deleted": "Der Benutzer wurde erfolgreich gelöscht!",
- "not-logged-in-to-update-email": "Um deine E-Mmail-Adresse zu ändern, musst du angemeldet sein."
+ "not-logged-in-to-update-email": "Um deine E-Mmail-Adresse zu ändern, musst du angemeldet sein.",
+ "email-not-found": "Wir konnten keinen Benutzer mit der von dir angegebenen E-Mail-Adresse finden. Vielleicht hast du dich vertippt?"
},
"budget_screen": {
"button-filter": "Filter",
diff --git a/assets/lang/en-US.json b/assets/lang/en-US.json
index 190e1e7d..7916b6b5 100644
--- a/assets/lang/en-US.json
+++ b/assets/lang/en-US.json
@@ -100,8 +100,9 @@
"delete-transaction": {
"dialog-button-cancel": "Cancel",
"dialog-button-delete": "Delete",
- "dialog-label-delete": "Do you really want to delete this entry?",
- "dialog-label-title": "Confirm"
+ "dialog-label-title": "Confirm deletion",
+ "dialog-message": "Do you really want to delete this entry?",
+ "dialog-message-serial": "Do you really want to delete this contract?\n This will delete all transactions booked via the contract."
}
},
"auth": {
@@ -119,7 +120,7 @@
"invalid-credential": "The login information cannot be used for registration. Please contact support@investit-academy.de.",
"invalid-disabled-field": "If you see this error, we have done something completely wrong. Please contact support@investit-academy.de.",
"invalid-display-name": "Your username is invalid.",
- "invalid-email": "Your email or password is not correct. Check for spelling errors?",
+ "invalid-email": "Your email address is invalid. Perhaps you have mistyped it?",
"invalid-email-verified": "Your email address could not be verified. Look our for spelling errors!",
"invalid-id-token": "The connection to the server malfunctioned. Please contact support@investit-academy.de.",
"invalid-last-sign-in-time": "The server could not validate your time zone. Please check if the time is set correctly on your mobile phone.",
@@ -152,7 +153,8 @@
"weak-password": "The password you entered is too weak. Please choose a stronger password.",
"wrong-password": "Your email or password is not correct. Check for spelling errors?",
"user-deleted": "The user was successfully deleted!",
- "not-logged-in-to-update-email": "To change your email address, you must be logged in."
+ "not-logged-in-to-update-email": "To change your email address, you must be logged in.",
+ "email-not-found": "We couldn't find a user with the email address you entered. Perhaps you have mistyped?"
},
"budget_screen": {
"button-filter": "Filter",
diff --git a/assets/lang/es-ES.json b/assets/lang/es-ES.json
index 15160fad..fe34049f 100644
--- a/assets/lang/es-ES.json
+++ b/assets/lang/es-ES.json
@@ -100,8 +100,9 @@
"delete-transaction": {
"dialog-button-cancel": "Cancelar",
"dialog-button-delete": "Borrar",
- "dialog-label-delete": "¿De verdad quieres borrar esta entrada?",
- "dialog-label-title": "Confirme"
+ "dialog-label-title": "Confirmar eliminación",
+ "dialog-message": "¿De verdad quieres borrar esta entrada?",
+ "dialog-message-serial": "¿Realmente desea eliminar este contrato?\n Esto eliminará todas las transacciones reservadas a través del contrato."
}
},
"auth": {
@@ -119,7 +120,7 @@
"invalid-credential": "Los datos de acceso no pueden utilizarse para el registro. Póngase en contacto con support@investit-academy.de.",
"invalid-disabled-field": "Si ve este error, es que hemos hecho algo completamente mal. Póngase en contacto con support@investit-academy.de.",
"invalid-display-name": "Su nombre de usuario no es válido.",
- "invalid-email": "Su correo electrónico o su contraseña no son correctos. ¿Comprobar errores ortográficos?",
+ "invalid-email": "Su dirección de correo electrónico no es válida. Tal vez la haya escrito mal.",
"invalid-email-verified": "Su dirección de correo electrónico no ha podido ser verificada. ¿Quizás lo has escrito mal?",
"invalid-id-token": "La conexión con el servidor era defectuosa. Póngase en contacto con support@investit-academy.de.",
"invalid-last-sign-in-time": "El servidor no ha podido validar su zona horaria. Por favor, compruebe si la hora está correctamente ajustada en su teléfono móvil.",
@@ -152,7 +153,8 @@
"weak-password": "La contraseña que has introducido es demasiado débil. Por favor, elija una contraseña más fuerte.",
"wrong-password": "Su correo electrónico o su contraseña no son correctos. ¿Comprobar errores ortográficos?",
"user-deleted": "El usuario se ha eliminado correctamente.",
- "not-logged-in-to-update-email": "Para cambiar su dirección de correo electrónico, debe iniciar sesión."
+ "not-logged-in-to-update-email": "Para cambiar su dirección de correo electrónico, debe iniciar sesión.",
+ "email-not-found": "No hemos podido encontrar ningún usuario con la dirección de correo electrónico que has introducido. ¿Quizás has escrito mal?"
},
"budget_screen": {
"button-filter": "Filtro",
diff --git a/assets/lang/fr-FR.json b/assets/lang/fr-FR.json
index de5d8006..1fb05bf8 100644
--- a/assets/lang/fr-FR.json
+++ b/assets/lang/fr-FR.json
@@ -100,8 +100,9 @@
"delete-transaction": {
"dialog-button-cancel": "Annuler",
"dialog-button-delete": "Supprimer",
- "dialog-label-delete": "Tu veux vraiment supprimer cette entrée ?",
- "dialog-label-title": "Confirmer"
+ "dialog-label-title": "Confirmer la suppression",
+ "dialog-message": "Tu veux vraiment supprimer cette entrée ?",
+ "dialog-message-serial": "Tu veux vraiment supprimer ce contrat ?\n Cela supprimera toutes les transactions enregistrées via ce contrat."
}
},
"auth": {
@@ -119,7 +120,7 @@
"invalid-credential": "Les informations de connexion ne peuvent pas être utilisées pour l'inscription. Veuillez vous inscrire auprès de support@investit-academy.de.",
"invalid-disabled-field": "Si tu vois cette erreur, c'est que nous avons fait quelque chose de complètement faux. Veuillez vous inscrire auprès de support@investit-academy.de.",
"invalid-display-name": "Ton nom d'utilisateur n'est pas valide.",
- "invalid-email": "Votre adresse électronique ou votre mot de passe n'est pas correct. Vérifier les fautes d'orthographe ?",
+ "invalid-email": "Ton adresse e-mail n'est pas valide. Tu as peut-être fait une erreur de frappe ?",
"invalid-email-verified": "Ton adresse e-mail n'a pas pu être vérifiée. Tu as peut-être fait une erreur de frappe ?",
"invalid-id-token": "La connexion au serveur était erronée. Veuillez contacter support@investit-academy.de.",
"invalid-last-sign-in-time": "Le serveur n'a pas pu valider ton fuseau horaire. Veuillez vérifier si l'heure est correctement réglée sur votre téléphone.",
@@ -152,7 +153,8 @@
"weak-password": "The password you entered is too weak. Please choose a stronger password.",
"wrong-password": "Votre adresse électronique ou votre mot de passe n'est pas correct. Vérifier les fautes d'orthographe ?",
"user-deleted": "L'utilisateur a été supprimé avec succès !",
- "not-logged-in-to-update-email": "Pour modifier ton adresse e-mail, tu dois être connecté(e)."
+ "not-logged-in-to-update-email": "Pour modifier ton adresse e-mail, tu dois être connecté(e).",
+ "email-not-found": "Nous n'avons pas trouvé d'utilisateur avec l'adresse e-mail que tu as indiquée. Tu as peut-être fait une erreur de frappe ?"
},
"budget_screen": {
"button-filter": "Filtre",
diff --git a/assets/lang/nl-NL.json b/assets/lang/nl-NL.json
index b991df60..39d9eb06 100644
--- a/assets/lang/nl-NL.json
+++ b/assets/lang/nl-NL.json
@@ -100,8 +100,9 @@
"delete-transaction": {
"dialog-button-cancel": "Annuleren",
"dialog-button-delete": "Verwijder",
- "dialog-label-delete": "Wil je dit bericht echt verwijderen?",
- "dialog-label-title": "Bevestig"
+ "dialog-label-title": "Verwijdering bevestigen",
+ "dialog-message": "Wil je dit bericht echt verwijderen?",
+ "dialog-message-serial": "Wil je dit contract echt verwijderen?\n Hiermee verwijder je alle transacties die via het contract zijn geboekt."
}
},
"auth": {
@@ -119,7 +120,7 @@
"invalid-credential": "De inloggegevens kunnen niet worden gebruikt voor registratie. Neem contact op met support@investit-academy.de.",
"invalid-disabled-field": "Als je deze fout ziet, hebben we iets helemaal verkeerd gedaan. Neem contact op met support@investit-academy.de.",
"invalid-display-name": "Uw gebruikersnaam is ongeldig.",
- "invalid-email": "Uw e-mail of wachtwoord is niet correct. Controleer op spelfouten?",
+ "invalid-email": "Uw e-mailadres is ongeldig. Misschien heb je het verkeerd getypt?",
"invalid-email-verified": "Uw e-mailadres kon niet worden geverifieerd. Misschien heb je het verkeerd getypt?",
"invalid-id-token": "De verbinding met de server was defect. Neem contact op met support@investit-academy.de.",
"invalid-last-sign-in-time": "De server kon uw tijdzone niet valideren. Controleer of de tijd correct is ingesteld op uw mobiele telefoon.",
@@ -152,7 +153,8 @@
"weak-password": "Het wachtwoord dat u hebt ingevoerd is te zwak. Kies een sterker wachtwoord.",
"wrong-password": "Uw e-mail of wachtwoord is niet correct. Controleer op spelfouten?",
"user-deleted": "De gebruiker is succesvol verwijderd!",
- "not-logged-in-to-update-email": "Om je e-mailadres te wijzigen, moet je ingelogd zijn."
+ "not-logged-in-to-update-email": "Om je e-mailadres te wijzigen, moet je ingelogd zijn.",
+ "email-not-found": "We konden geen gebruiker vinden met het e-mailadres dat je hebt opgegeven. Misschien heeft u een typefout gemaakt?"
},
"budget_screen": {
"button-filter": "Filter",
diff --git a/build.js b/build.js
new file mode 100644
index 00000000..55677432
--- /dev/null
+++ b/build.js
@@ -0,0 +1,29 @@
+// build.js
+require('dotenv').config()
+
+
+const fs = require('fs');
+const path = require('path');
+
+// Ensure .env variables are loaded
+if (!process.env) {
+ console.error('Failed to load environment variables');
+ process.exit(1);
+}
+
+// Convert loaded environment variables to .env file format
+const envContent = Object.keys(process.env)
+ .map(key => `${key}=${process.env[key]}`)
+ .join('\n');
+
+// Path to the .env file in your Flutter project
+const envPath = path.join(__dirname, '.env');
+
+// Write the .env file
+fs.writeFileSync(envPath, envContent);
+console.log('.env file written');
+
+
+
+console.log(`Three Words. Thirteen letters. Say it, and I'm yours: ${process.env.HELLOWORLD}`)
+console.log(`Sentry: ${process.env.SENTRY_DSN}`) // Remove after check
diff --git a/devtools_options.yaml b/devtools_options.yaml
new file mode 100644
index 00000000..2bc8e05f
--- /dev/null
+++ b/devtools_options.yaml
@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+ - provider: true
\ No newline at end of file
diff --git a/integration_test/robots/general/finder_type.dart b/integration_test/robots/general/finder_type.dart
index c3eb655d..41525d2b 100644
--- a/integration_test/robots/general/finder_type.dart
+++ b/integration_test/robots/general/finder_type.dart
@@ -15,11 +15,11 @@ class FinderType extends Finder {
@override
Iterable apply(Iterable candidates) {
- return finder.apply(candidates);
+ return finder.findInCandidates(candidates);
}
-
+
@override
- String get description => finder.description;
+ String get description => finder.describeMatch(Plurality.many);
Finder get title => find.descendant(of: this, matching: find.byKey(key));
}
diff --git a/integration_test/robots/onboarding_screen/onboarding_open_sign_in.dart b/integration_test/robots/onboarding_screen/onboarding_open_sign_in.dart
index 1d42032e..2440cdd8 100644
--- a/integration_test/robots/onboarding_screen/onboarding_open_sign_in.dart
+++ b/integration_test/robots/onboarding_screen/onboarding_open_sign_in.dart
@@ -7,7 +7,7 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
-import 'package:gradient_widgets/gradient_widgets.dart';
+import 'package:linum/common/widgets/gradient_button.dart';
import '../general/general_robot.dart';
diff --git a/integration_test/robots/onboarding_screen/onboarding_robot.dart b/integration_test/robots/onboarding_screen/onboarding_robot.dart
index 3aacf587..c61419c1 100644
--- a/integration_test/robots/onboarding_screen/onboarding_robot.dart
+++ b/integration_test/robots/onboarding_screen/onboarding_robot.dart
@@ -6,7 +6,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:gradient_widgets/gradient_widgets.dart';
+import 'package:linum/common/widgets/gradient_button.dart';
import '../general/general_robot.dart';
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
index 9625e105..0d140800 100644
--- a/ios/Flutter/AppFrameworkInfo.plist
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 11.0
+ 15.0
diff --git a/ios/Gemfile.lock b/ios/Gemfile.lock
index adaf605a..cae21853 100644
--- a/ios/Gemfile.lock
+++ b/ios/Gemfile.lock
@@ -1,43 +1,46 @@
GEM
remote: https://rubygems.org/
specs:
- CFPropertyList (3.0.5)
+ CFPropertyList (3.0.7)
+ base64
+ nkf
rexml
- addressable (2.8.1)
- public_suffix (>= 2.0.2, < 6.0)
- artifactory (3.0.15)
+ addressable (2.8.7)
+ public_suffix (>= 2.0.2, < 7.0)
+ artifactory (3.0.17)
atomos (0.1.3)
- aws-eventstream (1.2.0)
- aws-partitions (1.693.0)
- aws-sdk-core (3.168.4)
- aws-eventstream (~> 1, >= 1.0.2)
+ aws-eventstream (1.3.0)
+ aws-partitions (1.957.0)
+ aws-sdk-core (3.201.2)
+ aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
- aws-sigv4 (~> 1.5)
+ aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.61.0)
- aws-sdk-core (~> 3, >= 3.165.0)
- aws-sigv4 (~> 1.1)
- aws-sdk-s3 (1.117.2)
- aws-sdk-core (~> 3, >= 3.165.0)
+ aws-sdk-kms (1.88.0)
+ aws-sdk-core (~> 3, >= 3.201.0)
+ aws-sigv4 (~> 1.5)
+ aws-sdk-s3 (1.156.0)
+ aws-sdk-core (~> 3, >= 3.201.0)
aws-sdk-kms (~> 1)
- aws-sigv4 (~> 1.4)
- aws-sigv4 (1.5.2)
+ aws-sigv4 (~> 1.5)
+ aws-sigv4 (1.8.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
+ base64 (0.2.0)
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)
+ digest-crc (0.6.5)
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.97.1)
- faraday (1.10.2)
+ excon (0.109.0)
+ faraday (1.10.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
@@ -65,15 +68,15 @@ GEM
faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
- fastimage (2.2.6)
- fastlane (2.211.0)
+ fastimage (2.3.1)
+ fastlane (2.221.1)
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
+ colored (~> 1.2)
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
@@ -85,30 +88,32 @@ GEM
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
+ google-cloud-env (>= 1.6.0, < 2.0.0)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
+ http-cookie (~> 1.0.5)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
- multipart-post (~> 2.0.0)
+ multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2)
- optparse (~> 0.1.1)
+ optparse (>= 0.1.1, < 1.0.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
- security (= 0.1.3)
+ security (= 0.1.5)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
- terminal-table (>= 1.4.5, < 2.0.0)
+ terminal-table (~> 3)
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)
+ xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
gh_inspector (1.1.3)
- google-apis-androidpublisher_v3 (0.32.0)
- google-apis-core (>= 0.9.1, < 2.a)
- google-apis-core (0.9.5)
+ google-apis-androidpublisher_v3 (0.54.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-core (0.11.3)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
@@ -116,87 +121,87 @@ GEM
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
- webrick
- google-apis-iamcredentials_v1 (0.16.0)
- google-apis-core (>= 0.9.1, < 2.a)
- google-apis-playcustomapp_v1 (0.12.0)
- google-apis-core (>= 0.9.1, < 2.a)
- google-apis-storage_v1 (0.19.0)
- google-apis-core (>= 0.9.0, < 2.a)
- google-cloud-core (1.6.0)
- google-cloud-env (~> 1.0)
+ google-apis-iamcredentials_v1 (0.17.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-playcustomapp_v1 (0.13.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-storage_v1 (0.29.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-cloud-core (1.6.1)
+ google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
- google-cloud-errors (1.3.0)
- google-cloud-storage (1.44.0)
+ google-cloud-errors (1.3.1)
+ google-cloud-storage (1.45.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
- google-apis-storage_v1 (~> 0.19.0)
+ google-apis-storage_v1 (~> 0.29.0)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
- googleauth (1.3.0)
+ googleauth (1.8.1)
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)
+ http-cookie (1.0.6)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.2)
- json (2.6.3)
- jwt (2.6.0)
- memoist (0.16.2)
- mini_magick (4.12.0)
- mini_mime (1.1.2)
+ json (2.7.2)
+ jwt (2.8.2)
+ base64
+ mini_magick (4.13.2)
+ mini_mime (1.1.5)
multi_json (1.15.0)
- multipart-post (2.0.0)
+ multipart-post (2.4.1)
nanaimo (0.3.0)
naturally (2.2.1)
- optparse (0.1.1)
+ nkf (0.2.0)
+ optparse (0.5.0)
os (1.1.4)
- plist (3.6.0)
- public_suffix (5.0.1)
- rake (13.0.6)
+ plist (3.7.1)
+ public_suffix (5.1.1)
+ rake (13.2.1)
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)
+ rexml (3.2.9)
+ strscan
rouge (2.0.7)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
- security (0.1.3)
- signet (0.17.0)
+ security (0.1.5)
+ signet (0.18.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
- simctl (1.6.8)
+ simctl (1.6.10)
CFPropertyList
naturally
+ strscan (3.1.0)
terminal-notifier (2.0.0)
- terminal-table (1.8.0)
- unicode-display_width (~> 1.1, >= 1.1.1)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
- tty-screen (0.8.1)
+ tty-screen (0.8.2)
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)
+ unf_ext (0.0.9.1)
+ unicode-display_width (2.5.0)
word_wrap (1.0.0)
- xcodeproj (1.22.0)
+ xcodeproj (1.24.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
diff --git a/ios/Podfile b/ios/Podfile
index 8eaa9951..034c45d6 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-platform :ios, '12.0'
+platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -35,6 +35,8 @@ target 'Runner' do
#target 'RunnerTests' do
# inherit! :search_paths
#end
+
+ pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '10.28.0'
end
post_install do |installer|
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 44bc71e3..bd261335 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -148,6 +148,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
E436F918B44CECF44C5AB775 /* [CP] Embed Pods Frameworks */,
+ 2596916B8B815EC82E2B98F3 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -164,7 +165,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1430;
+ LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@@ -207,6 +208,23 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 2596916B8B815EC82E2B98F3 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 2a03eeff..dd751725 100644
--- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-
diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile
index b2d00234..decc4fb8 100644
--- a/ios/fastlane/Fastfile
+++ b/ios/fastlane/Fastfile
@@ -21,7 +21,10 @@ platform :ios do
end
lane :localBeta do
- disable_automatic_code_signing(path: "Runner.xcodeproj")
+ update_code_signing_settings(
+ use_automatic_signing: false,
+ path: "Runner.xcodeproj"
+ )
match(readonly: true)
gym(
configuration: "Release",
@@ -32,7 +35,10 @@ platform :ios do
end
lane :beta do
- disable_automatic_code_signing(path: "Runner.xcodeproj")
+ update_code_signing_settings(
+ use_automatic_signing: false,
+ path: "Runner.xcodeproj"
+ )
create_keychain(
name: ENV['MATCH_KEYCHAIN_NAME'],
@@ -45,7 +51,7 @@ platform :ios do
match(
type: "appstore",
- git_basic_authorization: ENV['GIT_BASIC_AUTH_TOKEN'],
+ git_private_key: "./repo_key",
readonly: true,
keychain_name: ENV['MATCH_KEYCHAIN_NAME'],
keychain_password: ENV['MATCH_KEYCHAIN_PASSWORD']
@@ -70,7 +76,10 @@ platform :ios do
end
lane :release do
- disable_automatic_code_signing(path: "Runner.xcodeproj")
+ update_code_signing_settings(
+ use_automatic_signing: false,
+ path: "Runner.xcodeproj"
+ )
create_keychain(
name: ENV['MATCH_KEYCHAIN_NAME'],
@@ -83,7 +92,7 @@ platform :ios do
match(
type: "appstore",
- git_basic_authorization: ENV['GIT_BASIC_AUTH_TOKEN'],
+ git_private_key: "./repo_key",
readonly: true,
keychain_name: ENV['MATCH_KEYCHAIN_NAME'],
keychain_password: ENV['MATCH_KEYCHAIN_PASSWORD']
diff --git a/ios/fastlane/Matchfile b/ios/fastlane/Matchfile
index 7b632ead..adc1d35d 100644
--- a/ios/fastlane/Matchfile
+++ b/ios/fastlane/Matchfile
@@ -1,4 +1,4 @@
-git_url("https://github.com/invest-it/linum-certs.git")
+git_url("git@github.com:invest-it/linum-certs.git")
storage_mode("git")
diff --git a/ios/fastlane/report.xml b/ios/fastlane/report.xml
index d510fc07..62c5fd4e 100644
--- a/ios/fastlane/report.xml
+++ b/ios/fastlane/report.xml
@@ -5,22 +5,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/ios/ios_cd.sh b/ios/ios_cd.sh
index 74622c70..ca57036d 100755
--- a/ios/ios_cd.sh
+++ b/ios/ios_cd.sh
@@ -5,11 +5,17 @@ cd ./ios || exit
gem install bundler:1.17.2
bundle install
-GIT_BASIC_AUTH_TOKEN=$(echo -n "$GIT_USER_NAME:$GIT_ACCESS_TOKEN" | base64 | tr -d \\n)
-export GIT_BASIC_AUTH_TOKEN
+echo "$FASTLANE_CERTS_REPO_KEY" > ./repo_key
+chmod 600 ./repo_key
+
+echo "$GOOGLE_SERVICE_INFO" | base64 --decode > ./Runner/GoogleService-Info.plist
+# This line must be removed as soon as the firebase tools start working again
gcloud secrets versions access latest --secret=linum-ios-auth-key-file --project=658687609050 > ./AuthKey.p8
+pod repo update
+pod install
+
cd ../
flutter build ios --release --no-codesign
@@ -19,7 +25,7 @@ cd ./ios || exit
if [ "$1" = "release" ]
then
- bundle exec fastlane release
+ MATCH_PASSWORD=${MATCH_PASSWORD} bundle exec fastlane release
else
- bundle exec fastlane beta
+ MATCH_PASSWORD=${MATCH_PASSWORD} bundle exec fastlane beta
fi
diff --git a/lib/app.dart b/lib/app.dart
index b7449709..eca0ae8d 100644
--- a/lib/app.dart
+++ b/lib/app.dart
@@ -1,21 +1,24 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
-import 'package:linum/core/design/theme/constants/main_theme_data.dart';
+import 'package:linum/core/design/theme/theme_data.dart';
import 'package:linum/core/navigation/main_router_delegate.dart';
import 'package:linum/core/navigation/main_routes.dart';
import 'package:linum/generated/objectbox/objectbox.g.dart';
import 'package:linum/services.dart';
+import 'package:shared_preferences/shared_preferences.dart';
class Linum extends StatelessWidget {
final bool? testing;
final Store store;
final MainRouterDelegate routerDelegate;
final RouteInformationParser routeInformationParser;
+ final SharedPreferences preferences;
const Linum({
required this.store,
required this.routerDelegate,
required this.routeInformationParser,
+ required this.preferences,
this.testing,
});
@@ -26,16 +29,19 @@ class Linum extends StatelessWidget {
} else {
return app;
} */
+ // print("rebuild linum app");
+ // TODO: Check if its better to wrap the app inside the application services
return MaterialApp(
title: 'Linum',
- theme: MainThemeData.lightTheme,
+ theme: LinumTheme.light(),
debugShowCheckedModeBanner: false,
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
home: ApplicationServices(
store: store,
+ preferences: preferences,
router: Router(
routerDelegate: routerDelegate,
routeInformationParser: routeInformationParser,
diff --git a/lib/common/components/action_lip/action_lip.dart b/lib/common/components/action_lip/action_lip.dart
index 78adb147..1a0854f2 100644
--- a/lib/common/components/action_lip/action_lip.dart
+++ b/lib/common/components/action_lip/action_lip.dart
@@ -52,7 +52,7 @@ class ActionLip extends StatelessWidget {
1,
),
decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.background,
+ color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32),
diff --git a/lib/common/components/dialogs/show_transaction_delete_dialog.dart b/lib/common/components/dialogs/show_transaction_delete_dialog.dart
index 349357dd..3b8f1283 100644
--- a/lib/common/components/dialogs/show_transaction_delete_dialog.dart
+++ b/lib/common/components/dialogs/show_transaction_delete_dialog.dart
@@ -7,10 +7,15 @@ import 'package:linum/generated/translation_keys.g.dart';
void showTransactionDeleteDialog(
BuildContext context,
Function() callbackFunction,
- ) {
+ {bool isSerialTransaction = false,}) {
+
+
+
showActionDialog(
context,
- message: tr(translationKeys.alertdialog.deleteTransaction.dialogLabelDelete),
+ message: isSerialTransaction
+ ? tr(translationKeys.alertdialog.deleteTransaction.dialogMessageSerial)
+ : tr(translationKeys.alertdialog.deleteTransaction.dialogMessage),
title: tr(translationKeys.alertdialog.deleteTransaction.dialogLabelTitle),
userMustDismissWithButton: false,
actions: [
diff --git a/lib/common/components/screen_card/widgets/screen_card_skeleton.dart b/lib/common/components/screen_card/widgets/screen_card_skeleton.dart
index 1444d290..91954c02 100644
--- a/lib/common/components/screen_card/widgets/screen_card_skeleton.dart
+++ b/lib/common/components/screen_card/widgets/screen_card_skeleton.dart
@@ -50,6 +50,7 @@ class _ScreenCardSkeletonState extends State {
controller: _flipCardController,
front: widget.frontSide,
back: widget.backSide!,
+
),
);
}
diff --git a/lib/common/components/sheets/linum_bottom_sheet.dart b/lib/common/components/sheets/linum_bottom_sheet.dart
new file mode 100644
index 00000000..e0f58d43
--- /dev/null
+++ b/lib/common/components/sheets/linum_bottom_sheet.dart
@@ -0,0 +1,45 @@
+import 'package:flutter/material.dart';
+
+class LinumBottomSheet extends StatelessWidget {
+ final String title;
+ final Widget body;
+
+ const LinumBottomSheet({
+ super.key,
+ required this.title,
+ required this.body,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
+ child: SafeArea(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ AppBar(
+ primary: false,
+ automaticallyImplyLeading: false,
+ title: Text(
+ title,
+ style: Theme.of(context).textTheme.headlineSmall,
+ ),
+ centerTitle: true,
+ actions: [
+ IconButton(
+ icon: const Icon(Icons.close),
+ onPressed: Navigator.of(context).pop,
+ ),
+ ],
+ iconTheme: const IconThemeData(color: Colors.black),
+ backgroundColor: Colors.transparent,
+ elevation: 0,
+ ),
+ body,
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/common/interfaces/service_interface.dart b/lib/common/interfaces/service_interface.dart
new file mode 100644
index 00000000..72707338
--- /dev/null
+++ b/lib/common/interfaces/service_interface.dart
@@ -0,0 +1,12 @@
+import 'package:flutter/cupertino.dart';
+
+abstract class IService {
+ void dispose();
+}
+
+abstract mixin class NotifyReady {
+ Future ready();
+}
+
+
+abstract class IProvidableService extends IService with ChangeNotifier {}
diff --git a/lib/common/types/filter_function.dart b/lib/common/types/filter_function.dart
index dc67de84..71b9c970 100644
--- a/lib/common/types/filter_function.dart
+++ b/lib/common/types/filter_function.dart
@@ -1 +1,8 @@
typedef Filter = bool Function(T item);
+
+bool applyFilter(Filter? filter, T element) {
+ if (filter == null) {
+ return true;
+ }
+ return filter(element);
+}
diff --git a/lib/common/utils/service_container.dart b/lib/common/utils/service_container.dart
new file mode 100644
index 00000000..c7a7136d
--- /dev/null
+++ b/lib/common/utils/service_container.dart
@@ -0,0 +1,51 @@
+import 'package:flutter/widgets.dart';
+import 'package:linum/common/interfaces/service_interface.dart';
+import 'package:provider/provider.dart';
+
+typedef ServiceEntry = ({
+ IService service, ChangeNotifierProvider? provider,
+});
+
+class ServiceContainer {
+ final List services = [];
+
+ void registerProvidableService(T service) {
+ services.add((
+ service: service,
+ provider: ChangeNotifierProvider.value(
+ value: service,
+ ),
+ ),);
+ }
+
+ void registerService(IService service) {
+ services.add((
+ service: service,
+ provider: null,
+ ),);
+ }
+
+ void clear() {
+ for (final entry in services.reversed) {
+ entry.service.dispose();
+ }
+ services.clear();
+ }
+
+ void dispose() {
+ clear();
+ }
+
+ Widget build(BuildContext context, Widget? child) {
+ final providers = services
+ .where((entry) => entry.provider != null)
+ .map((entry) {
+ return entry.provider!;
+ });
+
+ return MultiProvider(
+ providers: providers.toList(),
+ child: child,
+ );
+ }
+}
diff --git a/lib/common/utils/sorters.dart b/lib/common/utils/sorters.dart
index 810a9c23..d433b89c 100644
--- a/lib/common/utils/sorters.dart
+++ b/lib/common/utils/sorters.dart
@@ -37,7 +37,7 @@ class Sorters {
// maybe filter is used on maps
// if can be deleted later
if (a is Transaction && b is Transaction) {
- return (a.amount).compareTo(b.amount);
+ return a.amount.compareTo(b.amount);
}
return (a["amount"] as num).compareTo(b["amount"] as num);
@@ -45,36 +45,36 @@ class Sorters {
static int amountMostToLeast(dynamic b, dynamic a) {
if (a is Transaction && b is Transaction) {
- return (a.amount).compareTo(b.amount);
+ return a.amount.compareTo(b.amount);
}
return (a["amount"] as num).compareTo(b["amount"] as num);
}
static int categoryAlphabetically(Transaction a, Transaction b) {
- return (a.category).compareTo(b.category);
+ return a.category.compareTo(b.category);
}
static int categoryAlphabeticallyReversed(Transaction a, Transaction b) {
- return (a.category).compareTo(b.category);
+ return a.category.compareTo(b.category);
}
static int nameAlphabetically(dynamic a, dynamic b) {
if (a is Transaction && b is Transaction) {
- return (a.name).compareTo(b.name);
+ return a.name.compareTo(b.name);
}
return (a["name"] as String).compareTo(b["name"] as String);
}
static int nameAlphabeticallyReversed(dynamic b, dynamic a) {
if (a is Transaction && b is Transaction) {
- return (a.name).compareTo(b.name);
+ return a.name.compareTo(b.name);
}
return (a["name"] as String).compareTo(b["name"] as String);
}
static int dateNewToOld(dynamic a, dynamic b) {
if (a is Transaction && b is Transaction) {
- return (b.date).compareTo(a.date);
+ return b.date.compareTo(a.date);
}
return (b["time"] as Timestamp)
.compareTo(a["time"] as Timestamp);
@@ -82,7 +82,7 @@ class Sorters {
static int dateOldToNew(dynamic b, dynamic a) {
if (a is Transaction && b is Transaction) {
- return (a.date).compareTo(b.date);
+ return a.date.compareTo(b.date);
}
return (a["time"] as Timestamp)
.compareTo(b["time"] as Timestamp);
diff --git a/lib/common/widgets/gradient_button.dart b/lib/common/widgets/gradient_button.dart
new file mode 100644
index 00000000..424f0228
--- /dev/null
+++ b/lib/common/widgets/gradient_button.dart
@@ -0,0 +1,37 @@
+import 'package:flutter/material.dart';
+
+class GradientButton extends StatelessWidget {
+ final VoidCallback? onPressed;
+ final LinearGradient gradient;
+ final Widget child;
+ final Size? minimumSize;
+
+ const GradientButton({
+ super.key,
+ required this.onPressed,
+ required this.gradient,
+ required this.child,
+ this.minimumSize,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+
+ return GestureDetector(
+ onTap: onPressed,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ gradient: gradient,
+ ),
+ constraints: BoxConstraints(
+ minWidth: minimumSize?.width ?? 64,
+ minHeight: minimumSize?.height ?? 36,
+ ),
+ child: Center(
+ child: child,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/common/widgets/list_divider.dart b/lib/common/widgets/list_divider.dart
index 251c7834..473133f8 100644
--- a/lib/common/widgets/list_divider.dart
+++ b/lib/common/widgets/list_divider.dart
@@ -12,7 +12,7 @@ class ListDivider extends StatelessWidget {
final double B;
final double L;
- const ListDivider({this.T = 16.0, this.R = 0, this.B = 16.0, this.L = 0});
+ const ListDivider({this.T = 8.0, this.R = 0, this.B = 8.0, this.L = 0});
@override
Widget build(BuildContext context) {
@@ -20,7 +20,7 @@ class ListDivider extends StatelessWidget {
padding: EdgeInsets.only(top: T, right: R, bottom: B, left: L),
child: Divider(
thickness: 1.0,
- color: Theme.of(context).colorScheme.surface,
+ color: Theme.of(context).colorScheme.tertiary,
),
);
}
diff --git a/lib/common/widgets/list_header.dart b/lib/common/widgets/list_header.dart
index 31f32b43..760170db 100644
--- a/lib/common/widgets/list_header.dart
+++ b/lib/common/widgets/list_header.dart
@@ -19,7 +19,7 @@ class ListHeader extends StatelessWidget {
if (tooltipMessage == null) {
return Text(
tr(title).toUpperCase(),
- style: Theme.of(context).textTheme.labelSmall,
+ style: Theme.of(context).textTheme.labelMedium,
);
} else {
return Wrap(
@@ -28,7 +28,7 @@ class ListHeader extends StatelessWidget {
children: [
Text(
tr(title).toUpperCase(),
- style: Theme.of(context).textTheme.labelSmall,
+ style: Theme.of(context).textTheme.labelMedium,
),
Tooltip(
message: tooltipMessage?.tr(),
diff --git a/lib/core/authentication/domain/services/authentication_service.dart b/lib/core/authentication/domain/services/authentication_service.dart
index 61b993e4..b278d123 100644
--- a/lib/core/authentication/domain/services/authentication_service.dart
+++ b/lib/core/authentication/domain/services/authentication_service.dart
@@ -22,16 +22,17 @@ import 'package:sign_in_with_apple/sign_in_with_apple.dart';
class AuthenticationService extends SubscriptionHandler {
/// The FirebaseAuth Object of the Project
final FirebaseAuth _firebaseAuth;
+ final EventService _eventService;
late Logger logger;
AuthenticationService(this._firebaseAuth, {
String? languageCode,
required EventService eventService,
- }) {
+ }) : _eventService = eventService {
logger = Logger();
- super.subscribe(eventService.getGlobalEventStream(), (event) {
+ super.subscribe(_eventService.getGlobalEventStream(), (event) {
if (event.type == EventType.languageChange) {
updateLanguageCode(event.message);
}
@@ -83,7 +84,6 @@ class AuthenticationService extends SubscriptionHandler {
logger.i("Your mail is not verified.");
}
}
-
} on FirebaseAuthException catch (e) {
logger.e(e.message);
onError("auth.${e.code}");
@@ -196,7 +196,7 @@ class AuthenticationService extends SubscriptionHandler {
onError ??= logger.e;
try {
if (_firebaseAuth.currentUser != null) {
- await _firebaseAuth.currentUser!.updateEmail(newEmail);
+ await _firebaseAuth.currentUser!.verifyBeforeUpdateEmail(newEmail);
} else {
return onError("auth.not-logged-in-to-update-email");
}
@@ -245,7 +245,13 @@ class AuthenticationService extends SubscriptionHandler {
try {
await _firebaseAuth.sendPasswordResetEmail(email: email);
} on FirebaseAuthException catch (e) {
- return onError("auth.${e.code}");
+ if(e.code == "user-not-found"){
+ return onComplete("alertdialog.reset-password.message");
+ }
+ if(e.code == "invalid-email"){
+ return onError("auth.invalid-email");
+ }
+ return onError("auth.unknown");
}
onComplete("alertdialog.reset-password.message");
}
@@ -297,6 +303,7 @@ class AuthenticationService extends SubscriptionHandler {
if (user != null) {
await setLastMail(user.email);
}
+ // print("User changed");
notifyListeners();
}
diff --git a/lib/core/authentication/presentation/utils/show_change_email_action_lip.dart b/lib/core/authentication/presentation/utils/show_change_email_action_lip.dart
index 591ea51e..2b6873b7 100644
--- a/lib/core/authentication/presentation/utils/show_change_email_action_lip.dart
+++ b/lib/core/authentication/presentation/utils/show_change_email_action_lip.dart
@@ -1,21 +1,19 @@
import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/cupertino.dart';
-import 'package:linum/common/components/action_lip/viewmodels/action_lip_viewmodel.dart';
-import 'package:linum/core/authentication/presentation/widgets/change_email_action_lip/change_email_action_lip.dart';
+import 'package:flutter/material.dart';
+import 'package:linum/common/components/sheets/linum_bottom_sheet.dart';
+import 'package:linum/core/authentication/presentation/widgets/change_email_action_lip/change_email_bottom_sheet.dart';
import 'package:linum/core/design/layout/enums/screen_key.dart';
-import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
import 'package:linum/generated/translation_keys.g.dart';
-import 'package:provider/provider.dart';
-void showChangeEmailActionLip(BuildContext context, ScreenKey screenKey) {
- final viewModel = context.read();
-
- viewModel.setActionLip(
- context: context,
- screenKey: screenKey,
- actionLipBody: const ChangeEmailActionLip(),
- actionLipStatus: ActionLipVisibility.onviewport,
- actionLipTitle: tr(translationKeys.actionLip.changeEmail.labelTitle),
+void showChangeEmailBottomSheet(BuildContext context, ScreenKey screenKey) {
+ showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ builder: (BuildContext context) {
+ return LinumBottomSheet(
+ title: tr(translationKeys.actionLip.changeEmail.labelTitle),
+ body: const ChangeEmailBottomSheet(),
+ );
+ },
);
-
}
diff --git a/lib/core/authentication/presentation/utils/show_forgot_password_action_lip.dart b/lib/core/authentication/presentation/utils/show_forgot_password_action_lip.dart
index 3c6d4a73..8e871fe8 100644
--- a/lib/core/authentication/presentation/utils/show_forgot_password_action_lip.dart
+++ b/lib/core/authentication/presentation/utils/show_forgot_password_action_lip.dart
@@ -1,44 +1,47 @@
import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/cupertino.dart';
-import 'package:linum/common/components/action_lip/viewmodels/action_lip_viewmodel.dart';
+import 'package:flutter/material.dart';
+import 'package:linum/common/components/sheets/linum_bottom_sheet.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
-import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_forgot_password_action_lip.dart';
-import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_forgot_password_action_lip.dart';
+import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_forgot_password_bottom_sheet.dart';
+import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_forgot_password_bottom_sheet.dart';
import 'package:linum/core/design/layout/enums/screen_key.dart';
-import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
import 'package:linum/generated/translation_keys.g.dart';
import 'package:provider/provider.dart';
-void showForgotPasswordActionLip(BuildContext context, ScreenKey screenKey) {
+void showForgotPasswordBottomSheet(BuildContext context, ScreenKey screenKey) {
// lip if the user is not logged in
- final viewModel = context.read();
final authService = context.read();
final TextEditingController inputController = TextEditingController();
if (!authService.isLoggedIn) {
- viewModel.setActionLip(
- context: context,
- screenKey: screenKey,
- actionLipBody: UnregisteredUserForgotPasswordActionLip(
- controller: inputController,
- ),
- actionLipStatus: ActionLipVisibility.onviewport,
- actionLipTitle:
- tr(translationKeys.actionLip.forgotPassword.loggedOut.labelTitle),
+ showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ builder: (BuildContext context) {
+ return LinumBottomSheet(
+ title: tr(translationKeys.actionLip.forgotPassword.loggedOut.labelTitle),
+ body: UnregisteredUserForgotPasswordBottomSheet(
+ controller: inputController,
+ ),
+ );
+ },
);
}
// lip if the user has already authenticated themself
else {
- viewModel.setActionLip(
+ showModalBottomSheet(
context: context,
- screenKey: screenKey,
- actionLipBody: RegisteredUserForgotPasswordActionLip(
- controller: inputController,
- ),
- actionLipStatus: ActionLipVisibility.onviewport,
- actionLipTitle:
- tr(translationKeys.actionLip.forgotPassword.loggedIn.labelTitle),
+ isScrollControlled: true,
+ builder: (BuildContext context) {
+ return LinumBottomSheet(
+ title: tr(translationKeys.actionLip.forgotPassword.loggedIn.labelTitle),
+ body: RegisteredUserForgotPasswordBottomSheet(
+ controller: inputController,
+ ),
+ );
+ },
);
+
}
}
diff --git a/lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_action_lip.dart b/lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_bottom_sheet.dart
similarity index 88%
rename from lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_action_lip.dart
rename to lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_bottom_sheet.dart
index b6275589..7131be4d 100644
--- a/lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_action_lip.dart
+++ b/lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_bottom_sheet.dart
@@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import 'package:linum/core/authentication/presentation/widgets/change_email_action_lip/change_email_form.dart';
import 'package:linum/generated/translation_keys.g.dart';
-class ChangeEmailActionLip extends StatelessWidget {
- const ChangeEmailActionLip({
+class ChangeEmailBottomSheet extends StatelessWidget {
+ const ChangeEmailBottomSheet({
super.key,
});
@@ -30,6 +30,7 @@ class ChangeEmailActionLip extends StatelessWidget {
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: ChangeEmailForm(),
),
+ const SizedBox(height: 32,),
],
);
}
diff --git a/lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_form.dart b/lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_form.dart
index 1a29c1e9..21d215f9 100644
--- a/lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_form.dart
+++ b/lib/core/authentication/presentation/widgets/change_email_action_lip/change_email_form.dart
@@ -2,14 +2,9 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
-import 'package:gradient_widgets/gradient_widgets.dart';
-import 'package:linum/common/components/action_lip/viewmodels/action_lip_viewmodel.dart';
import 'package:linum/common/components/dialogs/show_alert_dialog.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
import 'package:linum/core/authentication/presentation/widgets/change_email_action_lip/email_input_field.dart';
-import 'package:linum/core/design/layout/enums/screen_key.dart';
-import 'package:linum/core/design/layout/utils/layout_helpers.dart';
-import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
import 'package:linum/generated/translation_keys.g.dart';
import 'package:provider/provider.dart';
@@ -31,17 +26,6 @@ class _ChangeEmailFormState extends State {
children: [
Container(
padding: const EdgeInsets.all(5.0),
- decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.onSecondary,
- borderRadius: BorderRadius.circular(10),
- boxShadow: [
- BoxShadow(
- color: Theme.of(context).colorScheme.onBackground,
- blurRadius: 20.0,
- offset: const Offset(0, 10),
- ),
- ],
- ),
child: Column(
children: [
EmailInputField(
@@ -69,37 +53,21 @@ class _ChangeEmailFormState extends State {
onEditingComplete: () => _callback(context),
hintLabel: tr(translationKeys.actionLip.changeEmail.hintLabelRepeat),
),
+ FilledButton(
+ onPressed: () {
+ final bool valid = _formKey.currentState!.validate();
+ if (valid) {
+ _callback(context);
+ }
+ },
+ child: Text(
+ tr(translationKeys.actionLip.changeEmail.buttonSubmit),
+ style: Theme.of(context).textTheme.labelLarge,
+ ),
+ ),
],
),
),
- SizedBox(
- height: context.proportionateScreenHeight(32),
- ),
- GradientButton(
- increaseHeightBy:
- context.proportionateScreenHeight(16),
- callback: () {
- final bool valid = _formKey.currentState!.validate();
- if (valid) {
- _callback(context);
- }
- },
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.primary,
- Theme.of(context).colorScheme.surface,
- ],
- ),
- elevation: 0,
- increaseWidthBy: double.infinity,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(8),
- ),
- child: Text(
- tr(translationKeys.actionLip.changeEmail.buttonSubmit),
- style: Theme.of(context).textTheme.labelLarge,
- ),
- ),
],
),
);
@@ -107,7 +75,6 @@ class _ChangeEmailFormState extends State {
void _callback(BuildContext context) {
final authService = context.read();
- final viewModel = context.read();
authService.updateEmail(
_controller.text,
@@ -123,11 +90,7 @@ class _ChangeEmailFormState extends State {
title: translationKeys.alertdialog.updateEmail.title,
actionTitle: translationKeys.alertdialog.updateEmail.action,
);
- viewModel.setActionLipStatus(
- context: context,
- screenKey: ScreenKey.settings,
- status: ActionLipVisibility.hidden,
- );
+ Navigator.pop(context);
FocusManager.instance.primaryFocus?.unfocus();
},
);
diff --git a/lib/core/authentication/presentation/widgets/change_email_action_lip/email_input_field.dart b/lib/core/authentication/presentation/widgets/change_email_action_lip/email_input_field.dart
index 44a54e8f..25dd17c0 100644
--- a/lib/core/authentication/presentation/widgets/change_email_action_lip/email_input_field.dart
+++ b/lib/core/authentication/presentation/widgets/change_email_action_lip/email_input_field.dart
@@ -28,10 +28,8 @@ class EmailInputField extends StatelessWidget {
keyboardType: TextInputType.emailAddress,
onEditingComplete: onEditingComplete,
decoration: InputDecoration(
- border: InputBorder.none,
- hintText: hintLabel,
- hintStyle: Theme.of(context).textTheme.bodyLarge
- ?.copyWith(color: Theme.of(context).colorScheme.secondary),
+ border: const OutlineInputBorder(),
+ labelText: hintLabel,
),
),
);
diff --git a/lib/core/authentication/presentation/widgets/change_email_button.dart b/lib/core/authentication/presentation/widgets/change_email_button.dart
index 640f5eb1..0b15eefb 100644
--- a/lib/core/authentication/presentation/widgets/change_email_button.dart
+++ b/lib/core/authentication/presentation/widgets/change_email_button.dart
@@ -8,7 +8,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:linum/core/authentication/presentation/utils/show_change_email_action_lip.dart';
import 'package:linum/core/design/layout/enums/screen_key.dart';
-import 'package:linum/core/design/layout/utils/layout_helpers.dart';
import 'package:linum/generated/translation_keys.g.dart';
class ChangeEmailButton extends StatelessWidget {
@@ -19,29 +18,20 @@ class ChangeEmailButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
return OutlinedButton(
key: const Key("changeEmailButton"),
- onPressed: () => showChangeEmailActionLip(context, screenKey),
+ onPressed: () => showChangeEmailBottomSheet(context, screenKey),
style: OutlinedButton.styleFrom(
- elevation: 8,
- shadowColor: Theme.of(context).colorScheme.onBackground,
- minimumSize: Size(
- double.infinity,
- context.proportionateScreenHeight(48),
- ),
- backgroundColor: Theme.of(context).colorScheme.background,
side: BorderSide(
- width: 2,
- color: Theme.of(context).colorScheme.primary,
- ),
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(8),
+ color: theme.colorScheme.primary,
),
),
child: Text(
tr(translationKeys.settingsScreen.systemSettings.buttonChangeEmail),
- style: Theme.of(context).textTheme.labelLarge
- ?.copyWith(color: Theme.of(context).colorScheme.onSurface),
+ style: theme.textTheme.labelMedium
+ ?.copyWith(color: theme.colorScheme.primary),
),
);
}
diff --git a/lib/core/authentication/presentation/widgets/delete_user_button.dart b/lib/core/authentication/presentation/widgets/delete_user_button.dart
index 20fc0eb6..0bf83040 100644
--- a/lib/core/authentication/presentation/widgets/delete_user_button.dart
+++ b/lib/core/authentication/presentation/widgets/delete_user_button.dart
@@ -4,7 +4,6 @@ import 'package:linum/common/components/dialogs/dialog_action.dart';
import 'package:linum/common/components/dialogs/show_action_dialog.dart';
import 'package:linum/common/components/dialogs/show_alert_dialog.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
-import 'package:linum/core/design/layout/utils/layout_helpers.dart';
import 'package:linum/generated/translation_keys.g.dart';
import 'package:provider/provider.dart';
@@ -13,65 +12,51 @@ class DeleteUserButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return Padding(
- padding: const EdgeInsets.symmetric(
- vertical: 8.0,
- ),
- child: OutlinedButton(
- onPressed: () {
- showActionDialog(
- context,
- message: tr(translationKeys.alertdialog.deleteAccount.message),
- actions: [
- DialogAction(
- actionTitle:
- tr(translationKeys.alertdialog.deleteAccount.cancel),
- dialogPurpose: DialogPurpose.secondary,
- ),
- DialogAction(
- actionTitle:
- tr(translationKeys.alertdialog.deleteAccount.action),
- dialogPurpose: DialogPurpose.danger,
- callback: () {
- final authService = context.read();
- final onError = (String message) => showAlertDialog(
- context,
- message: message,
- title: translationKeys.alertdialog.resetPassword.title,
- actionTitle: translationKeys.alertdialog.resetPassword.action,
- userMustDismissWithButton: true,
- );
- authService.deleteUserAccount(onError: onError);
- },
- ),
- ],
- title: tr(translationKeys.alertdialog.deleteAccount.title),
- );
- },
- style: OutlinedButton.styleFrom(
- elevation: 8,
- shadowColor: Theme.of(context).colorScheme.onBackground,
- minimumSize: Size(
- double.infinity,
- context.proportionateScreenHeight(48),
- ),
- backgroundColor: Theme.of(context).colorScheme.background,
- side: BorderSide(
- width: 2,
- color: Theme.of(context).colorScheme.error,
- ),
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(8),
- ),
- ),
- child: Text(
- tr(translationKeys.settingsScreen.systemSettings.buttonDeleteUser),
- style: Theme.of(context)
- .textTheme
- .labelLarge
- ?.copyWith(color: Theme.of(context).colorScheme.error),
+ final theme = Theme.of(context);
+
+ return OutlinedButton(
+ onPressed: () {
+ showActionDialog(
+ context,
+ message: tr(translationKeys.alertdialog.deleteAccount.message),
+ actions: [
+ DialogAction(
+ actionTitle:
+ tr(translationKeys.alertdialog.deleteAccount.cancel),
+ dialogPurpose: DialogPurpose.secondary,
+ ),
+ DialogAction(
+ actionTitle:
+ tr(translationKeys.alertdialog.deleteAccount.action),
+ dialogPurpose: DialogPurpose.danger,
+ callback: () {
+ final authService = context.read();
+ final onError = (String message) => showAlertDialog(
+ context,
+ message: message,
+ title: translationKeys.alertdialog.resetPassword.title,
+ actionTitle: translationKeys.alertdialog.resetPassword.action,
+ userMustDismissWithButton: true,
+ );
+ authService.deleteUserAccount(onError: onError);
+ },
+ ),
+ ],
+ title: tr(translationKeys.alertdialog.deleteAccount.title),
+ );
+ },
+ style: OutlinedButton.styleFrom(
+ side: BorderSide(
+ color: theme.colorScheme.error,
),
),
+ child: Text(
+ tr(translationKeys.settingsScreen.systemSettings.buttonDeleteUser),
+ style: theme
+ .textTheme
+ .labelMedium
+ ?.copyWith(color: theme.colorScheme.error),
+ ),
);
}
}
diff --git a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_input_field.dart b/lib/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_input_field.dart
similarity index 83%
rename from lib/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_input_field.dart
rename to lib/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_input_field.dart
index 515a643f..f82d6561 100644
--- a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_input_field.dart
+++ b/lib/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_input_field.dart
@@ -5,9 +5,9 @@ import 'package:linum/core/authentication/domain/services/authentication_service
import 'package:linum/generated/translation_keys.g.dart';
import 'package:provider/provider.dart';
-class UnregisteredUserInputField extends StatelessWidget {
+class ForgotPasswordInputField extends StatelessWidget {
final TextEditingController controller;
- const UnregisteredUserInputField({
+ const ForgotPasswordInputField({
super.key,
required this.controller,
});
@@ -42,14 +42,10 @@ class UnregisteredUserInputField extends StatelessWidget {
),
},
decoration: InputDecoration(
- border: InputBorder.none,
- hintText: tr(
+ border: const OutlineInputBorder(),
+ labelText: tr(
translationKeys.onboardingScreen.loginEmailHintlabel,
),
- hintStyle: Theme.of(context).textTheme.bodyLarge
- ?.copyWith(
- color: Theme.of(context).colorScheme.secondary,
- ),
),
),
);
diff --git a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_action_lip_scaffold.dart b/lib/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_scaffold.dart
similarity index 51%
rename from lib/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_action_lip_scaffold.dart
rename to lib/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_scaffold.dart
index 3dfde17f..e416a5e3 100644
--- a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_action_lip_scaffold.dart
+++ b/lib/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_scaffold.dart
@@ -1,14 +1,12 @@
import 'package:flutter/material.dart';
-import 'package:gradient_widgets/gradient_widgets.dart';
-import 'package:linum/core/design/layout/utils/layout_helpers.dart';
-class ForgotPasswordActionLipScaffold extends StatelessWidget {
+class ForgotPasswordScaffold extends StatelessWidget {
final String label;
final Widget inputField;
final VoidCallback callback;
final String buttonLabel;
final TextEditingController controller;
- const ForgotPasswordActionLipScaffold({
+ const ForgotPasswordScaffold({
super.key,
required this.label,
required this.inputField,
@@ -19,6 +17,8 @@ class ForgotPasswordActionLipScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
return Column(
children: [
Padding(
@@ -30,7 +30,7 @@ class ForgotPasswordActionLipScaffold extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
label,
- style: Theme.of(context).textTheme.bodyLarge,
+ style: theme.textTheme.bodyLarge,
textAlign: TextAlign.center,
),
),
@@ -42,15 +42,8 @@ class ForgotPasswordActionLipScaffold extends StatelessWidget {
Container(
padding: const EdgeInsets.all(5.0),
decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.onSecondary,
+ color: theme.colorScheme.onSecondary,
borderRadius: BorderRadius.circular(10),
- boxShadow: [
- BoxShadow(
- color: Theme.of(context).colorScheme.onBackground,
- blurRadius: 20.0,
- offset: const Offset(0, 10),
- ),
- ],
),
child: Column(
children: [
@@ -58,30 +51,16 @@ class ForgotPasswordActionLipScaffold extends StatelessWidget {
],
),
),
- SizedBox(
- height: context.proportionateScreenHeight(32),
- ),
- GradientButton(
- increaseHeightBy:
- context.proportionateScreenHeight(16),
+
+ FilledButton(
// Logged Out onPressed
- callback: callback,
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.primary,
- Theme.of(context).colorScheme.surface,
- ],
- ),
- elevation: 0,
- increaseWidthBy: double.infinity,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(8),
- ),
+ onPressed: callback,
child: Text(
buttonLabel,
- style: Theme.of(context).textTheme.labelLarge,
+ style: theme.textTheme.labelLarge,
),
),
+ const SizedBox(height: 32,),
],
),
),
diff --git a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_forgot_password_action_lip.dart b/lib/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_forgot_password_bottom_sheet.dart
similarity index 64%
rename from lib/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_forgot_password_action_lip.dart
rename to lib/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_forgot_password_bottom_sheet.dart
index cba183c3..d03ec03f 100644
--- a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_forgot_password_action_lip.dart
+++ b/lib/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_forgot_password_bottom_sheet.dart
@@ -1,28 +1,25 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
-import 'package:linum/common/components/action_lip/viewmodels/action_lip_viewmodel.dart';
import 'package:linum/common/components/dialogs/show_alert_dialog.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
-import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_action_lip_scaffold.dart';
-import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_input_field.dart';
-import 'package:linum/core/design/layout/enums/screen_key.dart';
-import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
+import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_input_field.dart';
+import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_scaffold.dart';
import 'package:linum/generated/translation_keys.g.dart';
import 'package:provider/provider.dart';
-class RegisteredUserForgotPasswordActionLip extends StatelessWidget {
+class RegisteredUserForgotPasswordBottomSheet extends StatelessWidget {
final TextEditingController controller;
- const RegisteredUserForgotPasswordActionLip({
+ const RegisteredUserForgotPasswordBottomSheet({
super.key,
required this.controller,
});
@override
Widget build(BuildContext context) {
- return ForgotPasswordActionLipScaffold(
+ return ForgotPasswordScaffold(
label: tr(translationKeys.actionLip.forgotPassword.loggedIn.labelDescription),
- inputField: RegisteredUserInputField(controller: controller),
+ inputField: ForgotPasswordInputField(controller: controller),
controller: controller,
callback: () => _callback(context),
buttonLabel: tr(translationKeys.actionLip.forgotPassword.loggedIn.buttonSubmit),
@@ -31,7 +28,6 @@ class RegisteredUserForgotPasswordActionLip extends StatelessWidget {
void _callback(BuildContext context) {
final authService = context.read();
- final viewModel = context.read();
authService.updatePassword(
controller.text,
@@ -46,11 +42,7 @@ class RegisteredUserForgotPasswordActionLip extends StatelessWidget {
title: translationKeys.alertdialog.updatePassword.title,
actionTitle: translationKeys.alertdialog.updatePassword.action,
);
- viewModel.setActionLipStatus(
- context: context,
- screenKey: ScreenKey.settings,
- status: ActionLipVisibility.hidden,
- );
+ Navigator.of(context).pop();
FocusManager.instance.primaryFocus?.unfocus();
},
);
diff --git a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_input_field.dart b/lib/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_input_field.dart
deleted file mode 100644
index 2a88e883..00000000
--- a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/registered_user_input_field.dart
+++ /dev/null
@@ -1,56 +0,0 @@
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-import 'package:linum/common/components/dialogs/show_alert_dialog.dart';
-import 'package:linum/core/authentication/domain/services/authentication_service.dart';
-import 'package:linum/generated/translation_keys.g.dart';
-import 'package:provider/provider.dart';
-
-class RegisteredUserInputField extends StatelessWidget {
- final TextEditingController controller;
- const RegisteredUserInputField({
- super.key,
- required this.controller,
- });
-
- @override
- Widget build(BuildContext context) {
- final authService = context.read();
- return Container(
- padding: const EdgeInsets.all(8.0),
- decoration: BoxDecoration(
- border: Border(
- bottom: BorderSide(color: Colors.grey.shade100),
- ),
- ),
- child: TextField(
- obscureText: true,
- controller: controller,
- keyboardType: TextInputType.visiblePassword,
- onEditingComplete: () => {
- authService.updatePassword(
- controller.text,
- onError: (message) => showAlertDialog(
- context,
- message: message,
- ),
- onComplete: (message) => showAlertDialog(
- context,
- message: message,
- title: translationKeys.alertdialog.updatePassword.title,
- actionTitle: translationKeys.alertdialog.updatePassword.action,
- userMustDismissWithButton: true,
- ),
- ),
- },
- decoration: InputDecoration(
- border: InputBorder.none,
- hintText: tr(
- translationKeys.onboardingScreen.loginPasswordHintlabel,
- ),
- hintStyle: Theme.of(context).textTheme.bodyLarge
- ?.copyWith(color: Theme.of(context).colorScheme.secondary),
- ),
- ),
- );
- }
-}
diff --git a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_forgot_password_action_lip.dart b/lib/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_forgot_password_bottom_sheet.dart
similarity index 60%
rename from lib/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_forgot_password_action_lip.dart
rename to lib/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_forgot_password_bottom_sheet.dart
index 466d402e..8e1c9808 100644
--- a/lib/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_forgot_password_action_lip.dart
+++ b/lib/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_forgot_password_bottom_sheet.dart
@@ -1,28 +1,25 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
-import 'package:linum/common/components/action_lip/viewmodels/action_lip_viewmodel.dart';
import 'package:linum/common/components/dialogs/show_alert_dialog.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
-import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_action_lip_scaffold.dart';
-import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/unregistered_user_input_field.dart';
-import 'package:linum/core/design/layout/enums/screen_key.dart';
-import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
+import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_input_field.dart';
+import 'package:linum/core/authentication/presentation/widgets/forgot_password_action_lip/forgot_password_scaffold.dart';
import 'package:linum/generated/translation_keys.g.dart';
import 'package:provider/provider.dart';
-class UnregisteredUserForgotPasswordActionLip extends StatelessWidget {
+class UnregisteredUserForgotPasswordBottomSheet extends StatelessWidget {
final TextEditingController controller;
- const UnregisteredUserForgotPasswordActionLip({
+ const UnregisteredUserForgotPasswordBottomSheet({
super.key,
required this.controller,
});
@override
Widget build(BuildContext context) {
- return ForgotPasswordActionLipScaffold(
+ return ForgotPasswordScaffold(
label: tr(translationKeys.actionLip.forgotPassword.loggedOut.labelDescription),
- inputField: UnregisteredUserInputField(controller: controller),
+ inputField: ForgotPasswordInputField(controller: controller),
controller: controller,
callback: () => _callback(context),
buttonLabel: tr(translationKeys.actionLip.forgotPassword.loggedOut.buttonSubmit),
@@ -31,14 +28,18 @@ class UnregisteredUserForgotPasswordActionLip extends StatelessWidget {
void _callback(BuildContext context) {
final authService = context.read();
- final viewModel = context.read();
authService.resetPassword(
controller.text,
- onError: (message) => showAlertDialog(
- context,
- message:message,
- ),
+ onError: (message)
+ {
+ showAlertDialog(
+ context,
+ title: translationKeys.alertdialog.error.titleStandard,
+ message: message,
+ actionTitle: translationKeys.alertdialog.error.actionStandard,
+ );
+ },
onComplete: (String message) {
showAlertDialog(
context,
@@ -46,11 +47,7 @@ class UnregisteredUserForgotPasswordActionLip extends StatelessWidget {
title: translationKeys.alertdialog.resetPassword.title,
actionTitle: translationKeys.alertdialog.resetPassword.action,
);
- viewModel.setActionLipStatus(
- context: context,
- screenKey: ScreenKey.onboarding,
- status: ActionLipVisibility.hidden,
- );
+ Navigator.pop(context);
FocusManager.instance.primaryFocus?.unfocus();
},
);
diff --git a/lib/core/authentication/presentation/widgets/forgot_password.dart b/lib/core/authentication/presentation/widgets/forgot_password_button.dart
similarity index 65%
rename from lib/core/authentication/presentation/widgets/forgot_password.dart
rename to lib/core/authentication/presentation/widgets/forgot_password_button.dart
index f48d7648..c28c63fb 100644
--- a/lib/core/authentication/presentation/widgets/forgot_password.dart
+++ b/lib/core/authentication/presentation/widgets/forgot_password_button.dart
@@ -16,8 +16,9 @@ import 'package:provider/provider.dart';
class ForgotPasswordButton extends StatelessWidget {
final ScreenKey screenKey;
+ final bool m3;
- const ForgotPasswordButton(this.screenKey);
+ const ForgotPasswordButton(this.screenKey, {this.m3 = true});
@override
@@ -25,17 +26,36 @@ class ForgotPasswordButton extends StatelessWidget {
final AuthenticationService authenticationService =
context.read();
+ final theme = Theme.of(context);
+
+ if (m3) {
+ return OutlinedButton(
+ key: const Key("forgotPasswordButton"),
+ onPressed: () => showForgotPasswordBottomSheet(context, screenKey),
+ style: OutlinedButton.styleFrom(
+ side: BorderSide(
+ color: theme.colorScheme.primary,
+ ),
+ ),
+ child: Text(
+ authenticationService.isLoggedIn
+ ? tr(translationKeys.settingsScreen.systemSettings.buttonForgotPassword)
+ : tr(translationKeys.onboardingScreen.loginLipForgotPasswordButton),
+ style: theme.textTheme.labelMedium
+ ?.copyWith(color: theme.colorScheme.primary),
+ ),
+ );
+ }
+
return OutlinedButton(
key: const Key("forgotPasswordButton"),
- onPressed: () => showForgotPasswordActionLip(context, screenKey),
+ onPressed: () => showForgotPasswordBottomSheet(context, screenKey),
style: OutlinedButton.styleFrom(
- elevation: 8,
- shadowColor: Theme.of(context).colorScheme.onBackground,
minimumSize: Size(
double.infinity,
context.proportionateScreenHeight(48),
),
- backgroundColor: Theme.of(context).colorScheme.background,
+ backgroundColor: Theme.of(context).colorScheme.surface,
side: BorderSide(
width: 2,
color: Theme.of(context).colorScheme.primary,
diff --git a/lib/core/authentication/presentation/widgets/logout_button.dart b/lib/core/authentication/presentation/widgets/logout_button.dart
new file mode 100644
index 00000000..292810bf
--- /dev/null
+++ b/lib/core/authentication/presentation/widgets/logout_button.dart
@@ -0,0 +1,33 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:linum/core/authentication/domain/services/authentication_service.dart';
+import 'package:linum/core/navigation/get_delegate.dart';
+import 'package:linum/generated/translation_keys.g.dart';
+import 'package:linum/screens/lock_screen/services/pin_code_service.dart';
+import 'package:provider/provider.dart';
+
+class LogoutButton extends StatelessWidget {
+ const LogoutButton({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return FilledButton(
+ key: const Key("logoutButton"),
+ onPressed: () => context.read()
+ .signOut()
+ .then((_) {
+ context.getMainRouterDelegate().rebuild();
+ context.read()
+ .resetOnLogout();
+ }),
+ child: Text(
+ tr(translationKeys.settingsScreen.systemSettings.buttonSignout),
+ style: theme.textTheme.labelMedium?.copyWith(
+ color: Colors.white,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/core/authentication/presentation/widgets/logout_form.dart b/lib/core/authentication/presentation/widgets/logout_form.dart
index 9658d423..cbb89f5a 100644
--- a/lib/core/authentication/presentation/widgets/logout_form.dart
+++ b/lib/core/authentication/presentation/widgets/logout_form.dart
@@ -6,15 +6,12 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
-import 'package:gradient_widgets/gradient_widgets.dart';
+import 'package:linum/common/widgets/list_divider.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
import 'package:linum/core/design/layout/utils/layout_helpers.dart';
-import 'package:linum/core/navigation/get_delegate.dart';
import 'package:linum/generated/translation_keys.g.dart';
-import 'package:linum/screens/lock_screen/services/pin_code_service.dart';
import 'package:provider/provider.dart';
-
class LogoutForm extends StatefulWidget {
@override
State createState() => _LogoutFormState();
@@ -23,48 +20,33 @@ class LogoutForm extends StatefulWidget {
class _LogoutFormState extends State {
@override
Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
return Column(
children: [
Padding(
- padding: EdgeInsets.symmetric(
- vertical: context.proportionateScreenHeight(16),
- ),
- child: Consumer(
- builder: (context, authService, _) {
- return Text(
- tr(translationKeys.logoutForm.labelCurrentEmail) + authService.userEmail,
- style: Theme.of(context).textTheme.bodyLarge,
- textAlign: TextAlign.center,
- );
- },
+ padding: EdgeInsets.only(
+ top: context.proportionateScreenHeight(16),
),
- ),
- GradientButton(
- key: const Key("logoutButton"),
- increaseHeightBy: context.proportionateScreenHeight(16),
- callback: () => context.read()
- .signOut()
- .then((_) {
- context.getMainRouterDelegate().rebuild();
- context.read()
- .resetOnLogout();
- }),
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.primary,
- Theme.of(context).colorScheme.surface,
+ child: Column(
+ children: [
+ Consumer(
+ builder: (context, authService, _) {
+ return Text(
+ tr(translationKeys.logoutForm.labelCurrentEmail) +
+ authService.userEmail,
+ style: theme.textTheme.bodyLarge,
+ textAlign: TextAlign.center,
+ );
+ },
+ ),
+ const ListDivider(
+ L: 32,
+ R: 32,
+ B: 0,
+ ),
],
),
- elevation: 0,
- increaseWidthBy: double.infinity,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
- child: Text(
- tr(translationKeys.settingsScreen.systemSettings.buttonSignout),
- style: Theme.of(context).textTheme.labelLarge,
- ),
- ),
- SizedBox(
- height: context.proportionateScreenHeight(8),
),
],
);
diff --git a/lib/core/authentication/presentation/widgets/sign_in_sign_up_button.dart b/lib/core/authentication/presentation/widgets/sign_in_sign_up_button.dart
index 0af7c2d3..b5d4ab67 100644
--- a/lib/core/authentication/presentation/widgets/sign_in_sign_up_button.dart
+++ b/lib/core/authentication/presentation/widgets/sign_in_sign_up_button.dart
@@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
-import 'package:gradient_widgets/gradient_widgets.dart';
+import 'package:linum/common/widgets/gradient_button.dart';
import 'package:linum/core/design/layout/utils/layout_helpers.dart';
-
-
class SignInSignUpButton extends StatelessWidget {
final Function() callback;
final String text;
@@ -17,18 +15,16 @@ class SignInSignUpButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GradientButton(
- increaseHeightBy: context.proportionateScreenHeight(16),
- callback: callback,
+ onPressed: callback,
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primary,
- Theme.of(context).colorScheme.surface,
+ Theme.of(context).colorScheme.tertiary,
],
),
- elevation: 0,
- increaseWidthBy: double.infinity,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(8),
+ minimumSize: Size(
+ double.infinity,
+ context.proportionateScreenHeight(48),
),
child: Text(
text,
diff --git a/lib/core/balance/repositories/balance_data_repository.dart b/lib/core/balance/repositories/balance_data_repository.dart
index 4b9c86f6..47ce4ca0 100644
--- a/lib/core/balance/repositories/balance_data_repository.dart
+++ b/lib/core/balance/repositories/balance_data_repository.dart
@@ -113,7 +113,7 @@ class BalanceDataRepository {
return doc;
} else {
- Logger().wtf("no data found in documentToUser");
+ Logger().f("no data found in documentToUser");
}
return null;
}
diff --git a/lib/core/balance/services/balance_data_service.dart b/lib/core/balance/services/balance_data_service.dart
index 16bc9d89..6946f0b0 100644
--- a/lib/core/balance/services/balance_data_service.dart
+++ b/lib/core/balance/services/balance_data_service.dart
@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart' hide Transaction;
-import 'package:flutter/cupertino.dart';
+import 'package:linum/common/interfaces/service_interface.dart';
import 'package:linum/core/balance/enums/serial_transaction_change_type_enum.dart';
import 'package:linum/core/balance/models/balance_document.dart';
import 'package:linum/core/balance/models/serial_transaction.dart';
@@ -11,7 +11,7 @@ import 'package:linum/core/balance/utils/serial_transaction_manager.dart';
import 'package:linum/core/balance/utils/transaction_manager.dart';
import 'package:logger/logger.dart';
-class BalanceDataService extends ChangeNotifier {
+class BalanceDataService extends IProvidableService {
final String userId;
BalanceDataService(this.userId) {
diff --git a/lib/core/balance/utils/serial_transaction_updater.dart b/lib/core/balance/utils/serial_transaction_updater.dart
index f800c417..58f6da67 100644
--- a/lib/core/balance/utils/serial_transaction_updater.dart
+++ b/lib/core/balance/utils/serial_transaction_updater.dart
@@ -81,7 +81,7 @@ class SerialTransactionUpdater {
isEdited = true;
} else if (newDate != null && oldDate != null) {
updatedInitialTime = Timestamp.fromDate(
- (serialTransaction.startDate).toDate().subtract(
+ serialTransaction.startDate.toDate().subtract(
oldDate.toDate().difference(newDate.toDate()),
),
);
diff --git a/lib/core/balance/utils/statistical_calculations.dart b/lib/core/balance/utils/statistical_calculations.dart
index ceb03954..46931780 100644
--- a/lib/core/balance/utils/statistical_calculations.dart
+++ b/lib/core/balance/utils/statistical_calculations.dart
@@ -103,7 +103,7 @@ class StatisticalCalculations {
if (t is Transaction) {
return t.repeatId == null;
}
- log.wtf(
+ log.f(
"Somehow in a list of Transaction an item wasn't a Transaction",
);
return true;
diff --git a/lib/core/categories/settings/domain/category_settings_service_impl.dart b/lib/core/categories/settings/domain/category_settings_service_impl.dart
index fc3fa979..e62a88c0 100644
--- a/lib/core/categories/settings/domain/category_settings_service_impl.dart
+++ b/lib/core/categories/settings/domain/category_settings_service_impl.dart
@@ -66,4 +66,9 @@ class CategorySettingsServiceImpl
return _repository.update(update);
}
+ @override
+ Future ready() async {
+ return _repository.ready();
+ }
+
}
diff --git a/lib/core/categories/settings/presentation/category_settings_service.dart b/lib/core/categories/settings/presentation/category_settings_service.dart
index 925e3580..0776d49a 100644
--- a/lib/core/categories/settings/presentation/category_settings_service.dart
+++ b/lib/core/categories/settings/presentation/category_settings_service.dart
@@ -1,8 +1,8 @@
-import 'package:flutter/material.dart';
import 'package:linum/common/enums/entry_type.dart';
+import 'package:linum/common/interfaces/service_interface.dart';
import 'package:linum/core/categories/core/data/models/category.dart';
-abstract class ICategorySettingsService with ChangeNotifier {
+abstract class ICategorySettingsService extends IProvidableService with NotifyReady {
Category? getEntryCategory(EntryType entryType);
Category getIncomeEntryCategory();
Category getExpenseEntryCategory();
diff --git a/lib/core/categories/settings/presentation/utils/show_change_standard_category_action_lip.dart b/lib/core/categories/settings/presentation/utils/show_change_standard_category_action_lip.dart
index 9ce5afcc..428bd32b 100644
--- a/lib/core/categories/settings/presentation/utils/show_change_standard_category_action_lip.dart
+++ b/lib/core/categories/settings/presentation/utils/show_change_standard_category_action_lip.dart
@@ -1,44 +1,39 @@
-import 'package:flutter/cupertino.dart';
-import 'package:linum/common/components/action_lip/viewmodels/action_lip_viewmodel.dart';
+import 'package:flutter/material.dart';
+import 'package:linum/common/components/sheets/linum_bottom_sheet.dart';
import 'package:linum/common/enums/entry_type.dart';
import 'package:linum/core/categories/core/constants/standard_categories.dart';
import 'package:linum/core/categories/core/data/models/category.dart';
import 'package:linum/core/categories/settings/presentation/category_settings_service.dart';
import 'package:linum/core/categories/settings/presentation/widgets/category_list_view.dart';
-import 'package:linum/core/design/layout/enums/screen_key.dart';
-import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
import 'package:provider/provider.dart';
-void showChangeStandardCategoryActionLip(BuildContext context, {
+void showChangeStandardCategoryBottomSheet(BuildContext context, {
required EntryType entryType,
- required String lipTitle,
+ required String title,
}) {
final categorySettings = context.read();
- final actionLipViewModel = context.read();
void onCategorySelection(Category category) {
categorySettings.setEntryCategory(category);
- actionLipViewModel.setActionLipStatus(
- context: context,
- screenKey: ScreenKey.settings,
- status: ActionLipVisibility.hidden,
- );
+ Navigator.pop(context);
}
final categoryList = standardCategories.values
.where((category) => category.entryType == entryType)
.toList();
-
-
- actionLipViewModel.setActionLip(
+
+ showModalBottomSheet(
context: context,
- screenKey: ScreenKey.settings,
- actionLipStatus: ActionLipVisibility.onviewport,
- actionLipTitle: lipTitle,
- actionLipBody: CategoryListView(
- categories: categoryList,
- defaultCategoryId: categorySettings.getEntryCategory(entryType)?.id ?? "",
- onCategorySelection: onCategorySelection,
- ),
+ builder: (BuildContext context) {
+ return LinumBottomSheet(
+ title: title,
+ body: CategoryListView(
+ categories: categoryList,
+ defaultCategoryId: categorySettings.getEntryCategory(entryType)?.id ?? "",
+ onCategorySelection: onCategorySelection,
+ ),
+ );
+ },
);
+
}
diff --git a/lib/core/categories/settings/presentation/widgets/standard_category_selector.dart b/lib/core/categories/settings/presentation/widgets/standard_category_selector.dart
index 59bb65aa..7d234c00 100644
--- a/lib/core/categories/settings/presentation/widgets/standard_category_selector.dart
+++ b/lib/core/categories/settings/presentation/widgets/standard_category_selector.dart
@@ -10,7 +10,7 @@ class StandardCategorySelector extends StatelessWidget {
final EntryType entryType;
final IconData icon;
final Color iconColor;
- final String lipTitle;
+ final String title;
final String buttonTitle;
const StandardCategorySelector({
@@ -18,7 +18,7 @@ class StandardCategorySelector extends StatelessWidget {
required this.entryType,
required this.icon,
required this.iconColor,
- required this.lipTitle,
+ required this.title,
required this.buttonTitle,
});
@@ -26,10 +26,10 @@ class StandardCategorySelector extends StatelessWidget {
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
- showChangeStandardCategoryActionLip(
+ showChangeStandardCategoryBottomSheet(
context,
entryType: entryType,
- lipTitle: lipTitle,
+ title: title,
);
},
child: Selector(
diff --git a/lib/core/categories/settings/presentation/widgets/standard_category_selectors.dart b/lib/core/categories/settings/presentation/widgets/standard_category_selectors.dart
index 99d61a3c..b354e580 100644
--- a/lib/core/categories/settings/presentation/widgets/standard_category_selectors.dart
+++ b/lib/core/categories/settings/presentation/widgets/standard_category_selectors.dart
@@ -27,14 +27,14 @@ class _StandardCategorySelectorsState extends State {
icon: Icons.north_east,
iconColor: Colors.green,
buttonTitle: tr(translationKeys.settingsScreen.standardIncomeSelector.labelTitle),
- lipTitle: tr(translationKeys.actionLip.standardCategory.income.labelTitle),
+ title: tr(translationKeys.actionLip.standardCategory.income.labelTitle),
),
StandardCategorySelector(
entryType: EntryType.expense,
icon: Icons.south_east,
iconColor: Colors.redAccent,
buttonTitle: tr(translationKeys.settingsScreen.standardExpenseSelector.labelTitle),
- lipTitle: tr(translationKeys.actionLip.standardCategory.expenses.labelTitle),
+ title: tr(translationKeys.actionLip.standardCategory.expenses.labelTitle),
),
],
);
diff --git a/lib/core/design/layout/screen_layout.dart b/lib/core/design/layout/screen_layout.dart
index 199b642d..d9dc6e8e 100644
--- a/lib/core/design/layout/screen_layout.dart
+++ b/lib/core/design/layout/screen_layout.dart
@@ -13,6 +13,7 @@ import 'package:provider/provider.dart';
/// The Layout for every main screen in Linum.
/// The app bar and floating action button are defined here.
+// TODO: This is Layout and ViewModel in one Widget. Consider separation
class ScreenLayout extends StatefulWidget {
final ScreenKey currentScreen;
final dynamic settings;
@@ -59,14 +60,19 @@ class _ScreenLayoutState extends State
}
}
+ void handleActionButton() {
+ // TODO: Move this code somewhere else where its contents can be changed
+
+ showEnterScreen(context);
+ }
+
@override
Widget build(BuildContext context) {
final CollectionReference balance =
- FirebaseFirestore.instance.collection('balance');
+ FirebaseFirestore.instance.collection('balance');
final routerDelegate = context.getMainRouterDelegate();
-
// ignore: unused_local_variable
final Widget loadingIndicator = Container(
color: Colors.grey[300],
@@ -89,16 +95,14 @@ class _ScreenLayoutState extends State
},
),
),
- floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
- floatingActionButton: FloatingActionButton(
- onPressed: () {
- showEnterScreen(context);
- },
+/* floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
+ floatingActionButton: FloatingActionButton.large(
+ onPressed: handleActionButton,
elevation: 2.0,
- backgroundColor: Theme.of(context).colorScheme.secondary,
- child: const Icon(Icons.add),
- ),
- bottomNavigationBar: FABBottomAppBar(
+ backgroundColor: Colors.white,
+ child: const Icon(Icons.add, color: Colors.black87),
+ ),*/
+ bottomNavigationBar: LinumNavigationBar(
items: [
BottomAppBarItem(
iconData: Icons.home,
@@ -126,10 +130,12 @@ class _ScreenLayoutState extends State
),
],
backgroundColor: Theme.of(context).colorScheme.primary,
+ useInlineAB: true,
centerItemText: '',
- color: Theme.of(context).colorScheme.background,
+ iconColor: Theme.of(context).colorScheme.surface,
selectedColor: Theme.of(context).colorScheme.secondary,
notchedShape: const CircularNotchedRectangle(),
+ onABPressed: handleActionButton, // TODO: Remove if decision is made to use the FAB
//gives the pageIndex the value (the current selected index in the
//bottom navigation bar)
),
diff --git a/lib/core/design/layout/utils/layout_helpers.dart b/lib/core/design/layout/utils/layout_helpers.dart
index 67af5b42..d6fed4d1 100644
--- a/lib/core/design/layout/utils/layout_helpers.dart
+++ b/lib/core/design/layout/utils/layout_helpers.dart
@@ -116,4 +116,22 @@ extension LayoutHelpers on BuildContext {
// on EVERY device.
return (inputWidth / LayoutReference.screenWidth) * useScreenWidth(this);
}
+
+
+ // The Iphone 15 Pro Max has an defaultWidth of 430.0
+ double scaledFontSize(double fontSize, {double defaultWidth = 430.0}) {
+ final screenWidth = MediaQuery.of(this).size.width;
+
+ final ratio = screenWidth / defaultWidth;
+
+ return fontSize * ratio;
+ }
+
+ double scaledHeight(double height, {double defaultHeight = 852.0}) {
+ final screenHeight = MediaQuery.of(this).size.height;
+
+ final ratio = screenHeight / defaultHeight;
+
+ return height * ratio;
+ }
}
diff --git a/lib/core/design/layout/widgets/app_bar_action.dart b/lib/core/design/layout/widgets/app_bar_action.dart
index 11a81d7b..d55f28ea 100644
--- a/lib/core/design/layout/widgets/app_bar_action.dart
+++ b/lib/core/design/layout/widgets/app_bar_action.dart
@@ -8,6 +8,11 @@ import 'package:flutter/material.dart';
import 'package:linum/core/navigation/get_delegate.dart';
import 'package:linum/core/navigation/main_routes.dart';
import 'package:logger/logger.dart';
+import 'package:wiredash/wiredash.dart';
+
+const Color lipContextColor = Color(
+ 0xFFC1E695,
+); //FUTURE TODO: We should not hardcode things. This should be drawn from the colorTheme in the future.
abstract class AppBarAction {
static Logger logger = Logger();
@@ -30,27 +35,35 @@ abstract class AppBarAction {
return AppBarAction.fromParameters(
icon: Icons.video_library_rounded,
ontap: () => context.getMainRouterDelegate().pushRoute(
- MainRoute.academy,
- ), // TODO: Find out why app closes on back-navigation
+ MainRoute.academy,
+ ),
+ );
+ },
+ DefaultAction.bugreport: (BuildContext context) {
+ return AppBarAction.fromParameters(
+ icon: Icons.bug_report_rounded,
+ ontap: () => Wiredash.of(context).show(inheritMaterialTheme: true),
);
},
DefaultAction.settings: (BuildContext context) {
return AppBarAction.fromParameters(
icon: Icons.settings_rounded,
ontap: () => context.getMainRouterDelegate().replaceLastRoute(
- MainRoute.settings,
- rememberReplacedRoute: true,
- ),
+ MainRoute.settings,
+ rememberReplacedRoute: true,
+ ),
);
},
- // TODO: Are these guys even used?
- DefaultAction.back: (BuildContext context) => const BackButton(),
+ DefaultAction.back: (BuildContext context) => const BackButton(
+ color: lipContextColor,
+ ),
DefaultAction.close: (BuildContext context) => const CloseButton(),
};
static IconButton fromParameters({
required IconData icon,
required void Function() ontap,
+ Color iconColor = lipContextColor,
bool active = true,
Key? key,
}) {
@@ -58,6 +71,7 @@ abstract class AppBarAction {
icon: Icon(icon),
onPressed: () => active ? ontap() : {},
key: key,
+ color: iconColor,
);
}
@@ -68,6 +82,7 @@ abstract class AppBarAction {
enum DefaultAction {
academy,
+ bugreport,
notification,
filter,
back,
diff --git a/lib/core/design/layout/widgets/body_section.dart b/lib/core/design/layout/widgets/body_section.dart
index c52bec7c..f4104279 100644
--- a/lib/core/design/layout/widgets/body_section.dart
+++ b/lib/core/design/layout/widgets/body_section.dart
@@ -33,7 +33,7 @@ class BodySection extends StatelessWidget {
),
// ignore: use_colored_box
child: Container(
- color: Theme.of(context).colorScheme.background,
+ color: Theme.of(context).colorScheme.surface,
child: Padding(
padding: hasScreenCard
? EdgeInsets.only(
diff --git a/lib/core/design/layout/widgets/bottom_app_bar.dart b/lib/core/design/layout/widgets/bottom_app_bar.dart
index 4b018636..6a204333 100644
--- a/lib/core/design/layout/widgets/bottom_app_bar.dart
+++ b/lib/core/design/layout/widgets/bottom_app_bar.dart
@@ -20,80 +20,73 @@ class BottomAppBarItem {
});
}
-class FABBottomAppBar extends StatefulWidget {
- const FABBottomAppBar({
+
+class LinumNavigationBar extends StatelessWidget {
+ const LinumNavigationBar({
required this.items,
required this.centerItemText,
required this.backgroundColor,
- required this.color,
+ required this.iconColor,
required this.selectedColor,
required this.notchedShape,
+ this.useInlineAB = false,
+ this.onABPressed,
});
+ final void Function()? onABPressed;
+ final bool useInlineAB;
final List items;
final String centerItemText;
double get notproportionateHeight => 64;
double get minHeight => 64.0;
double get iconSize => 26;
final Color backgroundColor;
- final Color color;
+ final Color iconColor;
final Color selectedColor;
final NotchedShape notchedShape;
- @override
- State createState() => FABBottomAppBarState();
-}
-
-class FABBottomAppBarState extends State {
@override
Widget build(BuildContext context) {
- final List items = List.generate(widget.items.length, (int index) {
+ final List items = List.generate(this.items.length, (int index) {
return _buildTabItem(
- item: widget.items[index],
+ context,
+ item: this.items[index],
index: index,
);
});
- items.insert(items.length >> 1, _buildMiddleTabItem());
+
+ if (useInlineAB) {
+ final actionButton = Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: FloatingActionButton(
+ onPressed: onABPressed,
+ elevation: 0,
+ backgroundColor: Colors.white,
+ child: const Icon(Icons.add, color: Colors.black87),
+ ),
+ );
+
+ items.insert(2, actionButton);
+ }
return BottomAppBar(
- shape: widget.notchedShape,
- color: widget.backgroundColor,
+ color: backgroundColor,
child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: items,
- ),
- );
- }
-
- Widget _buildMiddleTabItem() {
- return Expanded(
- child: SizedBox(
- height: context
- .proportionateScreenHeight(widget.notproportionateHeight),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- SizedBox(height: widget.iconSize),
- Text(
- widget.centerItemText,
- style: TextStyle(color: widget.color),
- ),
- ],
- ),
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: items,
),
);
}
- Widget _buildTabItem({
+ Widget _buildTabItem(BuildContext context, {
required BottomAppBarItem item,
required int index,
}) {
- final Color color = item.selected ? widget.selectedColor : widget.color;
+ final Color color = item.selected ? selectedColor : iconColor;
return Expanded(
child: Container(
height: context
- .proportionateScreenHeight(widget.notproportionateHeight),
- constraints: BoxConstraints(minHeight: widget.minHeight),
+ .proportionateScreenHeight(notproportionateHeight),
+ constraints: BoxConstraints(minHeight: minHeight),
child: Material(
type: MaterialType.transparency,
child: InkWell(
@@ -102,7 +95,7 @@ class FABBottomAppBarState extends State {
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
- Icon(item.iconData, color: color, size: widget.iconSize),
+ Icon(item.iconData, color: color, size: iconSize),
Text(
item.text,
style: TextStyle(
diff --git a/lib/core/design/layout/widgets/lip_section.dart b/lib/core/design/layout/widgets/lip_section.dart
index 128d9cc3..bc3ec7dc 100644
--- a/lib/core/design/layout/widgets/lip_section.dart
+++ b/lib/core/design/layout/widgets/lip_section.dart
@@ -8,9 +8,6 @@ import 'package:flutter/material.dart';
import 'package:linum/core/design/layout/enums/screen_fraction_enum.dart';
import 'package:linum/core/design/layout/utils/layout_helpers.dart';
-
-
-
class LipSection extends StatelessWidget {
final String lipTitle;
final bool isInverted;
@@ -49,7 +46,7 @@ class LipSection extends StatelessWidget {
style: Theme.of(context).textTheme.titleLarge,
/// Headlines are considered decorative elements and should therefore not be affected by system accessibility modifications - fixes #47
- textScaleFactor: 1.0,
+ textScaler: TextScaler.noScaling,
),
),
),
@@ -64,7 +61,7 @@ class LipSection extends StatelessWidget {
elevation: 0,
automaticallyImplyLeading: false,
backgroundColor: Colors.transparent,
- leading: leadingAction!(context),
+ leading: leadingAction?.call(context),
actions: _actionHelper(actions, context),
),
),
@@ -84,13 +81,12 @@ class LipSection extends StatelessWidget {
color: Theme.of(context).colorScheme.primary,
child: Baseline(
baselineType: TextBaseline.alphabetic,
- baseline:
- context.proportionateScreenHeight(164) - 12,
+ baseline: context.proportionateScreenHeight(164) - 12,
child: Text(
lipTitle,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
- textScaleFactor: 1.0,
+ textScaler: TextScaler.noScaling,
),
),
),
@@ -106,7 +102,7 @@ class LipSection extends StatelessWidget {
elevation: 0,
automaticallyImplyLeading: false,
backgroundColor: Colors.transparent,
- leading: leadingAction!(context),
+ leading: leadingAction?.call(context),
actions: _actionHelper(actions, context),
),
),
diff --git a/lib/core/design/theme/color_scheme.dart b/lib/core/design/theme/color_scheme.dart
new file mode 100644
index 00000000..d2e98078
--- /dev/null
+++ b/lib/core/design/theme/color_scheme.dart
@@ -0,0 +1,24 @@
+import 'package:flutter/material.dart';
+
+class LinumColorScheme {
+ static ColorScheme light() {
+ return const ColorScheme(
+ brightness: Brightness.light,
+ primary: Color(0xFF97BC4E),
+ primaryContainer: Colors.green,
+ secondary: Color(0xFF505050),
+ secondaryContainer: Colors.white,
+ tertiary: Color(0xFFC1E695),
+ tertiaryContainer: Color(0xFF808080),
+ surface: Color(0xFFFAFAFA),
+ // background: const Color(0xFFe6e0e9),
+ error: Color(0xFFEB5757),
+ errorContainer: Color.fromARGB(255, 250, 171, 171),
+ onPrimary: Color(0xFFFAFAFA),
+ onSecondary: Color(0xFFFAFAFA),
+ onSurface: Color(0xFF505050),
+ // onBackground: const Color(0xFF79747E),
+ onError: Colors.teal,
+ );
+ }
+}
diff --git a/lib/core/design/theme/constants/main_theme_data.dart b/lib/core/design/theme/constants/main_theme_data.dart
deleted file mode 100644
index d8bb61fe..00000000
--- a/lib/core/design/theme/constants/main_theme_data.dart
+++ /dev/null
@@ -1,77 +0,0 @@
-// Main Theme Data - Contains the entire MaterialTheme used in main.dart
-//
-// Author: NightmindOfficial
-// Co-Author: damattl
-// (Refactored)
-
-import 'package:flutter/material.dart';
-import 'package:linum/core/design/theme/constants/main_text_theme.dart';
-
-class MainThemeData {
- static final ThemeData lightTheme = ThemeData(
- brightness: Brightness.light,
-
- //use like this: Theme.of(context).colorScheme.NAME_OF_COLOR_STYLE
- colorScheme: const ColorScheme(
- primary: Color(0xFF97BC4E),
- primaryContainer: Colors.green,
- secondary: Color(0xFF505050),
- secondaryContainer: Colors.white,
- tertiary: Color(0xFFC1E695),
- tertiaryContainer: Color(0xFF808080),
- surface: Color(0xFFC1E695),
- background: Color(0xFFFAFAFA),
- error: Color(0xFFEB5757),
- errorContainer: Color.fromARGB(255, 250, 171, 171),
- onPrimary: Color(0xFFFAFAFA),
- onSecondary: Color(0xFFFAFAFA),
- onSurface: Color(0xFF505050),
- onBackground: Colors.black12,
- onError: Colors.teal,
- brightness: Brightness.light,
- ),
-
- // This is the generic textTheme where we store most basic applications
- // of different text styles. The names should be self-explaining.
- // use like this: Theme.of(context).textTheme.THEME_TYPE
-
- textSelectionTheme: const TextSelectionThemeData(
- selectionHandleColor: Colors.transparent,
- ),
-
- textTheme: MainTextTheme.lightTheme,
- );
-
- static final ThemeData darkTheme = ThemeData(
- brightness: Brightness.dark,
-
- colorScheme: const ColorScheme(
- primary: Color(0xFF97BC4E),
- primaryContainer: Colors.green,
- secondary: Color(0xFF505050),
- secondaryContainer: Colors.white,
- tertiary: Color(0xFFC1E695),
- tertiaryContainer: Color(0xFF808080),
- surface: Color(0xFFC1E695),
- background: Color(0xFFFAFAFA),
- error: Color(0xFFEB5757),
- errorContainer: Color.fromARGB(255, 250, 171, 171),
- onPrimary: Color(0xFFFAFAFA),
- onSecondary: Color(0xFFFAFAFA),
- onSurface: Color(0xFF505050),
- onBackground: Colors.black12,
- onError: Colors.teal,
- brightness: Brightness.light,
- ),
-
- // This is the generic textTheme where we store most basic applications
- // of different text styles. The names should be self-explaining.
- //use like this: Theme.of(context).textTheme.THEME_TYPE
-
- textSelectionTheme: const TextSelectionThemeData(
- selectionHandleColor: Colors.transparent,
- ),
-
- textTheme: MainTextTheme.lightTheme,
- );
-}
diff --git a/lib/core/design/theme/filled_button_theme.dart b/lib/core/design/theme/filled_button_theme.dart
new file mode 100644
index 00000000..3787250b
--- /dev/null
+++ b/lib/core/design/theme/filled_button_theme.dart
@@ -0,0 +1,36 @@
+import 'package:flutter/material.dart';
+import 'package:linum/core/design/theme/color_scheme.dart';
+import 'package:linum/core/design/theme/text_theme.dart';
+
+class LinumFilledButtonTheme {
+
+ static TextStyle _getTextStyle(Set states) {
+ final baseStyle = LinumTextTheme.lightTheme.labelMedium ?? const TextStyle();
+
+ if (states.contains(WidgetState.disabled)) {
+ return baseStyle.copyWith(
+ color: LinumColorScheme.light().onSurface.withOpacity(0.38),
+ );
+ }
+ return baseStyle.copyWith(
+ color: Colors.white,
+ );
+ }
+
+ static Color _getBackgroundColor(Set states) {
+ if (states.contains(WidgetState.disabled)) {
+ return LinumColorScheme.light().onSurface.withOpacity(0.12);
+ }
+
+ return LinumColorScheme.light().primary;
+ }
+
+ static FilledButtonThemeData light() {
+ return FilledButtonThemeData(
+ style: ButtonStyle(
+ textStyle: WidgetStateProperty.resolveWith(_getTextStyle),
+ backgroundColor: WidgetStateProperty.resolveWith(_getBackgroundColor),
+ ),
+ );
+ }
+}
diff --git a/lib/core/design/theme/constants/ring_colors.dart b/lib/core/design/theme/ring_colors.dart
similarity index 100%
rename from lib/core/design/theme/constants/ring_colors.dart
rename to lib/core/design/theme/ring_colors.dart
diff --git a/lib/core/design/theme/constants/main_text_theme.dart b/lib/core/design/theme/text_theme.dart
similarity index 99%
rename from lib/core/design/theme/constants/main_text_theme.dart
rename to lib/core/design/theme/text_theme.dart
index 9744d667..0f723e9b 100644
--- a/lib/core/design/theme/constants/main_text_theme.dart
+++ b/lib/core/design/theme/text_theme.dart
@@ -10,7 +10,7 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
-class MainTextTheme {
+class LinumTextTheme {
static final TextTheme lightTheme = TextTheme(
displayLarge: GoogleFonts.dmSans(
fontSize: 39.81,
diff --git a/lib/core/design/theme/theme_data.dart b/lib/core/design/theme/theme_data.dart
new file mode 100644
index 00000000..29cd9253
--- /dev/null
+++ b/lib/core/design/theme/theme_data.dart
@@ -0,0 +1,79 @@
+// Main Theme Data - Contains the entire MaterialTheme used in main.dart
+//
+// Author: NightmindOfficial
+// Co-Author: damattl
+// (Refactored)
+
+import 'package:flutter/material.dart';
+import 'package:linum/core/design/theme/color_scheme.dart';
+import 'package:linum/core/design/theme/filled_button_theme.dart';
+import 'package:linum/core/design/theme/text_theme.dart';
+
+
+
+class LinumTheme {
+
+ static ThemeData light() {
+ return ThemeData(
+ useMaterial3: true,
+ brightness: Brightness.light,
+
+ //use like this: Theme.of(context).colorScheme.NAME_OF_COLOR_STYLE
+ colorScheme: LinumColorScheme.light(),
+
+ // This is the generic textTheme where we store most basic applications
+ // of different text styles. The names should be self-explaining.
+ // use like this: Theme.of(context).textTheme.THEME_TYPE
+
+ textSelectionTheme: const TextSelectionThemeData(
+ selectionHandleColor: Colors.transparent,
+ ),
+
+ textTheme: LinumTextTheme.lightTheme,
+
+ bottomSheetTheme: const BottomSheetThemeData(
+ backgroundColor: Colors.white,
+ ),
+
+
+ filledButtonTheme: LinumFilledButtonTheme.light(),
+
+ );
+ }
+
+ static ThemeData dark() {
+ return ThemeData(
+ useMaterial3: false,
+ brightness: Brightness.dark,
+
+ colorScheme: const ColorScheme(
+ primary: Color(0xFF97BC4E),
+ primaryContainer: Colors.green,
+ secondary: Color(0xFF505050),
+ secondaryContainer: Colors.white,
+ tertiary: Color(0xFFC1E695),
+ tertiaryContainer: Color(0xFF808080),
+ surface: Color(0xFFe6e0e9),
+ // background: Color(0xFFFAFAFA),
+ error: Color(0xFFEB5757),
+ errorContainer: Color.fromARGB(255, 250, 171, 171),
+ onPrimary: Color(0xFFFAFAFA),
+ onSecondary: Color(0xFFFAFAFA),
+ onSurface: Color(0xFF505050),
+ // onBackground: Color(0xFF79747E),
+ onError: Colors.teal,
+ brightness: Brightness.light,
+ ),
+
+ // This is the generic textTheme where we store most basic applications
+ // of different text styles. The names should be self-explaining.
+ //use like this: Theme.of(context).textTheme.THEME_TYPE
+
+ textSelectionTheme: const TextSelectionThemeData(
+ selectionHandleColor: Colors.transparent,
+ ),
+
+ textTheme: LinumTextTheme.lightTheme,
+ );
+ }
+}
diff --git a/lib/core/events/event_types.dart b/lib/core/events/event_types.dart
index 495bfe8c..71fef9e8 100644
--- a/lib/core/events/event_types.dart
+++ b/lib/core/events/event_types.dart
@@ -1,4 +1,6 @@
enum EventType {
+ any,
start,
languageChange,
+ userChange,
}
diff --git a/lib/core/events/models/user_change_event.dart b/lib/core/events/models/user_change_event.dart
new file mode 100644
index 00000000..e5628e53
--- /dev/null
+++ b/lib/core/events/models/user_change_event.dart
@@ -0,0 +1,6 @@
+import 'package:linum/core/events/event_interface.dart';
+import 'package:linum/core/events/event_types.dart';
+
+class UserChangeEvent extends IEvent {
+ UserChangeEvent({required super.message, required super.sender}) : super(type: EventType.userChange);
+}
diff --git a/lib/core/localization/settings/data/language_settings.dart b/lib/core/localization/settings/data/language_settings.dart
index 49633e43..036c938d 100644
--- a/lib/core/localization/settings/data/language_settings.dart
+++ b/lib/core/localization/settings/data/language_settings.dart
@@ -1,24 +1,24 @@
class LanguageSettings {
final bool? useSystemLanguage;
- final String? languageCode;
+ final String? languageTag;
LanguageSettings({
required this.useSystemLanguage,
- required this.languageCode,
+ required this.languageTag,
});
LanguageSettings copyWith({
bool? useSystemLanguage,
- String? languageCode,
+ String? languageTag,
}) {
return LanguageSettings(
useSystemLanguage: useSystemLanguage ?? this.useSystemLanguage,
- languageCode: languageCode ?? this.languageCode,
+ languageTag: languageTag ?? this.languageTag,
);
}
@override
String toString() {
- return 'LanguageSettings(useSystemLanguage: $useSystemLanguage, languageCode: $languageCode})';
+ return 'LanguageSettings(useSystemLanguage: $useSystemLanguage, languageCode: $languageTag})';
}
}
diff --git a/lib/core/localization/settings/data/language_settings_mapper.dart b/lib/core/localization/settings/data/language_settings_mapper.dart
index 3c3732e8..bb424ecf 100644
--- a/lib/core/localization/settings/data/language_settings_mapper.dart
+++ b/lib/core/localization/settings/data/language_settings_mapper.dart
@@ -6,7 +6,7 @@ class LanguageSettingsMapper implements ISettingsMapper {
Map toMap(LanguageSettings model) {
final Map map = {};
final systemLanguage = model.useSystemLanguage;
- final languageCode = model.languageCode;
+ final languageCode = model.languageTag;
if (languageCode != null) {
map["languageCode"] = languageCode;
@@ -25,7 +25,7 @@ class LanguageSettingsMapper implements ISettingsMapper {
return LanguageSettings(
useSystemLanguage: useSystemLanguage,
- languageCode: languageCode,
+ languageTag: languageCode,
);
}
}
diff --git a/lib/core/localization/settings/data/language_settings_pref_adapter.dart b/lib/core/localization/settings/data/language_settings_pref_adapter.dart
new file mode 100644
index 00000000..ea6dbcd4
--- /dev/null
+++ b/lib/core/localization/settings/data/language_settings_pref_adapter.dart
@@ -0,0 +1,29 @@
+import 'package:linum/core/localization/settings/data/language_settings.dart';
+import 'package:linum/core/settings/data/pref_adapter.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+
+class LanguageSettingsPrefAdapter extends IPrefAdapter {
+ final SharedPreferences preferences;
+
+ LanguageSettingsPrefAdapter({required this.preferences});
+
+ @override
+ LanguageSettings load() {
+ final languageTag = preferences.getString("languageTag");
+ // TODO: Perhaps add useSystemLanguage to prefs
+ return LanguageSettings(
+ useSystemLanguage: languageTag == null,
+ languageTag: languageTag,
+ );
+ }
+
+ @override
+ void store(LanguageSettings settings) {
+ if (settings.languageTag == null) {
+ preferences.remove("languageTag");
+ return;
+ }
+ preferences.setString("languageTag", settings.languageTag!);
+ }
+
+}
diff --git a/lib/core/localization/settings/domain/language_settings_service_impl.dart b/lib/core/localization/settings/domain/language_settings_service_impl.dart
index 58d77651..bb9651c9 100644
--- a/lib/core/localization/settings/domain/language_settings_service_impl.dart
+++ b/lib/core/localization/settings/domain/language_settings_service_impl.dart
@@ -6,6 +6,7 @@ import 'package:linum/core/events/models/language_change_event.dart';
import 'package:linum/core/localization/settings/data/language_settings.dart';
import 'package:linum/core/localization/settings/presentation/language_settings_service.dart';
import 'package:linum/core/settings/domain/settings_repository.dart';
+import 'package:logger/logger.dart';
class LanguageSettingsServiceImpl extends SubscriptionHandler implements ILanguageSettingsService {
final ISettingsRepository _repository;
@@ -13,9 +14,11 @@ class LanguageSettingsServiceImpl extends SubscriptionHandler implements ILangua
LanguageSettingsServiceImpl(this._repository, this._eventService) {
+ // print("Constructor called");
super.subscribe(_repository.settingsStream, (event) {
+ // print(event);
_eventService.dispatch(LanguageChangeEvent(
- message: event.languageCode,
+ message: event.languageTag,
sender: (LanguageSettingsServiceImpl).toString(),
),);
notifyListeners();
@@ -23,43 +26,63 @@ class LanguageSettingsServiceImpl extends SubscriptionHandler implements ILangua
}
@override
- String? getLanguageCode() {
- return _repository.settings.languageCode;
+ String? getLanguageTag() {
+ final languageTag = _repository.settings.languageTag;
+ return languageTag;
}
@override
- Future setLanguageCode(String? languageCode) {
- if (languageCode == null) {
+ Future setLanguageTag(String? languageTag) async {
+ if (languageTag == null) {
return setUseSystemLanguage(true);
}
+ // TODO: Handle case of user not authorized
final update = _repository.settings.copyWith(
- languageCode: languageCode,
+ languageTag: languageTag,
useSystemLanguage: false,
);
- return _repository.update(update);
+
+ try {
+ await _repository.update(update);
+ } on Exception catch (e) {
+ Logger().e(e);
+ }
+
+ _eventService.dispatch(
+ LanguageChangeEvent(
+ message: languageTag,
+ sender: (LanguageSettingsServiceImpl).toString(),
+ ),
+ );
}
@override
bool get useSystemLanguage {
- return _repository.settings.useSystemLanguage ?? true;
+ return _repository.settings.useSystemLanguage ?? false;
}
@override
Future setUseSystemLanguage(bool value) async {
final update = LanguageSettings(
useSystemLanguage: value,
- languageCode: null,
+ languageTag: null,
);
await _repository.update(update);
}
@override
- bool isCurrentLanguageCode(String code) {
+ bool isCurrentLanguageTag(String code) {
if (useSystemLanguage) {
return false;
}
- return code == getLanguageCode();
+
+ return code == getLanguageTag();
+ }
+
+ @override
+ Future ready() async {
+ return _repository.ready();
}
}
diff --git a/lib/core/localization/settings/presentation/language_settings_service.dart b/lib/core/localization/settings/presentation/language_settings_service.dart
index c4212a08..72e4618a 100644
--- a/lib/core/localization/settings/presentation/language_settings_service.dart
+++ b/lib/core/localization/settings/presentation/language_settings_service.dart
@@ -1,10 +1,10 @@
-import 'package:flutter/cupertino.dart';
+import 'package:linum/common/interfaces/service_interface.dart';
-abstract class ILanguageSettingsService with ChangeNotifier {
- String? getLanguageCode();
- Future setLanguageCode(String? languageCode);
+abstract class ILanguageSettingsService extends IProvidableService with NotifyReady {
+ String? getLanguageTag();
+ Future setLanguageTag(String? languageCode);
Future setUseSystemLanguage(bool value);
bool get useSystemLanguage;
- bool isCurrentLanguageCode(String code);
+ bool isCurrentLanguageTag(String code);
}
diff --git a/lib/core/localization/settings/presentation/widgets/language_selector.dart b/lib/core/localization/settings/presentation/widgets/language_selector.dart
index e457f9ed..a16b77f0 100644
--- a/lib/core/localization/settings/presentation/widgets/language_selector.dart
+++ b/lib/core/localization/settings/presentation/widgets/language_selector.dart
@@ -30,7 +30,6 @@ class LanguageSelector extends StatelessWidget {
style: Theme.of(context).textTheme.bodyLarge,
).tr(),
value: languageSettingsService.useSystemLanguage,
- activeColor: Theme.of(context).colorScheme.primary,
onChanged: (bool value) {
languageSettingsService.setUseSystemLanguage(value);
},
@@ -43,11 +42,11 @@ class LanguageSelector extends StatelessWidget {
children: [
ToggleButtons(
isSelected: [
- languageSettingsService.isCurrentLanguageCode('de-DE'),
- languageSettingsService.isCurrentLanguageCode('en-US'),
- languageSettingsService.isCurrentLanguageCode('nl-NL'),
- languageSettingsService.isCurrentLanguageCode('es-ES'),
- languageSettingsService.isCurrentLanguageCode('fr-FR'),
+ languageSettingsService.isCurrentLanguageTag('de-DE'),
+ languageSettingsService.isCurrentLanguageTag('en-US'),
+ languageSettingsService.isCurrentLanguageTag('nl-NL'),
+ languageSettingsService.isCurrentLanguageTag('es-ES'),
+ languageSettingsService.isCurrentLanguageTag('fr-FR'),
],
onPressed: _selectLanguage(languageSettingsService),
borderRadius: BorderRadius.circular(32),
@@ -88,7 +87,7 @@ class LanguageSelector extends StatelessWidget {
default:
langSelector = 'en-US';
}
- languageSettingsService.setLanguageCode(langSelector);
+ languageSettingsService.setLanguageTag(langSelector);
};
}
}
diff --git a/lib/core/localization/settings/utils/locale_utils.dart b/lib/core/localization/settings/utils/locale_utils.dart
index 10dd8081..5fd7bb55 100644
--- a/lib/core/localization/settings/utils/locale_utils.dart
+++ b/lib/core/localization/settings/utils/locale_utils.dart
@@ -21,7 +21,7 @@ extension LocaleExtensions on BuildContext {
setLocale(fallbackLocale!);
}
} catch (e) {
- Logger().v("known life cycle error ");
+ Logger().t("known life cycle error ");
}
}
}
diff --git a/lib/core/navigation/main_router_delegate.dart b/lib/core/navigation/main_router_delegate.dart
index eaed0269..a123abde 100644
--- a/lib/core/navigation/main_router_delegate.dart
+++ b/lib/core/navigation/main_router_delegate.dart
@@ -3,24 +3,38 @@
// Author: damattl
//
+import 'dart:async';
+
import 'package:flutter/material.dart';
+import 'package:linum/common/interfaces/service_interface.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
+import 'package:linum/core/categories/settings/presentation/category_settings_service.dart';
import 'package:linum/core/design/layout/loading_scaffold.dart';
+import 'package:linum/core/localization/settings/presentation/language_settings_service.dart';
import 'package:linum/core/navigation/main_routes.dart';
import 'package:linum/core/navigation/main_routes_extensions.dart';
import 'package:linum/core/navigation/main_transition_delegate.dart';
+import 'package:linum/features/currencies/settings/presentation/currency_settings_service.dart';
import 'package:linum/screens/lock_screen/services/pin_code_service.dart';
import 'package:linum/screens/onboarding_screen/onboarding_screen.dart';
import 'package:logger/logger.dart';
import 'package:provider/provider.dart';
+Future notifyReady(List service) {
+ return Future.wait(service.map((service) => service.ready()));
+}
+
class MainRouterDelegate extends RouterDelegate
with ChangeNotifier, PopNavigatorRouterDelegateMixin {
@override
late final GlobalKey navigatorKey;
late final Logger logger;
+ bool _servicesReady = false;
+ final MainRoute defaultRoute;
+
+ String? lastUid;
- MainRouterDelegate() {
+ MainRouterDelegate({required this.defaultRoute}) {
navigatorKey = GlobalKey();
logger = Logger();
}
@@ -86,18 +100,19 @@ class MainRouterDelegate extends RouterDelegate
_pageStack.add(mainRoutes.pageFromRoute(MainRoute.sandbox));
}
if (_pageStack.isEmpty) {
- _pageStack.add(mainRoutes.pageFromRoute(MainRoute.home));
+ _pageStack.add(mainRoutes.pageFromRoute(defaultRoute));
}
if (pinCodeService.pinSet && !pinCodeService.sessionIsSafe) {
return _buildPinCodeStack(pinCodeService);
}
+ // TODO: Wait for services to finish
+
+
return List.of(_pageStack);
}
- List _buildPageStack(BuildContext context) {
- final AuthenticationService auth =
- context.watch();
+ List _buildPageStack(BuildContext context, AuthenticationService auth) {
if (auth.isLoggedIn) {
return _buildPageStackAuthorized(context);
} else {
@@ -105,19 +120,57 @@ class MainRouterDelegate extends RouterDelegate
}
}
- Navigator _buildNavigator(BuildContext context) {
+ Widget _buildNavigator(BuildContext context) {
+
final transitionDelegate = MainTransitionDelegate();
+
return Navigator(
key: navigatorKey,
// Add TransitionDelegate here
- pages: _buildPageStack(context),
+ pages: _buildPageStack(context, context.read()),
transitionDelegate: transitionDelegate,
onPopPage: _onPopPage,
);
+
+ }
+
+ Widget _awaitServicesReady(BuildContext context, Widget Function(BuildContext context) callback) {
+ if (_servicesReady) {
+ return callback(context);
+ }
+
+ return FutureBuilder(
+ future: notifyReady([
+ context.read(),
+ context.read(),
+ context.read(),
+ ]),
+ builder: (context, snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ _servicesReady = true;
+ return callback(context);
+ }
+ return const LoadingScaffold();
+ },
+ );
}
@override
Widget build(BuildContext context) {
+ final AuthenticationService auth = context.watch();
+
+ if (lastUid == null && auth.currentUser?.uid != null) {
+ _pageStack.clear();
+ }
+ if (lastUid != auth.currentUser?.uid) {
+ _servicesReady = false;
+ }
+ lastUid = auth.currentUser?.uid;
+
+ if (_showLoadingScreen) {
+ return const LoadingScaffold();
+ }
+
final pinCodeProvider =
context.read();
if (pinCodeProvider.pinSetStillLoading) {
@@ -125,13 +178,13 @@ class MainRouterDelegate extends RouterDelegate
future: pinCodeProvider.initializeIsPINSet(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
- return _buildNavigator(context);
+ return _awaitServicesReady(context, _buildNavigator);
}
return const LoadingScaffold();
},
);
} else {
- return _buildNavigator(context);
+ return _awaitServicesReady(context, _buildNavigator);
}
}
@@ -179,6 +232,16 @@ class MainRouterDelegate extends RouterDelegate
return Future.value(true);
}
+ bool _showLoadingScreen = false;
+
+ Future showLoadingScreen({Duration duration = const Duration(seconds: 2)}) async {
+ _showLoadingScreen = true;
+ notifyListeners();
+ await Future.delayed(duration);
+ _showLoadingScreen = false;
+ notifyListeners();
+ }
+
/// Push a route to the MainRouter's Stack.
/// Notifies all listening widgets.
void pushRoute(MainRoute route, {T? settings}) {
@@ -203,6 +266,10 @@ class MainRouterDelegate extends RouterDelegate
notifyListeners();
}
+ void resetServicesLoadingState() {
+ _servicesReady = false;
+ }
+
@override
Future setNewRoutePath(MainRoute route) async {}
}
diff --git a/lib/core/settings/data/pref_adapter.dart b/lib/core/settings/data/pref_adapter.dart
new file mode 100644
index 00000000..f2b7a679
--- /dev/null
+++ b/lib/core/settings/data/pref_adapter.dart
@@ -0,0 +1,4 @@
+abstract class IPrefAdapter {
+ T load();
+ void store(T settings);
+}
diff --git a/lib/core/settings/data/settings_repository_impl.dart b/lib/core/settings/data/settings_repository_impl.dart
index c7c3a8a1..f899346b 100644
--- a/lib/core/settings/data/settings_repository_impl.dart
+++ b/lib/core/settings/data/settings_repository_impl.dart
@@ -1,25 +1,46 @@
+import 'dart:async';
+
+import 'package:linum/core/settings/data/pref_adapter.dart';
import 'package:linum/core/settings/data/settings_mapper_interface.dart';
import 'package:linum/core/settings/domain/settings_repository.dart';
import 'package:linum/core/settings/domain/settings_storage.dart';
+import 'package:logger/logger.dart';
import 'package:rxdart/rxdart.dart';
class SettingsRepositoryImpl extends ISettingsRepository {
final ISettingsStorage _adapter;
final ISettingsMapper _mapper;
- final String? userId;
-
+ final IPrefAdapter? _prefAdapter;
+ final Completer _isReady = Completer();
final BehaviorSubject _settings = BehaviorSubject();
SettingsRepositoryImpl({
- this.userId,
required ISettingsStorage adapter,
required ISettingsMapper mapper,
- }): _adapter = adapter, _mapper = mapper {
- _settings.add(mapper.toModel({}));
+ IPrefAdapter? prefAdapter,
+ }): _adapter = adapter, _mapper = mapper, _prefAdapter = prefAdapter {
+ _init();
+ }
+
+
+ Future _init() async {
+ var defaultVal = _prefAdapter?.load();
+
+ // TODO: This might cause stream state errors, but for now it seems to work
+ Map? settings;
+ try {
+ settings = await _adapter.getDataForUser();
+ } on Exception catch (e) {
+ Logger().w(e);
+ }
+ if (settings != null) {
+ defaultVal = _mapper.toModel(settings);
+ }
+ _settings.add(defaultVal ?? _mapper.toModel({}));
var previous = _settings.value;
- final modifiedStream = adapter.getDataStreamForUser(userId).map((event) {
- return mapper.toModel(event);
+ final modifiedStream = _adapter.getDataStreamForUser().map((event) {
+ return _mapper.toModel(event);
}).where((element) {
final equals = element.toString() != previous.toString();
previous = element;
@@ -27,8 +48,11 @@ class SettingsRepositoryImpl extends ISettingsRepository {
});
_settings.addStream(modifiedStream);
+ _isReady.complete(true);
}
+
+
@override
Stream get settingsStream => _settings.stream;
@override
@@ -36,7 +60,13 @@ class SettingsRepositoryImpl extends ISettingsRepository {
@override
Future update(TSettings settings) async {
+ _prefAdapter?.store(settings);
final map = _mapper.toMap(settings);
- await _adapter.updateUserData(userId, map);
+ await _adapter.updateUserData(map);
+ }
+
+ @override
+ Future ready() {
+ return _isReady.future;
}
}
diff --git a/lib/core/settings/data/settings_storage_impl.dart b/lib/core/settings/data/settings_storage_impl.dart
index 0ea3a2d9..785a65b7 100644
--- a/lib/core/settings/data/settings_storage_impl.dart
+++ b/lib/core/settings/data/settings_storage_impl.dart
@@ -1,42 +1,64 @@
+import 'dart:async';
+
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:linum/core/settings/data/settings_data.dart';
import 'package:linum/core/settings/domain/settings_storage.dart';
+import 'package:logger/logger.dart';
+import 'package:rxdart/rxdart.dart';
class SettingsStorageImpl implements ISettingsStorage {
final FirebaseFirestore _firestore;
- SettingsStorageImpl(this._firestore);
-
- DocumentReference _getDocRef(String? userId)
- => _firestore.collection("account_settings").doc(userId);
-
- Future> _getSnapshot(String? userId) async {
- final snapshot = await _getSnapshot(userId);
- /* if (!snapshot.exists) {
- await snapshot.reference.set({});
- return _getSnapshot(userId);
- } */
- // TODO: Check if even necessary
- return snapshot;
+ final String? _userId;
+ final StreamController _controller = BehaviorSubject();
+ StreamSubscription? _streamSubscription;
+
+ SettingsStorageImpl(this._firestore, String? userId): _userId = userId {
+ _controller.onListen = () {
+ final docRef = _getDocRef();
+
+ // print("Subscribing now");
+
+ _streamSubscription = docRef.snapshots().map((event) => event.data() ?? {}).listen((event) {
+ // print("Snapshots: $event");
+ _controller.add(event);
+ });
+
+ };
+
+ _controller.onCancel = () {
+ _streamSubscription?.cancel();
+ };
}
+ DocumentReference _getDocRef()
+ => _firestore.collection("account_settings").doc(_userId);
+
@override
- Future getDataForUser(String? userId) async {
- final snapshot = await _getSnapshot(userId);
+ Future getDataForUser() async {
+ final snapshot = await _getDocRef().snapshots().first;
final map = snapshot.data();
- return map ?? {};
+ return map;
}
@override
- Stream getDataStreamForUser(String? userId) async* {
- final docRef = _getDocRef(userId);
- yield* docRef.snapshots().map((event) {
- return event.data() ?? {};
- });
+ Stream getDataStreamForUser() {
+ return _controller.stream.asBroadcastStream();
}
@override
- Future updateUserData(String? userId, SettingsData data) async {
- final docRef = _getDocRef(userId);
- await docRef.set(data, SetOptions(merge: true));
+ Future updateUserData(SettingsData data) async {
+ try {
+ final docRef = _getDocRef();
+ await docRef.set(data, SetOptions(merge: true));
+ } on Exception catch(e) {
+ Logger().e(e);
+ _controller.sink.add(data);
+ }
+ }
+
+ void dispose() {
+ // print("Disposed of SettingsStorage");
+ _streamSubscription?.cancel();
+ _controller.close();
}
}
diff --git a/lib/core/settings/domain/settings_repository.dart b/lib/core/settings/domain/settings_repository.dart
index 9a22f0ac..216c0128 100644
--- a/lib/core/settings/domain/settings_repository.dart
+++ b/lib/core/settings/domain/settings_repository.dart
@@ -1,6 +1,7 @@
-abstract class ISettingsRepository {
+import 'package:linum/common/interfaces/service_interface.dart';
+
+abstract class ISettingsRepository with NotifyReady {
Stream get settingsStream;
TSettings get settings;
-
Future update(TSettings settings);
}
diff --git a/lib/core/settings/domain/settings_storage.dart b/lib/core/settings/domain/settings_storage.dart
index 6c271946..e1da7ad8 100644
--- a/lib/core/settings/domain/settings_storage.dart
+++ b/lib/core/settings/domain/settings_storage.dart
@@ -2,8 +2,8 @@
import 'package:linum/core/settings/data/settings_data.dart';
abstract class ISettingsStorage {
- Future getDataForUser(String? userId);
- Stream getDataStreamForUser(String? userId);
+ Future getDataForUser();
+ Stream getDataStreamForUser();
- Future updateUserData(String? userId, SettingsData data);
+ Future updateUserData(SettingsData data);
}
diff --git a/lib/features/currencies/core/presentation/exchange_rate_service.dart b/lib/features/currencies/core/presentation/exchange_rate_service.dart
index ea9871ee..da316e7f 100644
--- a/lib/features/currencies/core/presentation/exchange_rate_service.dart
+++ b/lib/features/currencies/core/presentation/exchange_rate_service.dart
@@ -1,8 +1,8 @@
-import 'package:flutter/cupertino.dart';
+import 'package:linum/common/interfaces/service_interface.dart';
import 'package:linum/core/balance/models/transaction.dart';
import 'package:linum/features/currencies/core/data/models/currency.dart';
-abstract class IExchangeRateService with ChangeNotifier {
+abstract class IExchangeRateService extends IProvidableService {
Currency get standardCurrency;
Future addExchangeRatesToTransactions(List transactions);
}
diff --git a/lib/features/currencies/settings/domain/currency_settings_service_impl.dart b/lib/features/currencies/settings/domain/currency_settings_service_impl.dart
index 7b7627e0..cf35f8a0 100644
--- a/lib/features/currencies/settings/domain/currency_settings_service_impl.dart
+++ b/lib/features/currencies/settings/domain/currency_settings_service_impl.dart
@@ -24,4 +24,9 @@ class CurrencySettingsServiceImpl extends SubscriptionHandler implements ICurren
final update = _repository.settings.copyWith(currency: currency);
await _repository.update(update);
}
+
+ @override
+ Future ready() async {
+ return _repository.ready();
+ }
}
diff --git a/lib/features/currencies/settings/presentation/currency_settings_service.dart b/lib/features/currencies/settings/presentation/currency_settings_service.dart
index acb7f42d..0f9d8ad8 100644
--- a/lib/features/currencies/settings/presentation/currency_settings_service.dart
+++ b/lib/features/currencies/settings/presentation/currency_settings_service.dart
@@ -1,7 +1,7 @@
-import 'package:flutter/material.dart';
+import 'package:linum/common/interfaces/service_interface.dart';
import 'package:linum/features/currencies/core/data/models/currency.dart';
-abstract class ICurrencySettingsService with ChangeNotifier {
+abstract class ICurrencySettingsService extends IProvidableService with NotifyReady {
Currency getStandardCurrency();
Future setStandardCurrency(Currency currency);
}
diff --git a/lib/features/currencies/settings/presentation/widgets/curreny_list_view.dart b/lib/features/currencies/settings/presentation/widgets/curreny_list_view.dart
index 39913e23..1a7783d4 100644
--- a/lib/features/currencies/settings/presentation/widgets/curreny_list_view.dart
+++ b/lib/features/currencies/settings/presentation/widgets/curreny_list_view.dart
@@ -1,9 +1,6 @@
import 'package:flutter/material.dart';
-import 'package:linum/common/components/action_lip/viewmodels/action_lip_viewmodel.dart';
import 'package:linum/core/design/layout/enums/screen_fraction_enum.dart';
-import 'package:linum/core/design/layout/enums/screen_key.dart';
import 'package:linum/core/design/layout/utils/layout_helpers.dart';
-import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
import 'package:linum/features/currencies/core/constants/standard_currencies.dart';
import 'package:linum/features/currencies/core/presentation/widgets/currency_list_tile.dart';
import 'package:linum/features/currencies/settings/presentation/currency_settings_service.dart';
@@ -34,11 +31,7 @@ class CurrencyListView extends StatelessWidget {
selected: currency.name == settings.getStandardCurrency().name,
onTap: () {
settings.setStandardCurrency(currencies[index]);
- context.read().setActionLipStatus(
- context: context,
- screenKey: ScreenKey.settings,
- status: ActionLipVisibility.hidden,
- );
+ Navigator.pop(context);
},
);
},
diff --git a/lib/features/currencies/settings/presentation/widgets/standard_currency_selector.dart b/lib/features/currencies/settings/presentation/widgets/standard_currency_selector.dart
index 11d9ddc6..e5a7a199 100644
--- a/lib/features/currencies/settings/presentation/widgets/standard_currency_selector.dart
+++ b/lib/features/currencies/settings/presentation/widgets/standard_currency_selector.dart
@@ -1,8 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
-import 'package:linum/common/components/action_lip/viewmodels/action_lip_viewmodel.dart';
-import 'package:linum/core/design/layout/enums/screen_key.dart';
-import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
+import 'package:linum/common/components/sheets/linum_bottom_sheet.dart';
import 'package:linum/features/currencies/core/data/models/currency.dart';
import 'package:linum/features/currencies/core/presentation/widgets/currency_list_tile.dart';
import 'package:linum/features/currencies/settings/presentation/currency_settings_service.dart';
@@ -25,12 +23,15 @@ class _StandardCurrencySelectorState extends State {
children: [
GestureDetector(
onTap: () {
- context.read().setActionLip(
+ showModalBottomSheet(
+ useSafeArea: true,
context: context,
- screenKey: ScreenKey.settings,
- actionLipStatus: ActionLipVisibility.onviewport,
- actionLipTitle: tr(translationKeys.actionLip.standardCurrency.labelTitle),
- actionLipBody: CurrencyListView(),
+ builder: (BuildContext context) {
+ return LinumBottomSheet(
+ title: tr(translationKeys.actionLip.standardCurrency.labelTitle),
+ body: CurrencyListView(),
+ );
+ },
);
},
child: Selector(
diff --git a/lib/generated/translation_keys.g.dart b/lib/generated/translation_keys.g.dart
index d7a1d398..e3e7d53b 100644
--- a/lib/generated/translation_keys.g.dart
+++ b/lib/generated/translation_keys.g.dart
@@ -130,10 +130,12 @@ const translationKeys = (
'alertdialog.delete-transaction.dialog-button-cancel', // Abbrechen
dialogButtonDelete:
'alertdialog.delete-transaction.dialog-button-delete', // Löschen
- dialogLabelDelete:
- 'alertdialog.delete-transaction.dialog-label-delete', // Möchtest du diesen Eintrag wirklich löschen?
+ dialogMessage:
+ 'alertdialog.delete-transaction.dialog-message', // Möchtest du diesen Eintrag wirklich löschen?
+ dialogMessageSerial:
+ 'alertdialog.delete-transaction.dialog-message-serial', // Möchtest du diesen Vertrag wirklich löschen? Dadurch werden alle über den Vertrag gebuchten Transaktionen gelöscht.
dialogLabelTitle:
- 'alertdialog.delete-transaction.dialog-label-title' // Bestätigen
+ 'alertdialog.delete-transaction.dialog-label-title' // Löschen bestätigen
)
),
auth: (
@@ -229,6 +231,8 @@ const translationKeys = (
'auth.user-deleted', // Der Benutzer wurde erfolgreich gelöscht!
userNotFound:
'auth.user-not-found', // Deine E-Mail oder dein Passwort sind nicht korrekt. Vielleicht hast du dich vertippt?
+ emailNotFound:
+ 'auth.email-not-found', // Wir konnten keinen Benutzer mit der von dir angegebenen E-Mail-Adresse finden. Vielleicht hast du dich vertippt?
weakPassword:
'auth.weak-password', // Das eingegebene Passwort ist zu schwach. Bitte wähle ein stärkeres Passwort.
wrongPassword:
diff --git a/lib/global_event_handler.dart b/lib/global_event_handler.dart
index fe49e3f1..38a8f99c 100644
--- a/lib/global_event_handler.dart
+++ b/lib/global_event_handler.dart
@@ -1,34 +1,70 @@
+import 'dart:async';
+
import 'package:flutter/material.dart';
import 'package:linum/core/events/event_interface.dart';
import 'package:linum/core/events/event_service.dart';
+import 'package:linum/core/events/event_types.dart';
import 'package:provider/provider.dart';
typedef AppEventListener = void Function(IEvent event, BuildContext context);
-/// Listens for all events with a global audience.
+/// Listens for all events with a global audience. // TODO: change
/// Passes the event information to the registered listeners.
-class GlobalEventListener extends StatelessWidget {
+class EventListener extends StatefulWidget {
final Widget child;
- final List listeners;
+ final Stream? eventStream;
+ final String? streamName;
+ final Map> listeners;
- const GlobalEventListener({
+ const EventListener({
super.key,
- required this.child,
+ this.eventStream,
+ this.streamName,
required this.listeners,
- });
+ required this.child,
+ }) : assert(eventStream != null || streamName != null);
+
+ @override
+ State createState() => _EventListenerState();
+}
+
+class _EventListenerState extends State {
+ StreamSubscription? subscription;
+
+
+
+ // TODO: Update
+ @override
+ void didChangeDependencies() {
+ subscription?.cancel();
+
+ Stream? eventStream = widget.eventStream;
+
+ if (widget.eventStream == null) {
+ eventStream = context.read().getEventStream(widget.streamName ?? "global");
+ }
+
+ subscription = eventStream?.listen((IEvent event) {
+ for (final AppEventListener listener in widget.listeners[EventType.any] ?? []) {
+ listener(event, context);
+ }
+ for (final AppEventListener listener in widget.listeners[event.type] ?? []) {
+ listener(event, context);
+ }
+ });
+ super.didChangeDependencies();
+ }
+
+
@override
Widget build(BuildContext context) {
- return StreamBuilder(
- stream: context.read().getEventStream("global"),
- builder: (context, snapshot) {
- if (snapshot.hasData) {
- for (final listener in listeners) {
- listener(snapshot.requireData, context);
- }
- }
- return child;
- },
- );
+ return widget.child;
+ }
+
+ @override
+ void dispose() {
+ subscription?.cancel();
+ super.dispose();
}
}
diff --git a/lib/main.dart b/lib/main.dart
index 18c53da4..9834f6bc 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -7,12 +7,16 @@
import 'package:easy_localization/easy_localization.dart' hide TextDirection;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
+import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:linum/app.dart';
import 'package:linum/core/localization/settings/constants/supported_locales.dart';
import 'package:linum/core/navigation/main_route_information_parser.dart';
import 'package:linum/core/navigation/main_router_delegate.dart';
+import 'package:linum/core/navigation/main_routes.dart';
import 'package:linum/generated/objectbox/objectbox.g.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
+import 'package:wiredash/wiredash.dart';
Future main({bool? testing}) async {
WidgetsFlutterBinding.ensureInitialized();
@@ -27,19 +31,42 @@ Future main({bool? testing}) async {
// Force Portrait Mode
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
- SharedPreferences.getInstance().then((pref) {
- runApp(
- LifecycleWatcher(store: store, testing: testing),
- );
- });
+ // Load .env
+ await dotenv.load();
+
+ final pref = await SharedPreferences.getInstance();
+
+ FlutterError.onError = (details) async {
+ await Sentry.captureException(details.exception, stackTrace: details.stack);
+ FlutterError.presentError(details);
+ };
+
+ await SentryFlutter.init(
+ (options) {
+ options.dsn = dotenv.env['SENTRY_DSN'];
+ options.tracesSampleRate = 1.0;
+ options.profilesSampleRate = 1.0;
+ options.diagnosticLevel = SentryLevel.warning;
+ },
+ );
+
+ runApp(
+ LifecycleWatcher(store: store, testing: testing, preferences: pref),
+ );
}
/// Wrapper to handle global lifecycle changes, for example the app closing.
class LifecycleWatcher extends StatefulWidget {
final Store store;
final bool? testing;
- const LifecycleWatcher({super.key, required this.store, this.testing});
+ final SharedPreferences preferences;
+ const LifecycleWatcher({
+ super.key,
+ required this.store,
+ this.testing,
+ required this.preferences,
+ });
@override
State createState() => _LifecycleWatcherState();
@@ -48,18 +75,35 @@ class LifecycleWatcher extends StatefulWidget {
class _LifecycleWatcherState extends State {
@override
Widget build(BuildContext context) {
- final MainRouterDelegate routerDelegate = MainRouterDelegate();
- final MainRouteInformationParser routeInformationParser = MainRouteInformationParser();
-
- return EasyLocalization(
- supportedLocales: supportedLocales,
- path: 'assets/lang',
- fallbackLocale: const Locale('de', 'DE'),
- child: Linum(
+ final MainRouterDelegate routerDelegate = MainRouterDelegate(
+ defaultRoute: MainRoute.home,
+ );
+
+ final MainRouteInformationParser routeInformationParser =
+ MainRouteInformationParser();
+ // print("Rebuild LifecycleWatcher");
+ return Wiredash(
+ feedbackOptions: const WiredashFeedbackOptions(
+ labels: [
+ Label(id: 'label-ztqz1iic2d', title: 'Bug'),
+ Label(id: 'label-vc1hsuuyj3', title: 'Improvement'),
+ Label(id: 'label-eobuukbzgi', title: 'Praise'),
+ ],
+ ),
+ projectId: dotenv.env[
+ 'WIREDASH_PROJECT_ID']!, //FUTURE Check if the null checks can cause issues and rewrite if necessary
+ secret: dotenv.env['WIREDASH_SECRET']!,
+ child: EasyLocalization(
+ supportedLocales: supportedLocales,
+ path: 'assets/lang',
+ fallbackLocale: const Locale('de', 'DE'),
+ child: Linum(
store: widget.store,
routerDelegate: routerDelegate,
routeInformationParser: routeInformationParser,
testing: widget.testing,
+ preferences: widget.preferences,
+ ),
),
);
}
diff --git a/lib/screens/academy_screen/academy_screen.dart b/lib/screens/academy_screen/academy_screen.dart
index 0ff181b3..2349df14 100644
--- a/lib/screens/academy_screen/academy_screen.dart
+++ b/lib/screens/academy_screen/academy_screen.dart
@@ -10,25 +10,18 @@ import 'package:linum/core/design/layout/enums/screen_fraction_enum.dart';
import 'package:linum/core/design/layout/utils/layout_helpers.dart';
import 'package:linum/core/design/layout/widgets/app_bar_action.dart';
import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
-import 'package:linum/core/navigation/get_delegate.dart';
import 'package:linum/core/navigation/url_handler.dart';
import 'package:linum/generated/translation_keys.g.dart';
-
/// Page Index: 4
class AcademyScreen extends StatelessWidget {
const AcademyScreen({super.key});
@override
Widget build(BuildContext context) {
-
return ScreenSkeleton(
- head: 'Academy',
- leadingAction: (BuildContext context) => AppBarAction.fromParameters(
- icon: Icons.arrow_back_rounded,
- ontap: () => context.getMainRouterDelegate().popRoute(),
- // TODO: Maybe use another method, animation does not look good.
- ),
+ head: 'YouTube',
+ leadingAction: AppBarAction.fromPreset(DefaultAction.back),
isInverted: true,
body: Center(
child: Column(
diff --git a/lib/screens/budget_screen/budget_screen.dart b/lib/screens/budget_screen/budget_screen.dart
index f855fbfb..2c3f1d94 100644
--- a/lib/screens/budget_screen/budget_screen.dart
+++ b/lib/screens/budget_screen/budget_screen.dart
@@ -1,4 +1,3 @@
-
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:linum/common/utils/filters.dart';
@@ -10,7 +9,6 @@ import 'package:linum/generated/translation_keys.g.dart';
import 'package:linum/screens/home_screen/widgets/home_screen_listview.dart';
import 'package:provider/provider.dart';
-
/// Budget Screen
/// Screen listing all Balances ever made without any filtering (future entries not recognized).
/// Page Index: 1
@@ -19,8 +17,7 @@ class BudgetScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final AlgorithmService algorithmService =
- context.watch();
+ final AlgorithmService algorithmService = context.watch();
if (algorithmService.state.filter != Filters.noFilter) {
algorithmService.setCurrentFilterAlgorithm(Filters.noFilter);
@@ -28,6 +25,11 @@ class BudgetScreen extends StatelessWidget {
return ScreenSkeleton(
head: 'Budget',
leadingAction: AppBarAction.fromPreset(DefaultAction.academy),
+ actions: [
+ AppBarAction.fromPreset(
+ DefaultAction.bugreport,
+ ),
+ ],
body: Column(
children: [
Row(
diff --git a/lib/screens/enter_screen/domain/suggesting/category_guesser.dart b/lib/screens/enter_screen/domain/suggesting/category_guesser.dart
index a876c8d2..50013da0 100644
--- a/lib/screens/enter_screen/domain/suggesting/category_guesser.dart
+++ b/lib/screens/enter_screen/domain/suggesting/category_guesser.dart
@@ -23,6 +23,7 @@ class CategoryGuesser implements IGuesser {
if (filter != null && !filter!(entry.value)) {
continue;
}
+
String? valueSubstring;
final translatedLabel = translator
.translate(entry.value.label)
diff --git a/lib/screens/enter_screen/domain/suggesting/get_sub_suggestions.dart b/lib/screens/enter_screen/domain/suggesting/get_sub_suggestions.dart
index e8dc8a78..d3062d5e 100644
--- a/lib/screens/enter_screen/domain/suggesting/get_sub_suggestions.dart
+++ b/lib/screens/enter_screen/domain/suggesting/get_sub_suggestions.dart
@@ -1,21 +1,27 @@
+import 'package:linum/common/types/filter_function.dart';
import 'package:linum/core/repeating/constants/standard_repeat_configs.dart';
import 'package:linum/screens/enter_screen/domain/constants/parsable_date_map.dart';
import 'package:linum/screens/enter_screen/domain/enums/input_flag.dart';
import 'package:linum/screens/enter_screen/domain/models/suggestion.dart';
+import 'package:linum/screens/enter_screen/domain/models/suggestion_filters.dart';
import 'package:linum/screens/enter_screen/domain/suggesting/suggestable_categories.dart';
-List getSubSuggestions(InputFlag flag) {
+
+List getSubSuggestions(InputFlag flag, {ParsingFilters? filters}) {
switch (flag) {
case InputFlag.category:
return getSuggestableCategories()
+ .where((e) => applyFilter(filters?.categoryFilter, e.value))
.map((e) => Suggestion(label: e.value.label))
.toList();
case InputFlag.date:
return parsableDateMap.entries
+ .where((e) => applyFilter(filters?.dateFilter, e.key))
.map((e) => Suggestion(label: e.value))
.toList();
case InputFlag.repeatInfo:
return repeatConfigurations.entries
+ .where((e) => applyFilter(filters?.repeatFilter, e.key))
.map((e) => Suggestion(label: e.value.label))
.toList();
default:
diff --git a/lib/screens/enter_screen/domain/suggesting/make_suggestions.dart b/lib/screens/enter_screen/domain/suggesting/make_suggestions.dart
index 0e09f640..e7380069 100644
--- a/lib/screens/enter_screen/domain/suggesting/make_suggestions.dart
+++ b/lib/screens/enter_screen/domain/suggesting/make_suggestions.dart
@@ -38,11 +38,7 @@ Map makeSuggestions({
final flagGuesser = FlagGuesser(getInputFlagMap(translator));
final repeatGuesser = RepeatConfigGuesser(filter: repeatFilter);
-
-
-
final parsed = TagParser(getInputFlagMap(translator)).parse(preCursorText);
-
if (parsed.flag == null) {
final suggestions = flagGuesser.guess(parsed.text);
if (suggestions.isNotEmpty) {
@@ -66,10 +62,9 @@ Map makeSuggestions({
if (repeatSuggestions.isNotEmpty) {
return repeatSuggestions;
}
+
return {};
}
-
-
switch (parsed.flag) {
case InputFlag.category:
return categoryGuesser.suggest(parsed.text);
diff --git a/lib/screens/enter_screen/enter_screen.dart b/lib/screens/enter_screen/enter_screen.dart
index 34452528..6d122e93 100644
--- a/lib/screens/enter_screen/enter_screen.dart
+++ b/lib/screens/enter_screen/enter_screen.dart
@@ -144,10 +144,10 @@ class EnterScreen extends StatelessWidget {
}) {
if (transaction != null) {
balanceDataService.updateTransaction(transaction);
- } else if (serialTransaction != null && changeMode != null) {
+ } else if (serialTransaction != null) {
balanceDataService.updateSerialTransaction(
serialTransaction: serialTransaction,
- changeMode: changeMode,
+ changeMode: SerialTransactionChangeMode.all,
oldDate: this.transaction?.formerDate ?? this.transaction?.date,
newDate: serialTransaction.startDate,
);
@@ -162,14 +162,15 @@ class EnterScreen extends StatelessWidget {
showTransactionDeleteDialog(context, () {
if (transaction != null && changeMode == null) {
balanceDataService.removeTransaction(transaction);
- } else if (serialTransaction != null && changeMode != null) {
+ } else if (serialTransaction != null) {
balanceDataService.removeSerialTransaction(
serialTransaction: serialTransaction,
- removeType: changeMode,
+ removeType: SerialTransactionChangeMode.all,
date: transaction?.date,
);
}
- });
+ Navigator.pop(context);
+ }, isSerialTransaction: true,);
},
);
}
diff --git a/lib/screens/enter_screen/presentation/constants/highlight_colors.dart b/lib/screens/enter_screen/presentation/constants/highlight_colors.dart
new file mode 100644
index 00000000..6d3974cc
--- /dev/null
+++ b/lib/screens/enter_screen/presentation/constants/highlight_colors.dart
@@ -0,0 +1,10 @@
+import 'package:flutter/material.dart';
+
+const highlightColors = {
+ "name": Colors.black54,
+ "amount": Color(0xFFC5D794),
+ "currency": Color(0xFFB9D7E1),
+ "category": Color(0xFFFBCEB0),
+ "date": Color(0xFFF8ABC8),
+ "repeatInfo": Color(0xFFDDB6F6),
+};
diff --git a/lib/screens/enter_screen/presentation/constants/hightlight_colors.dart b/lib/screens/enter_screen/presentation/constants/hightlight_colors.dart
deleted file mode 100644
index ca84970c..00000000
--- a/lib/screens/enter_screen/presentation/constants/hightlight_colors.dart
+++ /dev/null
@@ -1,10 +0,0 @@
-import 'package:flutter/material.dart';
-
-const highlightColors = {
- "name": Colors.black54,
- "amount": Colors.lightGreen,
- "currency": Colors.blue,
- "category": Colors.orange,
- "date": Colors.pink,
- "repeatInfo": Colors.purple,
-};
diff --git a/lib/screens/enter_screen/presentation/utils/form_data_updater.dart b/lib/screens/enter_screen/presentation/utils/form_data_updater.dart
index eb3df1a7..e5df9dfc 100644
--- a/lib/screens/enter_screen/presentation/utils/form_data_updater.dart
+++ b/lib/screens/enter_screen/presentation/utils/form_data_updater.dart
@@ -88,6 +88,7 @@ class FormDataUpdater {
category: updatedCategory,
date: updatedDate,
repeatConfiguration: updatedRepeatInfo,
+ notes: newData.options.notes,
),
);
@@ -96,6 +97,7 @@ class FormDataUpdater {
if (updatedData == null) {
return newData;
}
+
return updatedData!;
}
diff --git a/lib/screens/enter_screen/presentation/utils/get_default_values.dart b/lib/screens/enter_screen/presentation/utils/get_default_values.dart
index 6bd43ba1..bdc0ba2b 100644
--- a/lib/screens/enter_screen/presentation/utils/get_default_values.dart
+++ b/lib/screens/enter_screen/presentation/utils/get_default_values.dart
@@ -1,4 +1,5 @@
import 'package:flutter/cupertino.dart';
+import 'package:linum/core/balance/services/algorithm_service.dart';
import 'package:linum/core/categories/settings/presentation/category_settings_service.dart';
import 'package:linum/core/repeating/constants/standard_repeat_configs.dart';
import 'package:linum/core/repeating/enums/repeat_interval.dart';
@@ -10,11 +11,19 @@ DefaultValues getDefaultValues(BuildContext context) {
final currencySettingsService = context.read();
final categorySettingsService = context.read();
+ var date = DateTime.now().toIso8601String();
+
+ final AlgorithmService algorithmService = context.read();
+ if(algorithmService.state.shownMonth.year != DateTime.now().year
+ || algorithmService.state.shownMonth.month != DateTime.now().month){
+ date = algorithmService.state.shownMonth.toIso8601String();
+ }
+
return DefaultValues(
name: "",
amount: 0,
currency: currencySettingsService.getStandardCurrency(),
- date: DateTime.now().toIso8601String(),
+ date: date,
expenseCategory: categorySettingsService.getExpenseEntryCategory(),
incomeCategory: categorySettingsService.getIncomeEntryCategory(),
repeatConfiguration: repeatConfigurations[RepeatInterval.none]!,
diff --git a/lib/screens/enter_screen/presentation/utils/highlight_text_controller.dart b/lib/screens/enter_screen/presentation/utils/highlight_text_controller.dart
index 73ff6076..35a6b0bf 100644
--- a/lib/screens/enter_screen/presentation/utils/highlight_text_controller.dart
+++ b/lib/screens/enter_screen/presentation/utils/highlight_text_controller.dart
@@ -8,7 +8,7 @@ import 'package:linum/screens/enter_screen/domain/models/suggestion_filters.dart
import 'package:linum/screens/enter_screen/domain/parsing/input_parser.dart';
import 'package:linum/screens/enter_screen/domain/suggesting/insert_suggestion.dart';
import 'package:linum/screens/enter_screen/domain/suggesting/make_suggestions.dart';
-import 'package:linum/screens/enter_screen/presentation/constants/hightlight_colors.dart';
+import 'package:linum/screens/enter_screen/presentation/constants/highlight_colors.dart';
import 'package:linum/screens/enter_screen/presentation/utils/example_string_builder.dart';
import 'package:linum/screens/enter_screen/presentation/utils/span_list_builder.dart';
@@ -20,7 +20,8 @@ class HighlightTextEditingController extends TextEditingController {
final ExampleStringBuilder exampleStringBuilder;
final ParsingFilters? parsingFilters;
final ITranslator translator;
- final GlobalKey cursorRefKey = GlobalKey(debugLabel: "TextEditingFieldCursorRef");
+ final GlobalKey cursorRefKey =
+ GlobalKey(debugLabel: "TextEditingFieldCursorRef");
HighlightTextEditingController({
required this.exampleStringBuilder,
@@ -28,7 +29,7 @@ class HighlightTextEditingController extends TextEditingController {
this.parsingFilters,
super.text,
}) {
- cursorHeightOffset = verticalPadding*2 + verticalMargin*2 + 2;
+ cursorHeight = verticalPadding * 2 + verticalMargin * 2;
}
Map _suggestions = {};
@@ -40,9 +41,8 @@ class HighlightTextEditingController extends TextEditingController {
int offsetCounter = 0;
final double verticalPadding = 2.5;
- final double verticalMargin = 1;
- late final double cursorHeightOffset;
-
+ final double verticalMargin = 2;
+ late final double cursorHeight;
@override
set value(TextEditingValue newValue) {
@@ -51,19 +51,17 @@ class HighlightTextEditingController extends TextEditingController {
final oldText = value.text;
final newText = newValue.text;
-
if (oldText == newText) {
super.value = newValue;
return;
}
final parser = InputParser(translator)
- ..categoryFilter = parsingFilters?.categoryFilter
- ..repeatFilter = parsingFilters?.repeatFilter
- ..dateFilter = parsingFilters?.dateFilter;
+ ..categoryFilter = parsingFilters?.categoryFilter
+ ..repeatFilter = parsingFilters?.repeatFilter
+ ..dateFilter = parsingFilters?.dateFilter;
final parsed = parser.parse(newText);
-
exampleStringBuilder.rebuild(parsed);
_parsed = parsed;
_suggestions = makeSuggestions(
@@ -78,24 +76,27 @@ class HighlightTextEditingController extends TextEditingController {
super.value = newValue;
}
-
-
-
@override
- TextSpan buildTextSpan({required BuildContext context, TextStyle? style , required bool withComposing}) {
- assert(!value.composing.isValid || !withComposing || value.isComposingRangeValid);
+ TextSpan buildTextSpan(
+ {required BuildContext context,
+ TextStyle? style,
+ required bool withComposing,}) {
+ assert(!value.composing.isValid ||
+ !withComposing ||
+ value.isComposingRangeValid,);
final parsedInputList = _parsed?.toList() ?? [];
- parsedInputList.sortByCompare((element) => element.indices.start, (a, b) => a.compareTo(b));
+ parsedInputList.sortByCompare(
+ (element) => element.indices.start, (a, b) => a.compareTo(b),);
final builder = SpanListBuilder(
- verticalPadding: verticalPadding,
- horizontalPadding: 2.5,
- verticalMargin: verticalMargin,
- borderRadius: const Radius.circular(5.0),
- baseStyle: style,
- cursorRefKey: cursorRefKey,
- cursor: selection.base.offset,
+ verticalPadding: verticalPadding,
+ horizontalPadding: 2.5,
+ verticalMargin: verticalMargin,
+ borderRadius: const Radius.circular(5.0),
+ baseStyle: style,
+ cursorRefKey: cursorRefKey,
+ cursor: selection.base.offset,
);
var counter = 0; // Current position in Text
@@ -113,7 +114,6 @@ class HighlightTextEditingController extends TextEditingController {
);
}
-
void useSuggestion(Suggestion suggestion, Suggestion? flagSuggestion) {
value = insertSuggestion(
suggestion: suggestion,
@@ -123,7 +123,6 @@ class HighlightTextEditingController extends TextEditingController {
);
}
-
void _addRemainingChars(SpanListBuilder builder, int counter) {
if (counter < text.length) {
builder.addCharList(
@@ -141,7 +140,8 @@ class HighlightTextEditingController extends TextEditingController {
}
}
- void _addParsedInputToSpan(ParsedInput parsedInput, SpanListBuilder builder, int counter) {
+ void _addParsedInputToSpan(
+ ParsedInput parsedInput, SpanListBuilder builder, int counter,) {
final start = parsedInput.indices.start;
if (counter < start) {
diff --git a/lib/screens/enter_screen/presentation/utils/show_enter_screen_menu.dart b/lib/screens/enter_screen/presentation/utils/show_enter_screen_menu.dart
index 7fc1968b..45eb7850 100644
--- a/lib/screens/enter_screen/presentation/utils/show_enter_screen_menu.dart
+++ b/lib/screens/enter_screen/presentation/utils/show_enter_screen_menu.dart
@@ -9,14 +9,24 @@ void showEnterScreenMenu({
required Widget content,
}) {
final viewModel = context.read();
+ final mediaQuery = MediaQuery.of(context);
+ final view = View.of(context);
final controller = showBottomSheet(
- backgroundColor: Colors.transparent,
+ constraints: BoxConstraints(
+ maxHeight: mediaQuery.size.height - view.viewPadding.top / view.devicePixelRatio,
+ ),
context: context,
builder: (context) {
- return EnterScreenMenuScaffold(title: title, content: content);
+ return Padding(
+ padding: EdgeInsets.only(
+ bottom: MediaQuery.of(context).viewInsets.bottom,
+ ),
+ child: EnterScreenMenuScaffold(title: title, content: content),
+ );
},
+ enableDrag: true,
+ showDragHandle: true,
);
viewModel.isBottomSheetOpened = true;
-
controller.closed.then((value) => viewModel.isBottomSheetOpened = false);
}
diff --git a/lib/screens/enter_screen/presentation/utils/span_list_builder.dart b/lib/screens/enter_screen/presentation/utils/span_list_builder.dart
index 560a24af..4ff63870 100644
--- a/lib/screens/enter_screen/presentation/utils/span_list_builder.dart
+++ b/lib/screens/enter_screen/presentation/utils/span_list_builder.dart
@@ -8,9 +8,10 @@ class SpanListBuilder {
final double horizontalPadding;
final double verticalMargin;
final Radius borderRadius;
- final TextStyle? baseStyle;
+ late final TextStyle? baseStyle;
final GlobalKey cursorRefKey;
final int cursor;
+ late final double lineHeight;
int _charCount = 0;
@@ -22,7 +23,9 @@ class SpanListBuilder {
required this.cursorRefKey,
required this.cursor,
this.baseStyle,
- });
+ }) {
+ lineHeight = baseStyle?.height ?? 1.0;
+ }
Color _highlightColor = Colors.black;
Color _highlightTextColor = Colors.white;
@@ -46,16 +49,11 @@ class SpanListBuilder {
_charCount += 1;
spans.add(
- WidgetSpan(
- child: Container(
- margin: EdgeInsets.symmetric(vertical: verticalMargin),
- padding: EdgeInsets.symmetric(vertical: verticalPadding),
- key: _charCount == cursor ? cursorRefKey : null,
- child: Text(
- char,
- style: style,
- ),
- ),
+ PaddedSpan(
+ padding: EdgeInsets.symmetric(vertical: verticalPadding),
+ content: char,
+ key: _charCount == cursor ? cursorRefKey : null,
+ textStyle: style,
),
);
@@ -117,26 +115,22 @@ class SpanListBuilder {
_charCount += 1;
- final span = WidgetSpan(
- child: Container(
- key: _charCount == cursor ? cursorRefKey : null,
- margin: EdgeInsets.symmetric(vertical: verticalMargin),
- decoration: BoxDecoration(
- color: _highlightColor,
- borderRadius: _getCorrectHighlightBorderRadius(isStart, isEnd),
- ),
- padding: _getCorrectHighlightPadding(isStart, isEnd),
- child: Text(
- char,
- style: baseStyle?.copyWith(
- color: _highlightTextColor,
- ),
- ),
+
+ final span = PaddedSpan(
+ key: _charCount == cursor ? cursorRefKey : null,
+ margin: EdgeInsets.symmetric(vertical: verticalMargin),
+ padding: _getCorrectHighlightPadding(isStart, isEnd),
+ content: char,
+ decoration: BoxDecoration(
+ color: _highlightColor,
+ borderRadius: _getCorrectHighlightBorderRadius(isStart, isEnd),
+ ),
+ textStyle: baseStyle?.copyWith(
+ color: _highlightTextColor,
),
);
-
if (spanList != null) {
spanList.add(span);
return;
@@ -205,3 +199,45 @@ class SpanListBuilder {
return spans;
}
}
+
+class PaddedSpan extends WidgetSpan {
+ final TextStyle? textStyle;
+ final EdgeInsets margin;
+ final EdgeInsets padding;
+ final Key? key;
+ final String content;
+ final BoxDecoration? decoration;
+
+ PaddedSpan({
+ this.textStyle,
+ this.margin = EdgeInsets.zero,
+ required this.padding,
+ required this.content,
+ this.decoration,
+ super.alignment = PlaceholderAlignment.middle,
+ this.key,
+ }): super(
+ child: SizedBox(
+ height: (textStyle?.fontSize ?? 16) * (textStyle?.height ?? 1.0),
+ child: Padding(
+ padding: margin,
+ child: Container(
+ key: key,
+ decoration: decoration,
+ padding: padding,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ content,
+ style: textStyle?.copyWith(
+ height: 1.0,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+}
diff --git a/lib/screens/enter_screen/presentation/view_models/enter_screen_form_view_model.dart b/lib/screens/enter_screen/presentation/view_models/enter_screen_form_view_model.dart
index e2a01a6f..0bd4f808 100644
--- a/lib/screens/enter_screen/presentation/view_models/enter_screen_form_view_model.dart
+++ b/lib/screens/enter_screen/presentation/view_models/enter_screen_form_view_model.dart
@@ -50,7 +50,6 @@ class EnterScreenFormViewModel extends ChangeNotifier {
void handleUpdate(EntryType entryType) {
if (entryType != _entryType) {
_entryType = entryType;
- // TODO: Check if this is enough
notifyListeners();
}
}
diff --git a/lib/screens/enter_screen/presentation/view_models/enter_screen_text_field_view_model.dart b/lib/screens/enter_screen/presentation/view_models/enter_screen_text_field_view_model.dart
index df6d9404..2b107dc1 100644
--- a/lib/screens/enter_screen/presentation/view_models/enter_screen_text_field_view_model.dart
+++ b/lib/screens/enter_screen/presentation/view_models/enter_screen_text_field_view_model.dart
@@ -28,12 +28,17 @@ class EnterScreenTextFieldViewModel extends ChangeNotifier {
late StreamSubscription _streamSubscription;
final ITranslator _translator;
+ final ParsingFilters? parsingFilters;
HighlightTextEditingController get textController => _textController;
final GlobalKey textFieldKey = LabeledGlobalKey("text_field");
- EnterScreenTextFieldViewModel(BuildContext context, this._translator) {
+ EnterScreenTextFieldViewModel(
+ BuildContext context,
+ this._translator, {
+ this.parsingFilters,
+ }) {
_context = context;
_formViewModel = context.read();
@@ -67,14 +72,11 @@ class EnterScreenTextFieldViewModel extends ChangeNotifier {
translator: _translator,
),
translator: _translator,
- parsingFilters: ParsingFilters(
- categoryFilter: (category) => category.entryType == _entryType,
- ),
+ parsingFilters: parsingFilters,
);
textController.text = _formViewModel.data.parsed.raw;
- // TODO Get rid of this
textController.addListener(() {
final parsedData = textController.parsed;
@@ -163,6 +165,7 @@ class EnterScreenTextFieldViewModel extends ChangeNotifier {
onSelection: (tag, flag) {
textController.useSuggestion(tag, flag);
},
+ parsingFilters: parsingFilters,
),
),
);
diff --git a/lib/screens/enter_screen/presentation/widgets/buttons/entry_type_switch.dart b/lib/screens/enter_screen/presentation/widgets/buttons/entry_type_switch.dart
index 30037712..ba9574ad 100644
--- a/lib/screens/enter_screen/presentation/widgets/buttons/entry_type_switch.dart
+++ b/lib/screens/enter_screen/presentation/widgets/buttons/entry_type_switch.dart
@@ -1,11 +1,14 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:linum/common/enums/entry_type.dart';
+import 'package:linum/core/design/layout/utils/layout_helpers.dart';
import 'package:linum/generated/translation_keys.g.dart';
import 'package:linum/screens/enter_screen/presentation/view_models/enter_screen_form_view_model.dart';
import 'package:linum/screens/enter_screen/presentation/view_models/enter_screen_view_model.dart';
import 'package:provider/provider.dart';
+
+
class EnterScreenEntryTypeSwitch extends StatelessWidget {
const EnterScreenEntryTypeSwitch({super.key});
@@ -38,7 +41,7 @@ class EnterScreenEntryTypeSwitch extends StatelessWidget {
: Colors.black26,
//TODO remove hardcoded textStyle once Material You is implemented
- fontSize: 12,
+ fontSize: context.scaledFontSize(12),
),
),
),
@@ -55,7 +58,7 @@ class EnterScreenEntryTypeSwitch extends StatelessWidget {
: Colors.black26,
//TODO remove hardcoded textStyle once Material You is implemented
- fontSize: 12,
+ fontSize: context.scaledFontSize(12),
),
),
),
diff --git a/lib/screens/enter_screen/presentation/widgets/buttons/tag_selector_button.dart b/lib/screens/enter_screen/presentation/widgets/buttons/tag_selector_button.dart
index ced2fbe9..8c1aaea9 100644
--- a/lib/screens/enter_screen/presentation/widgets/buttons/tag_selector_button.dart
+++ b/lib/screens/enter_screen/presentation/widgets/buttons/tag_selector_button.dart
@@ -7,6 +7,7 @@ class TagSelectorButton extends StatelessWidget {
final String? symbol;
final IconData? icon;
final Color textColor;
+ final bool showBadge;
final void Function() onTap;
const TagSelectorButton({
super.key,
@@ -15,6 +16,7 @@ class TagSelectorButton extends StatelessWidget {
this.icon,
this.textColor = Colors.black,
this.onTap = _onTap,
+ this.showBadge = false,
});
@override
@@ -46,6 +48,21 @@ class TagSelectorButton extends StatelessWidget {
);
}
+ final badges = [];
+ if (showBadge) {
+ badges.add(
+ Positioned(
+ top: -8,
+ right: -16,
+ child: Badge.count(
+ count: 1,
+ textColor: Colors.white,
+ largeSize: 14,
+ ),
+ ),
+ );
+ }
+
return GestureDetector(
onTap: onTap,
child: Container(
@@ -54,14 +71,20 @@ class TagSelectorButton extends StatelessWidget {
borderRadius: const BorderRadius.all(Radius.circular(5000)),
),
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
- child: Row(
- mainAxisSize: MainAxisSize.min,
+ child: Stack(
+ clipBehavior: Clip.none,
children: [
- ...iconWidgets,
- Text(
- title,
- style: TextStyle(color: textColor, fontSize: 14),
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ...iconWidgets,
+ Text(
+ title,
+ style: TextStyle(color: textColor, fontSize: 14),
+ ),
+ ],
),
+ ...badges,
],
),
),
diff --git a/lib/screens/enter_screen/presentation/widgets/enter_screen_scaffold.dart b/lib/screens/enter_screen/presentation/widgets/enter_screen_scaffold.dart
index f49a4e57..02063d45 100644
--- a/lib/screens/enter_screen/presentation/widgets/enter_screen_scaffold.dart
+++ b/lib/screens/enter_screen/presentation/widgets/enter_screen_scaffold.dart
@@ -20,7 +20,7 @@ class EnterScreenScaffold extends StatelessWidget {
return ConstrainedBox(
constraints: BoxConstraints(
- maxHeight: context.proportionateScreenHeightFraction(ScreenFraction.threefifths) + useKeyBoardHeight(context),
+ maxHeight: context.proportionateScreenHeightFraction(ScreenFraction.half) + useKeyBoardHeight(context),
),
child: Scaffold(
diff --git a/lib/screens/enter_screen/presentation/widgets/form/enter_screen_text_field.dart b/lib/screens/enter_screen/presentation/widgets/form/enter_screen_text_field.dart
index e69b31c1..8782fbe1 100644
--- a/lib/screens/enter_screen/presentation/widgets/form/enter_screen_text_field.dart
+++ b/lib/screens/enter_screen/presentation/widgets/form/enter_screen_text_field.dart
@@ -2,14 +2,16 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:linum/common/utils/base_translator.dart';
import 'package:linum/generated/translation_keys.g.dart';
+import 'package:linum/screens/enter_screen/domain/models/suggestion_filters.dart';
import 'package:linum/screens/enter_screen/presentation/view_models/enter_screen_form_view_model.dart';
import 'package:linum/screens/enter_screen/presentation/view_models/enter_screen_text_field_view_model.dart';
import 'package:provider/provider.dart';
class EnterScreenTextField extends StatelessWidget {
-
+ final ParsingFilters? parsingFilters;
const EnterScreenTextField({
super.key,
+ this.parsingFilters,
});
@override
@@ -18,6 +20,7 @@ class EnterScreenTextField extends StatelessWidget {
fontSize: 16,
letterSpacing: 1.0,
textBaseline: TextBaseline.alphabetic,
+ height: 2.0,
);
final locale = context.locale;
@@ -27,12 +30,16 @@ class EnterScreenTextField extends StatelessWidget {
EnterScreenTextFieldViewModel
>(
create: (context) => EnterScreenTextFieldViewModel(
- context, BaseTranslator(locale.languageCode),
+ context,
+ BaseTranslator(locale.languageCode),
+ parsingFilters: parsingFilters,
),
update: (context, formViewModel, textFieldViewModel) {
if (textFieldViewModel == null) {
return EnterScreenTextFieldViewModel(
- context, BaseTranslator(locale.languageCode),
+ context,
+ BaseTranslator(locale.languageCode),
+ parsingFilters: parsingFilters,
);
}
textFieldViewModel.handleUpdate(context);
@@ -47,8 +54,8 @@ class EnterScreenTextField extends StatelessWidget {
),
validator: _validateInput,
keyboardType: TextInputType.multiline,
- maxLines: 6,
- cursorHeight: baseTextStyle.fontSize! + textFieldViewModel.textController.cursorHeightOffset,
+ maxLines: null,
+ cursorHeight: baseTextStyle.fontSize! + textFieldViewModel.textController.cursorHeight,
autofocus: true,
style: baseTextStyle,
key: textFieldViewModel.textFieldKey,
diff --git a/lib/screens/enter_screen/presentation/widgets/form/quick_tag_menu.dart b/lib/screens/enter_screen/presentation/widgets/form/quick_tag_menu.dart
index 29fee65b..a8d4011f 100644
--- a/lib/screens/enter_screen/presentation/widgets/form/quick_tag_menu.dart
+++ b/lib/screens/enter_screen/presentation/widgets/form/quick_tag_menu.dart
@@ -78,6 +78,9 @@ class QuickTagMenu extends StatelessWidget {
formViewModel.data.options.repeatConfiguration ?? formViewModel.defaultValues.repeatConfiguration;
final date = formViewModel.data.options.date ?? formViewModel.defaultValues.date;
+ final hasNote = formViewModel.data.options.notes != null;
+
+
return [
TagSelectorButton(
title: tr(formatter.format(date) ?? ""),
@@ -140,6 +143,7 @@ class QuickTagMenu extends StatelessWidget {
TagSelectorButton(
title: tr(translationKeys.enterScreen.menu.notes),
icon: Icons.edit_outlined,
+ showBadge: hasNote,
onTap: () {
showEnterScreenMenu(
context: context,
diff --git a/lib/screens/enter_screen/presentation/widgets/menu/category_list_view.dart b/lib/screens/enter_screen/presentation/widgets/menu/category_list_view.dart
index 4c024dfa..a414426b 100644
--- a/lib/screens/enter_screen/presentation/widgets/menu/category_list_view.dart
+++ b/lib/screens/enter_screen/presentation/widgets/menu/category_list_view.dart
@@ -43,7 +43,10 @@ class CategoryListView extends StatelessWidget {
},
),
),
- const LinumCloseButton(),
+ const LinumCloseButton(
+ padding: EdgeInsets.symmetric(vertical: 8),
+ ),
+ const SizedBox(height: 24,),
],
),
);
diff --git a/lib/screens/enter_screen/presentation/widgets/menu/currency_list_view.dart b/lib/screens/enter_screen/presentation/widgets/menu/currency_list_view.dart
index e1af5eb4..18263f8d 100644
--- a/lib/screens/enter_screen/presentation/widgets/menu/currency_list_view.dart
+++ b/lib/screens/enter_screen/presentation/widgets/menu/currency_list_view.dart
@@ -1,3 +1,4 @@
+import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:linum/core/design/layout/enums/screen_fraction_enum.dart';
import 'package:linum/core/design/layout/utils/layout_helpers.dart';
@@ -45,7 +46,10 @@ class CurrencyListView extends StatelessWidget {
},
),
),
- const LinumCloseButton(),
+ const LinumCloseButton(
+ padding: EdgeInsets.symmetric(vertical: 8),
+ ),
+ const SizedBox(height: 24,),
],
),
);
diff --git a/lib/screens/enter_screen/presentation/widgets/menu/enter_screen_menu_scaffold.dart b/lib/screens/enter_screen/presentation/widgets/menu/enter_screen_menu_scaffold.dart
index 169a9b46..68a29590 100644
--- a/lib/screens/enter_screen/presentation/widgets/menu/enter_screen_menu_scaffold.dart
+++ b/lib/screens/enter_screen/presentation/widgets/menu/enter_screen_menu_scaffold.dart
@@ -33,7 +33,6 @@ class EnterScreenMenuScaffold extends StatelessWidget {
Container(
width: double.infinity,
alignment: Alignment.center,
- padding: const EdgeInsets.symmetric(vertical: 32.0),
child: Text(
title!,
style: Theme.of(context).textTheme.headlineSmall,
diff --git a/lib/screens/enter_screen/presentation/widgets/menu/notes_view.dart b/lib/screens/enter_screen/presentation/widgets/menu/notes_view.dart
index 22af8935..55cad6a3 100644
--- a/lib/screens/enter_screen/presentation/widgets/menu/notes_view.dart
+++ b/lib/screens/enter_screen/presentation/widgets/menu/notes_view.dart
@@ -17,40 +17,43 @@ class NotesView extends StatelessWidget {
text: (notes != null && notes != "") ? notes : null,
);
return Expanded(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 24.0),
- child: Flex(
- direction: Axis.horizontal,
- children: [
- Expanded(
- child: TextField(
- controller: textController,
- keyboardType: TextInputType.multiline,
- maxLines: null,
- decoration: InputDecoration(
- border: InputBorder.none,
- hintText: tr(translationKeys.enterScreen.enterNoteHint),
+ child: Padding(
+ padding: const EdgeInsets.only(bottom: 24),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 24.0),
+ child: Flex(
+ direction: Axis.horizontal,
+ children: [
+ Expanded(
+ child: TextField(
+ controller: textController,
+ keyboardType: TextInputType.multiline,
+ maxLines: null,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ hintText: tr(translationKeys.enterScreen.enterNoteHint),
+ ),
),
),
- ),
- const EnterScreenAbortButton(),
- ],
+ const EnterScreenAbortButton(),
+ ],
+ ),
),
- ),
- MenuActionButton(
- label: tr(translationKeys.enterScreen.button.saveNote),
- padding: const EdgeInsets.symmetric(vertical: 24.0),
- onPressed: () {
- formViewModel.data = formViewModel.data.copyWithOptions(
- notes: notes,
- );
- Navigator.pop(context);
- },
- ),
- ],
+ MenuActionButton(
+ label: tr(translationKeys.enterScreen.button.saveNote),
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ onPressed: () {
+ formViewModel.data = formViewModel.data.copyWithOptions(
+ notes: textController.text,
+ );
+ Navigator.pop(context);
+ },
+ ),
+ ],
+ ),
),
);
}
diff --git a/lib/screens/enter_screen/presentation/widgets/menu/repeat_config_list_view.dart b/lib/screens/enter_screen/presentation/widgets/menu/repeat_config_list_view.dart
index d468afac..cb1ab49b 100644
--- a/lib/screens/enter_screen/presentation/widgets/menu/repeat_config_list_view.dart
+++ b/lib/screens/enter_screen/presentation/widgets/menu/repeat_config_list_view.dart
@@ -45,7 +45,10 @@ class RepeatConfigListView extends StatelessWidget {
},
),
),
- const LinumCloseButton(),
+ const LinumCloseButton(
+ padding: EdgeInsets.symmetric(vertical: 8),
+ ),
+ const SizedBox(height: 24,),
],
),
);
diff --git a/lib/screens/enter_screen/presentation/widgets/overlay/suggestion_list.dart b/lib/screens/enter_screen/presentation/widgets/overlay/suggestion_list.dart
index f4be3cac..9e0276b9 100644
--- a/lib/screens/enter_screen/presentation/widgets/overlay/suggestion_list.dart
+++ b/lib/screens/enter_screen/presentation/widgets/overlay/suggestion_list.dart
@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:linum/screens/enter_screen/domain/models/suggestion.dart';
+import 'package:linum/screens/enter_screen/domain/models/suggestion_filters.dart';
import 'package:linum/screens/enter_screen/domain/suggesting/get_sub_suggestions.dart';
import 'package:linum/screens/enter_screen/presentation/widgets/overlay/suggestion_list_item.dart';
@@ -12,6 +13,7 @@ void _onSelection(
class SuggestionList extends StatelessWidget {
final List suggestions;
final double maxHeight;
+ final ParsingFilters? parsingFilters;
final void Function(
Suggestion suggestion,
Suggestion? flagSuggestion,
@@ -22,6 +24,7 @@ class SuggestionList extends StatelessWidget {
required this.suggestions,
this.onSelection = _onSelection,
this.maxHeight = 200,
+ this.parsingFilters,
});
@override
@@ -34,6 +37,7 @@ class SuggestionList extends StatelessWidget {
flagSuggestion: suggestions.first,
onSelection: onSelection,
maxHeight: maxHeight,
+ parsingFilters: parsingFilters,
);
}
@@ -82,6 +86,7 @@ class _SubSuggestionsList extends StatelessWidget {
final List? boxShadow;
final Suggestion flagSuggestion;
final double maxHeight;
+ final ParsingFilters? parsingFilters;
final void Function(
Suggestion suggestion,
Suggestion? parentSuggestion,
@@ -92,11 +97,15 @@ class _SubSuggestionsList extends StatelessWidget {
this.onSelection = _onSelection,
this.maxHeight = 200,
this.boxShadow,
+ this.parsingFilters,
});
@override
Widget build(BuildContext context) {
- final subSuggestions = getSubSuggestions(flagSuggestion.flag!); // TODO: HIDE FROM PRESENTATION
+ final subSuggestions = getSubSuggestions(
+ flagSuggestion.flag!,
+ filters: parsingFilters,
+ ); // TODO: HIDE FROM PRESENTATION
return DecoratedBox(
decoration: BoxDecoration(
diff --git a/lib/screens/enter_screen/presentation/widgets/views/enter_screen_form_view.dart b/lib/screens/enter_screen/presentation/widgets/views/enter_screen_form_view.dart
index 83213a91..6b37e86c 100644
--- a/lib/screens/enter_screen/presentation/widgets/views/enter_screen_form_view.dart
+++ b/lib/screens/enter_screen/presentation/widgets/views/enter_screen_form_view.dart
@@ -3,14 +3,16 @@ import 'dart:math';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:linum/common/utils/base_translator.dart';
+import 'package:linum/core/design/layout/utils/layout_helpers.dart';
import 'package:linum/core/design/layout/utils/media_query_accessors.dart';
import 'package:linum/core/repeating/constants/standard_repeat_configs.dart';
import 'package:linum/features/currencies/core/constants/standard_currencies.dart';
+import 'package:linum/screens/enter_screen/domain/models/suggestion_filters.dart';
+import 'package:linum/screens/enter_screen/presentation/utils/context_extensions.dart';
import 'package:linum/screens/enter_screen/presentation/utils/get_default_values.dart';
import 'package:linum/screens/enter_screen/presentation/utils/initial_form_data_builder.dart';
import 'package:linum/screens/enter_screen/presentation/view_models/enter_screen_form_view_model.dart';
import 'package:linum/screens/enter_screen/presentation/view_models/enter_screen_view_model.dart';
-import 'package:linum/screens/enter_screen/presentation/widgets/buttons/abort_button.dart';
import 'package:linum/screens/enter_screen/presentation/widgets/buttons/continue_button.dart';
import 'package:linum/screens/enter_screen/presentation/widgets/buttons/delete_button.dart';
import 'package:linum/screens/enter_screen/presentation/widgets/buttons/entry_type_switch.dart';
@@ -48,6 +50,7 @@ class EnterScreenFormView extends StatelessWidget {
repeatConfigurations: repeatConfigurations,
translator: translator,
);
+
final screenViewModel = context.read();
builder
..useTransaction(
@@ -59,6 +62,7 @@ class EnterScreenFormView extends StatelessWidget {
)
..entryType = screenViewModel.entryType;
+
final initialData = builder.build();
return EnterScreenFormViewModel(
defaultValues: getDefaultValues(context),
@@ -79,54 +83,56 @@ class _EnterScreenFormView extends StatelessWidget {
context.read()
.keyboardStateListener.inform(keyboardHeight);
- final availableSpace = useScreenHeight(context) - 400 - 30;
+ final fixedHeight = context.scaledHeight(400);
+ final availableSpace = useScreenHeight(context) - fixedHeight - 30;
final adjustedKeyboardHeight = min(keyboardHeight, availableSpace);
return EnterScreenScaffold(
- bodyHeight: 400 + adjustedKeyboardHeight,
+ bodyHeight: fixedHeight + adjustedKeyboardHeight,
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 24),
child: Column(
children: [
- Flex(
- direction: Axis.horizontal,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Expanded(
- child: Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 40,
- vertical: 10,
- ),
+ Expanded(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: SingleChildScrollView(
+ child: Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 20,
+ vertical: 10,
+ ),
- child: const EnterScreenTextField(),
+ child: EnterScreenTextField(
+ parsingFilters: ParsingFilters(
+ categoryFilter: (category) => category.entryType == context.getEntryType(),
+ ),
+ ),
+ ),
+ ),
),
- ),
- const Padding(
- padding: EdgeInsets.only(
- right: 55,
- top: 18,
- bottom: 10,
+ Column(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
+ child: QuickTagMenu(),
+ ),
+ Container(
+ padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
+ child: const Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ EnterScreenDeleteButton(),
+ EnterScreenEntryTypeSwitch(),
+ EnterScreenContinueButton(),
+ ],
+ ),
+ ),
+ ],
),
- child: EnterScreenAbortButton(),
- ),
- ],
- ),
- const Expanded(
- child: Padding(
- padding: EdgeInsets.symmetric(horizontal: 40, vertical: 10),
- child: QuickTagMenu(),
- ),
- ),
- Container(
- padding: const EdgeInsets.fromLTRB(40, 20, 40, 10),
- child: const Flex(
- direction: Axis.horizontal,
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- EnterScreenDeleteButton(),
- EnterScreenEntryTypeSwitch(),
- EnterScreenContinueButton(),
],
),
),
diff --git a/lib/screens/home_screen/components/home_screen_card/widgets/home_screen_card.dart b/lib/screens/home_screen/components/home_screen_card/widgets/home_screen_card.dart
index 8fa5c3df..27ebea92 100644
--- a/lib/screens/home_screen/components/home_screen_card/widgets/home_screen_card.dart
+++ b/lib/screens/home_screen/components/home_screen_card/widgets/home_screen_card.dart
@@ -9,7 +9,6 @@ import 'package:flutter/material.dart';
import 'package:linum/common/components/screen_card/widgets/screen_card_side.dart';
import 'package:linum/common/components/screen_card/widgets/screen_card_skeleton.dart';
import 'package:linum/core/design/layout/utils/layout_helpers.dart';
-import 'package:linum/screens/home_screen/components/home_screen_card/widgets/home_screen_card_back.dart';
import 'package:linum/screens/home_screen/components/home_screen_card/widgets/home_screen_card_front.dart';
class HomeScreenCard extends StatelessWidget {
@@ -27,11 +26,11 @@ class HomeScreenCard extends StatelessWidget {
cardWidth: cardWidth,
cardHeight: cardHeight,
),
- backSide: ScreenCardSide(
+ /* backSide: ScreenCardSide(
content: HomeScreenCardBack(),
cardWidth: cardWidth,
cardHeight: cardHeight,
- ),
+ ),*/
flipCardController: controller,
);
}
diff --git a/lib/screens/home_screen/components/home_screen_card/widgets/home_screen_card_front.dart b/lib/screens/home_screen/components/home_screen_card/widgets/home_screen_card_front.dart
index 4a460b2c..f0dfc566 100644
--- a/lib/screens/home_screen/components/home_screen_card/widgets/home_screen_card_front.dart
+++ b/lib/screens/home_screen/components/home_screen_card/widgets/home_screen_card_front.dart
@@ -89,7 +89,7 @@ class HomeScreenCardFront extends StatelessWidget {
: Theme.of(context).textTheme.displaySmall,
),
),
- Expanded(
+ /*Expanded(
child: Align(
alignment: Alignment.topRight,
child: IconButton(
@@ -104,7 +104,8 @@ class HomeScreenCardFront extends StatelessWidget {
),
),
),
- ),
+ ),*/
+ const Spacer(),
],
),
Text(
diff --git a/lib/screens/home_screen/home_screen.dart b/lib/screens/home_screen/home_screen.dart
index 85e6e2f8..bbee2ddb 100644
--- a/lib/screens/home_screen/home_screen.dart
+++ b/lib/screens/home_screen/home_screen.dart
@@ -21,8 +21,6 @@ import 'package:linum/screens/home_screen/widgets/home_screen_listview.dart';
import 'package:linum/screens/lock_screen/services/pin_code_service.dart';
import 'package:provider/provider.dart';
-
-
/// Page Index: 0
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@@ -42,8 +40,7 @@ class _HomeScreenState extends State {
void resetAlgorithmProvider() {
// TODO: read how the Widget is re-rendered here
- final AlgorithmService algorithmProvider =
- context.read();
+ final AlgorithmService algorithmProvider = context.read();
if (algorithmProvider.state.filter == Filters.noFilter) {
algorithmProvider.resetCurrentShownMonth();
@@ -57,8 +54,7 @@ class _HomeScreenState extends State {
@override
Widget build(BuildContext context) {
- final PinCodeService pinCodeProvider =
- context.watch();
+ final PinCodeService pinCodeProvider = context.watch();
resetAlgorithmProvider();
@@ -77,6 +73,7 @@ class _HomeScreenState extends State {
key: const Key("pinRecallButton"),
),
AppBarAction.fromPreset(DefaultAction.settings),
+ AppBarAction.fromPreset(DefaultAction.bugreport),
],
screenCard: HomeScreenCard(
controller: _flipCardController!,
@@ -98,13 +95,17 @@ class _HomeScreenState extends State {
DropdownMenuItem(
value: false,
child: Text(
- tr(translationKeys.homeScreen.labelRecentTransactions),
+ tr(translationKeys
+ .homeScreen.labelRecentTransactions,
+ ),
),
),
DropdownMenuItem(
value: true,
child: Text(
- tr(translationKeys.homeScreen.labelActiveSerialcontracts),
+ tr(translationKeys
+ .homeScreen.labelActiveSerialcontracts,
+ ),
),
),
],
@@ -125,12 +126,16 @@ class _HomeScreenState extends State {
),
TextButton(
onPressed: () {
- context.getMainRouterDelegate().pushRoute(MainRoute.filter);
+ context
+ .getMainRouterDelegate()
+ .pushRoute(MainRoute.filter);
},
child: Text(
showRepeatables
- ? tr(translationKeys.homeScreen.buttonShowAll).toUpperCase()
- : tr(translationKeys.homeScreen.buttonShowMore).toUpperCase(),
+ ? tr(translationKeys.homeScreen.buttonShowAll)
+ .toUpperCase()
+ : tr(translationKeys.homeScreen.buttonShowMore)
+ .toUpperCase(),
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontSize: 14,
diff --git a/lib/screens/home_screen/widgets/transaction_tile.dart b/lib/screens/home_screen/widgets/transaction_tile.dart
index 561caf04..38b016d0 100644
--- a/lib/screens/home_screen/widgets/transaction_tile.dart
+++ b/lib/screens/home_screen/widgets/transaction_tile.dart
@@ -7,12 +7,12 @@ import 'package:linum/common/components/dialogs/show_transaction_delete_dialog.d
import 'package:linum/core/balance/models/transaction.dart';
import 'package:linum/core/balance/services/balance_data_service.dart';
import 'package:linum/core/balance/utils/transaction_amount_formatter.dart';
-import 'package:linum/core/categories/core/constants/standard_categories.dart';
-import 'package:linum/core/categories/core/utils/translate_category.dart';
-import 'package:linum/generated/translation_keys.g.dart';
import 'package:linum/screens/enter_screen/presentation/utils/show_enter_screen.dart';
import 'package:linum/screens/home_screen/widgets/serial_delete_mode_selection_view.dart';
import 'package:linum/screens/home_screen/widgets/transaction_amount_display.dart';
+import 'package:linum/screens/home_screen/widgets/transaction_tile_background.dart';
+import 'package:linum/screens/home_screen/widgets/transaction_tile_icon.dart';
+import 'package:linum/screens/home_screen/widgets/transaction_tile_title.dart';
import 'package:provider/provider.dart';
class TransactionTile extends StatelessWidget {
@@ -32,143 +32,43 @@ class TransactionTile extends StatelessWidget {
Widget build(BuildContext context) {
final String langCode = context.locale.languageCode;
final DateFormat formatter = DateFormat('EEEE, dd. MMMM yyyy', langCode);
- final balanceDataService = context.read();
+
return GestureDetector(
onTap: () {
showEnterScreen(context, transaction: transaction);
},
child: Dismissible(
- background: ColoredBox(
- color: Colors.red,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.end,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Padding(
- padding: const EdgeInsets.only(right: 30),
- child: Wrap(
- alignment: WrapAlignment.center,
- spacing: 16.0,
- children: [
- Text(
- tr(translationKeys.listview.dismissible.labelDelete),
- style: Theme.of(context).textTheme.labelLarge,
- ),
- Icon(
- Icons.delete,
- color: Theme.of(context).colorScheme.secondaryContainer,
- ),
- ],
- ),
- ),
- ],
- ),
- ),
+ background: const TransactionTileBackground(),
key: Key(transaction.id),
direction: DismissDirection.endToStart,
dismissThresholds: const {
DismissDirection.endToStart: 0.5,
},
- confirmDismiss: (DismissDirection direction) {
- // TODO refactor! SerialDeleteSelectionView has duplicated code from the enterscreen's serial transaction change mode selection.
- if(transaction.repeatId != null){
- const radius = Radius.circular(16.0);
- showModalBottomSheet(
- context: context,
- builder: (context) {
- return SerialDeleteModeSelectionView(transaction: transaction,);
- },
- isDismissible: true,
- shape: const RoundedRectangleBorder(
- borderRadius: BorderRadius.only(topLeft: radius, topRight: radius),
- ),
- );
- } else {
- showTransactionDeleteDialog(context, () async {
- balanceDataService.removeTransaction(transaction);
- });
- }
- return Future.value(false);
- },
+ confirmDismiss: (DismissDirection dismissDirection) => _confirmDismiss(context),
child: ListTile(
leading: badge.Badge(
- padding: const EdgeInsets.all(2),
- toAnimate: false,
- position: const badge.BadgePosition(bottom: 23, start: 23),
- elevation: 1,
- badgeColor: isFutureItem && transaction.repeatId != null
- ? Theme.of(context).colorScheme.tertiaryContainer
- //badgeColor for current transactions
- : transaction.amount > 0
- //badgeColor for future transactions
- ? transaction.repeatId != null
- ? Theme.of(context).colorScheme.tertiary
- // ignore: use_full_hex_values_for_flutter_colors
- : const Color(0x000000)
- : transaction.repeatId != null
- ? Theme.of(context).colorScheme.errorContainer
- // ignore: use_full_hex_values_for_flutter_colors
- : const Color(0x000000),
- //cannot use the suggestion as it produces an unwanted white point
+ badgeStyle: badge.BadgeStyle(
+ padding: const EdgeInsets.all(2),
+ elevation: 1,
+ badgeColor: _selectBadgeColor(context),
+ ),
+ position: badge.BadgePosition.custom(bottom: 23, start: 23),
badgeContent: transaction.repeatId != null
? Icon(
Icons.autorenew_rounded,
- color: isFutureItem
- ? transaction.amount > 0
- ? Theme.of(context).colorScheme.tertiary
- : Theme.of(context).colorScheme.errorContainer
- : Theme.of(context).colorScheme.secondaryContainer,
+ color: _selectBadgeContentColor(context),
size: 18,
)
: const SizedBox(),
- child: CircleAvatar(
- backgroundColor: isFutureItem
- ? transaction.amount > 0
- ? Theme.of(context).colorScheme.tertiary
- // FUTURE INCOME BACKGROUND
- : Theme.of(context).colorScheme.errorContainer
- // FUTURE EXPENSE BACKGROUND
- : transaction.amount > 0
- ? Theme.of(context).colorScheme.secondary
- // PRESENT INCOME BACKGROUND
- : Theme.of(context).colorScheme.secondary,
- // PRESENT EXPENSE BACKGROUND
- child: transaction.amount > 0
- ? Icon(
- standardCategories[transaction.category]?.icon ??
- Icons.error,
- color: isFutureItem
- ? Theme.of(context).colorScheme.onPrimary
- // FUTURE INCOME ICON
- : Theme.of(context).colorScheme.tertiary,
- // PRESENT INCOME ICON
- )
- : Icon(
- standardCategories[transaction.category]?.icon ??
- Icons.error,
- color: isFutureItem
- ? Theme.of(context)
- .colorScheme
- .onPrimary // FUTURE EXPENSE ICON
- : Theme.of(context)
- .colorScheme
- .errorContainer, // PRESENT EXPENSE ICON
- ),
+ child: TransactionTileIcon(
+ transaction: transaction,
+ isFutureItem: isFutureItem,
),
),
- title: Text(
- transaction.name != ""
- ? transaction.name
- : translateCategoryId(
- transaction.category,
- isExpense: transaction.amount <= 0,
- ),
- style: isFutureItem
- ? Theme.of(context).textTheme.bodyLarge!.copyWith(
- fontStyle: FontStyle.italic,
- color: Theme.of(context).colorScheme.onSurface,
- ) : Theme.of(context).textTheme.bodyLarge,
+ title: TransactionTileTitle(
+ transaction: transaction,
+ isFutureItem: isFutureItem,
),
subtitle: Text(
formatter.format(
@@ -176,9 +76,10 @@ class TransactionTile extends StatelessWidget {
).toUpperCase(),
style: isFutureItem
? Theme.of(context).textTheme.labelSmall!.copyWith(
- fontStyle: FontStyle.italic,
- color: Theme.of(context).colorScheme.onSurface,
- ) : Theme.of(context).textTheme.labelSmall,
+ fontStyle: FontStyle.italic,
+ color: Theme.of(context).colorScheme.onSurface,
+ )
+ : Theme.of(context).textTheme.labelSmall,
),
trailing: TransactionAmountDisplay(
transaction: transaction,
@@ -188,4 +89,55 @@ class TransactionTile extends StatelessWidget {
),
);
}
+
+ Color _selectBadgeColor(BuildContext context) {
+ if (isFutureItem && transaction.repeatId != null) {
+ return Theme.of(context).colorScheme.tertiaryContainer;
+ }
+
+ if (transaction.repeatId != null && transaction.amount > 0) {
+ return Theme.of(context).colorScheme.tertiary;
+ }
+
+ if (transaction.repeatId != null) {
+ return Theme.of(context).colorScheme.errorContainer;
+ }
+
+ // ignore: use_full_hex_values_for_flutter_colors
+ return const Color(0x000000);
+ //cannot use the suggestion as it produces an unwanted white point
+ }
+
+ Color _selectBadgeContentColor(BuildContext context) {
+ if (!isFutureItem) {
+ return Theme.of(context).colorScheme.secondaryContainer;
+ }
+
+ return transaction.amount > 0
+ ? Theme.of(context).colorScheme.tertiary
+ : Theme.of(context).colorScheme.errorContainer;
+ }
+
+ Future _confirmDismiss(BuildContext context) async {
+ // TODO refactor! SerialDeleteSelectionView has duplicated code from the enterscreen's serial transaction change mode selection.
+ final balanceDataService = context.read();
+
+ if(transaction.repeatId != null){
+ const radius = Radius.circular(16.0);
+ showModalBottomSheet(
+ context: context,
+ builder: (context) {
+ return SerialDeleteModeSelectionView(transaction: transaction,);
+ },
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.only(topLeft: radius, topRight: radius),
+ ),
+ );
+ } else {
+ showTransactionDeleteDialog(context, () async {
+ balanceDataService.removeTransaction(transaction);
+ });
+ }
+ return Future.value(false);
+ }
}
diff --git a/lib/screens/home_screen/widgets/transaction_tile_background.dart b/lib/screens/home_screen/widgets/transaction_tile_background.dart
new file mode 100644
index 00000000..54a9c890
--- /dev/null
+++ b/lib/screens/home_screen/widgets/transaction_tile_background.dart
@@ -0,0 +1,37 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:linum/generated/translation_keys.g.dart';
+
+class TransactionTileBackground extends StatelessWidget {
+ const TransactionTileBackground({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return ColoredBox(
+ color: Colors.red,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(right: 30),
+ child: Wrap(
+ alignment: WrapAlignment.center,
+ spacing: 16.0,
+ children: [
+ Text(
+ tr(translationKeys.listview.dismissible.labelDelete),
+ style: Theme.of(context).textTheme.labelLarge,
+ ),
+ Icon(
+ Icons.delete,
+ color: Theme.of(context).colorScheme.secondaryContainer,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/screens/home_screen/widgets/transaction_tile_icon.dart b/lib/screens/home_screen/widgets/transaction_tile_icon.dart
new file mode 100644
index 00000000..028a6b61
--- /dev/null
+++ b/lib/screens/home_screen/widgets/transaction_tile_icon.dart
@@ -0,0 +1,45 @@
+import 'package:flutter/material.dart';
+import 'package:linum/core/balance/models/transaction.dart';
+import 'package:linum/core/categories/core/constants/standard_categories.dart';
+
+class TransactionTileIcon extends StatelessWidget {
+ final Transaction transaction;
+ final bool isFutureItem;
+ const TransactionTileIcon({
+ super.key,
+ required this.transaction,
+ required this.isFutureItem,
+ });
+
+ // TODO: Get rid of these ternaries
+ @override
+ Widget build(BuildContext context) {
+ return CircleAvatar(
+ backgroundColor: _selectBackgroundColor(context),
+ // PRESENT EXPENSE BACKGROUND
+ child: Icon(
+ standardCategories[transaction.category]?.icon ?? Icons.error,
+ color: _selectIconColor(context),
+ ),
+ );
+ }
+
+ Color _selectBackgroundColor(BuildContext context) {
+ if (isFutureItem) {
+ return transaction.amount > 0
+ ? Theme.of(context).colorScheme.tertiary
+ : Theme.of(context).colorScheme.errorContainer;
+ }
+ return Theme.of(context).colorScheme.secondary;
+ }
+
+ Color _selectIconColor(BuildContext context) {
+ if (isFutureItem) {
+ return Theme.of(context).colorScheme.onPrimary;
+ }
+
+ return transaction.amount > 0
+ ? Theme.of(context).colorScheme.tertiary
+ : Theme.of(context).colorScheme.errorContainer;
+ }
+}
diff --git a/lib/screens/home_screen/widgets/transaction_tile_title.dart b/lib/screens/home_screen/widgets/transaction_tile_title.dart
new file mode 100644
index 00000000..f75f3d52
--- /dev/null
+++ b/lib/screens/home_screen/widgets/transaction_tile_title.dart
@@ -0,0 +1,50 @@
+import 'package:flutter/material.dart';
+import 'package:linum/core/balance/models/transaction.dart';
+import 'package:linum/core/categories/core/utils/translate_category.dart';
+
+class TransactionTileTitle extends StatelessWidget {
+ final Transaction transaction;
+ final bool isFutureItem;
+
+ const TransactionTileTitle({
+ super.key,
+ required this.transaction,
+ required this.isFutureItem,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final titleText = transaction.name != ""
+ ? transaction.name
+ : translateCategoryId(
+ transaction.category,
+ isExpense: transaction.amount <= 0,
+ );
+
+ return Row(
+ children: [
+ Text(
+ titleText,
+ style: isFutureItem
+ ? Theme.of(context).textTheme.bodyLarge!.copyWith(
+ fontStyle: FontStyle.italic,
+ color: Theme.of(context).colorScheme.onSurface,
+ )
+ : Theme.of(context).textTheme.bodyLarge,
+ ),
+ const SizedBox(width: 10,),
+ transaction.note != null
+ ? Badge(
+ largeSize: 20,
+ backgroundColor: Theme.of(context).colorScheme.primary,
+ label: const Icon(
+ Icons.edit_outlined,
+ size: 12,
+ color: Colors.white,
+ ),
+ )
+ : Container(),
+ ],
+ );
+ }
+}
diff --git a/lib/screens/lock_screen/services/pin_code_service.dart b/lib/screens/lock_screen/services/pin_code_service.dart
index 52ad4386..1674a601 100644
--- a/lib/screens/lock_screen/services/pin_code_service.dart
+++ b/lib/screens/lock_screen/services/pin_code_service.dart
@@ -10,15 +10,16 @@ import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:linum/common/components/dialogs/dialog_action.dart';
import 'package:linum/common/components/dialogs/show_action_dialog.dart';
+import 'package:linum/common/interfaces/service_interface.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
-import 'package:linum/core/design/theme/constants/ring_colors.dart';
+import 'package:linum/core/design/theme/ring_colors.dart';
import 'package:linum/core/navigation/get_delegate.dart';
import 'package:linum/core/navigation/main_routes.dart';
import 'package:linum/generated/translation_keys.g.dart';
import 'package:linum/screens/lock_screen/models/lock_screen_action.dart';
import 'package:shared_preferences/shared_preferences.dart';
-class PinCodeService extends ChangeNotifier {
+class PinCodeService extends IProvidableService {
String _code = '';
int _pinSlot = 0;
Color _ringColor = RingColors.green;
diff --git a/lib/screens/lock_screen/widgets/pin_field.dart b/lib/screens/lock_screen/widgets/pin_field.dart
index 88b49edd..16efc487 100644
--- a/lib/screens/lock_screen/widgets/pin_field.dart
+++ b/lib/screens/lock_screen/widgets/pin_field.dart
@@ -27,7 +27,7 @@ class PinField extends StatelessWidget {
decoration: BoxDecoration(
color: selectedIndex >= index
? Theme.of(context).colorScheme.tertiary
- : Theme.of(context).colorScheme.background,
+ : Theme.of(context).colorScheme.surface,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
diff --git a/lib/screens/onboarding_screen/widgets/language_dropdown_menu.dart b/lib/screens/onboarding_screen/widgets/language_dropdown_menu.dart
index 4d0050ce..c2e2d2fa 100644
--- a/lib/screens/onboarding_screen/widgets/language_dropdown_menu.dart
+++ b/lib/screens/onboarding_screen/widgets/language_dropdown_menu.dart
@@ -1,10 +1,11 @@
+import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
-import 'package:linum/core/authentication/domain/services/authentication_service.dart';
+import 'package:linum/core/localization/settings/constants/supported_locales.dart';
+import 'package:linum/core/localization/settings/presentation/language_settings_service.dart';
import 'package:linum/core/localization/settings/utils/country_flag_generator.dart';
import 'package:linum/screens/onboarding_screen/constants/country_codes.dart';
import 'package:provider/provider.dart';
-import 'package:shared_preferences/shared_preferences.dart';
class LanguageDropDownMenu extends StatelessWidget {
const LanguageDropDownMenu({super.key});
@@ -32,25 +33,14 @@ class LanguageDropDownMenu extends StatelessWidget {
);
}
- void _handleOnDropdownChanged(BuildContext context, String? value) {
+ Future _handleOnDropdownChanged(BuildContext context, String? value) async {
if (value != null) {
- SharedPreferences.getInstance().then((pref) {
- pref.setString(
- "languageCode",
- countryFlagsToCountryCode[value] ?? "en",
- );
- });
- final String langString = countryFlagsToCountryCode[value] ?? "en";
- context.setLocale(
- Locale(
- langString,
- langString != "en" ? langString.toUpperCase() : "US",
- ),
- );
+ final String langCode = countryFlagsToCountryCode[value] ?? "en";
+
+ final languageTag = supportedLocales.firstWhereOrNull((l) => l.languageCode == langCode)?.toLanguageTag();
+
+ await context.read().setLanguageTag(languageTag);
- context.read().updateLanguageCode(
- context.locale.languageCode,
- );
}
}
}
diff --git a/lib/screens/onboarding_screen/widgets/login_form/login_form.dart b/lib/screens/onboarding_screen/widgets/login_form/login_form.dart
index 783b419b..4ee42235 100644
--- a/lib/screens/onboarding_screen/widgets/login_form/login_form.dart
+++ b/lib/screens/onboarding_screen/widgets/login_form/login_form.dart
@@ -10,7 +10,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:linum/common/components/dialogs/show_alert_dialog.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
-import 'package:linum/core/authentication/presentation/widgets/forgot_password.dart';
+import 'package:linum/core/authentication/presentation/widgets/forgot_password_button.dart';
import 'package:linum/core/authentication/presentation/widgets/sign_in_sign_up_button.dart';
import 'package:linum/core/authentication/presentation/widgets/sign_in_with_google_button.dart';
import 'package:linum/core/design/layout/enums/screen_key.dart';
@@ -63,30 +63,23 @@ class _LoginFormState extends State {
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
children: [
- Container(
- padding: const EdgeInsets.all(5.0),
- decoration: BoxDecoration(
- color: theme.colorScheme.onSecondary,
- borderRadius: BorderRadius.circular(10),
- boxShadow: [
- BoxShadow(
- color: theme.colorScheme.onBackground,
- blurRadius: 20.0,
- offset: const Offset(0, 10),
- ),
- ],
- ),
- child: Column(
- children: [
- LoginEmailField(
- controller: _mailController!,
- validated: _mailValidated,
- ),
- LoginPasswordField(
- controller: _passController,
- validated: _passValidated,
- ),
- ],
+ Material(
+ elevation: 1,
+ borderRadius: BorderRadius.circular(8),
+ child: Container(
+ padding: const EdgeInsets.all(5.0),
+ child: Column(
+ children: [
+ LoginEmailField(
+ controller: _mailController!,
+ validated: _mailValidated,
+ ),
+ LoginPasswordField(
+ controller: _passController,
+ validated: _passValidated,
+ ),
+ ],
+ ),
),
),
SizedBox(
@@ -99,7 +92,7 @@ class _LoginFormState extends State {
SizedBox(
height: context.proportionateScreenHeight(8),
),
- const ForgotPasswordButton(ScreenKey.onboarding),
+ const ForgotPasswordButton(ScreenKey.onboarding, m3: false,),
SizedBox(
height: context.proportionateScreenHeight(8),
),
diff --git a/lib/screens/onboarding_screen/widgets/onboarding_page.dart b/lib/screens/onboarding_screen/widgets/onboarding_page.dart
index 080e860d..4c81300a 100644
--- a/lib/screens/onboarding_screen/widgets/onboarding_page.dart
+++ b/lib/screens/onboarding_screen/widgets/onboarding_page.dart
@@ -12,6 +12,7 @@ import 'package:linum/screens/onboarding_screen/widgets/onboarding_slide_indicat
import 'package:linum/screens/onboarding_screen/widgets/onboarding_slide_show.dart';
import 'package:linum/screens/onboarding_screen/widgets/views/login_view.dart';
import 'package:linum/screens/onboarding_screen/widgets/views/register_view.dart';
+import 'package:linum/screens/settings_screen/widgets/version_number.dart';
import 'package:provider/provider.dart';
class OnboardingPage extends StatelessWidget {
@@ -20,7 +21,7 @@ class OnboardingPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final OnboardingScreenViewModel viewModel =
- context.read();
+ context.read();
return ScreenSkeleton(
head: '', // will not be displayed anyways
@@ -56,6 +57,10 @@ class OnboardingPage extends StatelessWidget {
height: context.proportionateScreenHeight(10),
),
const OnboardingLoginButton(),
+ SizedBox(
+ height: context.proportionateScreenHeight(10),
+ ),
+ VersionNumber(),
],
),
),
@@ -68,5 +73,4 @@ class OnboardingPage extends StatelessWidget {
),
);
}
-
}
diff --git a/lib/screens/onboarding_screen/widgets/onboarding_register_button.dart b/lib/screens/onboarding_screen/widgets/onboarding_register_button.dart
index 8a2df139..558c9d95 100644
--- a/lib/screens/onboarding_screen/widgets/onboarding_register_button.dart
+++ b/lib/screens/onboarding_screen/widgets/onboarding_register_button.dart
@@ -1,6 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
-import 'package:gradient_widgets/gradient_widgets.dart';
+import 'package:linum/common/widgets/gradient_button.dart';
import 'package:linum/core/design/layout/utils/layout_helpers.dart';
import 'package:linum/generated/translation_keys.g.dart';
import 'package:linum/screens/onboarding_screen/enums/onboarding_page_state.dart';
@@ -18,20 +18,17 @@ class OnboardingRegisterButton extends StatelessWidget {
child: SizedBox(
width: double.infinity,
child: GradientButton(
- callback: () => viewModel
+ onPressed: () => viewModel
.setPageState(OnboardingPageState.register),
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primary,
- Theme.of(context).colorScheme.surface,
+ Theme.of(context).colorScheme.tertiary,
],
),
- elevation: 0,
- increaseHeightBy: context
- .proportionateScreenHeight(56 - 24),
- increaseWidthBy: double.infinity,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(8),
+ minimumSize: Size(
+ double.infinity,
+ context.proportionateScreenHeight(48),
),
child: Text(
tr(translationKeys.onboardingScreen.registerButton),
diff --git a/lib/screens/onboarding_screen/widgets/register_form/register_form.dart b/lib/screens/onboarding_screen/widgets/register_form/register_form.dart
index c1098f44..d9692b8e 100644
--- a/lib/screens/onboarding_screen/widgets/register_form/register_form.dart
+++ b/lib/screens/onboarding_screen/widgets/register_form/register_form.dart
@@ -64,30 +64,23 @@ class _RegisterFormState extends State {
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
children: [
- Container(
- padding: const EdgeInsets.all(5.0),
- decoration: BoxDecoration(
- color: theme.colorScheme.onSecondary,
- borderRadius: BorderRadius.circular(10),
- boxShadow: [
- BoxShadow(
- color: theme.colorScheme.onBackground,
- blurRadius: 20.0,
- offset: const Offset(0, 10),
- ),
- ],
- ),
- child: Column(
- children: [
- RegisterEmailField(
- controller: _mailController,
- validated: _mailValidated,
- ),
- RegisterPasswordField(
- controller: _passController,
- validated: _passValidated,
- ),
- ],
+ Material(
+ elevation: 1,
+ borderRadius: BorderRadius.circular(8),
+ child: Container(
+ padding: const EdgeInsets.all(5.0),
+ child: Column(
+ children: [
+ RegisterEmailField(
+ controller: _mailController,
+ validated: _mailValidated,
+ ),
+ RegisterPasswordField(
+ controller: _passController,
+ validated: _passValidated,
+ ),
+ ],
+ ),
),
),
SizedBox(
@@ -126,6 +119,7 @@ class _RegisterFormState extends State {
onPressed: context.read()
.signInWithApple,
text: tr(translationKeys.onboardingScreen.appleButton),
+ height: context.proportionateScreenHeight(40),
),
],
SizedBox(
diff --git a/lib/screens/onboarding_screen/widgets/views/login_view.dart b/lib/screens/onboarding_screen/widgets/views/login_view.dart
index 341dfe8c..815833e7 100644
--- a/lib/screens/onboarding_screen/widgets/views/login_view.dart
+++ b/lib/screens/onboarding_screen/widgets/views/login_view.dart
@@ -43,7 +43,7 @@ class LoginView extends StatelessWidget {
),
// padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom / 10000),
decoration: BoxDecoration(
- color: theme.colorScheme.background
+ color: theme.colorScheme.surface
.withOpacity(_opacity(pageState)),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(32),
diff --git a/lib/screens/onboarding_screen/widgets/views/register_view.dart b/lib/screens/onboarding_screen/widgets/views/register_view.dart
index 2fa25657..e264b3fb 100644
--- a/lib/screens/onboarding_screen/widgets/views/register_view.dart
+++ b/lib/screens/onboarding_screen/widgets/views/register_view.dart
@@ -35,7 +35,7 @@ class RegisterView extends StatelessWidget {
duration: const Duration(milliseconds: 800),
transform: Matrix4.translationValues(0, _yOffset(context, pageState), 0),
decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.background,
+ color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32),
diff --git a/lib/screens/settings_screen/settings_screen.dart b/lib/screens/settings_screen/settings_screen.dart
index f6ee826e..27d591b0 100644
--- a/lib/screens/settings_screen/settings_screen.dart
+++ b/lib/screens/settings_screen/settings_screen.dart
@@ -10,10 +10,12 @@ import 'package:linum/common/widgets/list_divider.dart';
import 'package:linum/common/widgets/list_header.dart';
import 'package:linum/core/authentication/presentation/widgets/change_email_button.dart';
import 'package:linum/core/authentication/presentation/widgets/delete_user_button.dart';
-import 'package:linum/core/authentication/presentation/widgets/forgot_password.dart';
+import 'package:linum/core/authentication/presentation/widgets/forgot_password_button.dart';
+import 'package:linum/core/authentication/presentation/widgets/logout_button.dart';
import 'package:linum/core/authentication/presentation/widgets/logout_form.dart';
import 'package:linum/core/categories/settings/presentation/widgets/standard_category_selectors.dart';
import 'package:linum/core/design/layout/enums/screen_key.dart';
+import 'package:linum/core/design/layout/widgets/app_bar_action.dart';
import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
import 'package:linum/core/localization/settings/presentation/widgets/language_selector.dart';
import 'package:linum/features/currencies/settings/presentation/widgets/standard_currency_selector.dart';
@@ -27,63 +29,81 @@ class SettingsScreen extends StatelessWidget {
Widget build(BuildContext context) {
return ScreenSkeleton(
head: 'Account',
+ isInverted: true,
screenKey: ScreenKey.settings,
initialActionLipBody: Container(),
- body: ScrollConfiguration(
- behavior: const SilentScroll(),
- child: ListView(
- key: const Key("accountListView"),
- padding: const EdgeInsets.symmetric(
- horizontal: 40.0,
- vertical: 24.0,
- ),
- children: [
- /// STANDARD CATEGORY
- const ListHeader(
- 'settings_screen.standard-category.label-title',
- tooltipMessage: 'settings_screen.standard-category.label-tooltip',
- ),
- const StandardCategorySelectors(),
- const ListDivider(),
+ actions: [
+ AppBarAction.fromPreset(DefaultAction.bugreport),
+ ],
+ body: Column(
+ children: [
+ LogoutForm(),
+ Expanded(
+ child: ScrollConfiguration(
+ behavior: const SilentScroll(),
+ child: ListView(
+ key: const Key("accountListView"),
+ padding: const EdgeInsets.symmetric(
+ horizontal: 40.0,
+ vertical: 24.0,
+ ),
+ children: [
+ /// STANDARD CATEGORY
+ const ListHeader(
+ 'settings_screen.standard-category.label-title',
+ tooltipMessage:
+ 'settings_screen.standard-category.label-tooltip',
+ ),
+ const StandardCategorySelectors(),
+ const ListDivider(),
- /// PIN SWITCH
- const ListHeader(
- 'settings_screen.pin-lock.label-title',
- tooltipMessage: 'settings_screen.pin-lock.label-tooltip',
- ),
- const PinSwitch(),
- const ListDivider(),
+ /// PIN SWITCH
+ const ListHeader(
+ 'settings_screen.pin-lock.label-title',
+ tooltipMessage: 'settings_screen.pin-lock.label-tooltip',
+ ),
+ const PinSwitch(),
+ const ListDivider(),
- const ListHeader('settings_screen.standard-currency.label-title'),
- const StandardCurrencySelector(),
- const ListDivider(),
+ const ListHeader(
+ 'settings_screen.standard-currency.label-title',
+ ),
+ const StandardCurrencySelector(),
+ const ListDivider(),
- /// LANGUAGE SWITCH
- const ListHeader(
- 'settings_screen.language-settings.label-title',
- ),
- const LanguageSelector(),
- const ListDivider(),
+ /// LANGUAGE SWITCH
+ const ListHeader(
+ 'settings_screen.language-settings.label-title',
+ ),
+ const LanguageSelector(),
+ const ListDivider(),
- /// YOUR ACCOUNT
- const ListHeader('settings_screen.system-settings.label-title'),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- // All Authentication Actions (including logOut will be handled via widgets/auth from now on.)
- LogoutForm(),
- const ForgotPasswordButton(ScreenKey.settings),
- const SizedBox(height: 4),
- const ChangeEmailButton(ScreenKey.settings),
- const DeleteUserButton(),
- ],
- ),
+ /// YOUR ACCOUNT
+ const ListHeader(
+ 'settings_screen.system-settings.label-title',
+ ),
+ const SizedBox(
+ height: 16.0,
+ ),
+ const Wrap(
+ spacing: 8.0,
+ children: [
+ LogoutButton(),
+ ForgotPasswordButton(ScreenKey.settings),
+ ChangeEmailButton(ScreenKey.settings),
+ //TODO add ReportBugButton(),
+ DeleteUserButton(),
+ ],
+ ),
- /// VERSION NUMBER
- VersionNumber(),
- const SizedBox(height: 40),
- ],
- ),
+ /// VERSION NUMBER
+ VersionNumber(),
+ const SizedBox(height: 40),
+ ],
+ ),
+ ),
+ ),
+ ],
),
);
}
diff --git a/lib/screens/settings_screen/widgets/pin_switch.dart b/lib/screens/settings_screen/widgets/pin_switch.dart
index 742bc978..087df50a 100644
--- a/lib/screens/settings_screen/widgets/pin_switch.dart
+++ b/lib/screens/settings_screen/widgets/pin_switch.dart
@@ -30,7 +30,6 @@ class PinSwitch extends StatelessWidget {
style: Theme.of(context).textTheme.bodyLarge,
),
value: pinCodeService.pinSet,
- activeColor: Theme.of(context).colorScheme.primaryContainer,
onChanged: pinCodeService.pinSetStillLoading
? null
: (_) {
diff --git a/lib/screens/statistics_screen/statistics_screen.dart b/lib/screens/statistics_screen/statistics_screen.dart
index b0b5f44c..c043edcf 100644
--- a/lib/screens/statistics_screen/statistics_screen.dart
+++ b/lib/screens/statistics_screen/statistics_screen.dart
@@ -7,6 +7,7 @@ import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:linum/core/design/layout/widgets/app_bar_action.dart';
import 'package:linum/core/design/layout/widgets/screen_skeleton.dart';
import 'package:linum/core/design/layout/widgets/top_bar_action_item.dart';
import 'package:linum/generated/translation_keys.g.dart';
@@ -18,6 +19,9 @@ class StatisticsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScreenSkeleton(
+ actions: [
+ AppBarAction.fromPreset(DefaultAction.bugreport),
+ ],
head: 'Stats',
body: Center(
child: Column(
diff --git a/lib/services.dart b/lib/services.dart
index 28f7c986..400faabd 100644
--- a/lib/services.dart
+++ b/lib/services.dart
@@ -1,10 +1,14 @@
+import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:linum/core/design/layout/loading_scaffold.dart';
import 'package:linum/core/events/event_interface.dart';
+import 'package:linum/core/events/event_types.dart';
+import 'package:linum/core/localization/settings/constants/supported_locales.dart';
import 'package:linum/core/localization/settings/presentation/language_settings_service.dart';
import 'package:linum/core/localization/settings/utils/locale_utils.dart';
+import 'package:linum/core/navigation/main_router_delegate.dart';
import 'package:linum/firebase/firebase_options.g.dart';
import 'package:linum/generated/objectbox/objectbox.g.dart';
import 'package:linum/generated/translation_keys.g.dart';
@@ -12,6 +16,7 @@ import 'package:linum/global_event_handler.dart';
import 'package:linum/user_dependent_services.dart';
import 'package:linum/user_independent_services.dart';
import 'package:provider/provider.dart';
+import 'package:shared_preferences/shared_preferences.dart';
/// Place were all application services are defined.
@@ -21,7 +26,8 @@ import 'package:provider/provider.dart';
class ApplicationServices extends StatelessWidget {
final Router router;
final Store store;
- ApplicationServices({super.key, required this.store, required this.router});
+ final SharedPreferences preferences;
+ ApplicationServices({super.key, required this.store, required this.router, required this.preferences});
final Future _initializedApp = Firebase.initializeApp(
name: "Linum",
@@ -47,15 +53,22 @@ class ApplicationServices extends StatelessWidget {
}
if (snapshot.connectionState == ConnectionState.done) {
return UserIndependentServices(
- child: UserDependentServices(
- store: store,
- child: GlobalEventListener(
- listeners: [
- _handleLanguageChangeEvent,
- ],
- child: router,
- ),
- ),
+ builder: (context, user, child) {
+ return UserDependentServices(
+ store: store,
+ user: user,
+ preferences: preferences,
+ child: EventListener(
+ streamName: "global",
+ listeners: {
+ EventType.languageChange: [
+ _handleLanguageChangeEvent,
+ ],
+ },
+ child: router, // TODO: Move child outside
+ ),
+ );
+ },
);
}
return const LoadingScaffold();
@@ -64,16 +77,28 @@ class ApplicationServices extends StatelessWidget {
}
// TODO: find a place for this
- void _handleLanguageChangeEvent(IEvent event, BuildContext context) {
+ Future _handleLanguageChangeEvent(IEvent event, BuildContext context) async {
final languageSettingsService = context.read();
- final String? langCode = languageSettingsService.getLanguageCode();
- Locale? locale;
- if (!languageSettingsService.useSystemLanguage && langCode != null) {
- final List langArray = langCode.split("-");
- locale = Locale(langArray[0], langArray[1]);
+ final String? localeStr = event.message;
+ final locale = supportedLocales.firstWhereOrNull((l) => l.toLanguageTag() == localeStr);
+ // print(localeStr);
+ // print(languageSettingsService.useSystemLanguage);
+
+
+ if (context.locale.toLanguageTag() == localeStr && !languageSettingsService.useSystemLanguage) {
+ return;
+ }
+
+ final delegate = router.routerDelegate as MainRouterDelegate;
+ delegate.showLoadingScreen(duration: const Duration(milliseconds: 750));
+ delegate.resetServicesLoadingState();
+
+ if (localeStr != null && locale != null && !languageSettingsService.useSystemLanguage) {
+ // print("Set App Locale");
context.setAppLocale(locale);
} else {
context.setLocaleToDeviceLocale();
}
}
+
}
diff --git a/lib/user_dependent_services.dart b/lib/user_dependent_services.dart
index 96d07c44..80b1ae24 100644
--- a/lib/user_dependent_services.dart
+++ b/lib/user_dependent_services.dart
@@ -1,5 +1,7 @@
import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
+import 'package:linum/common/utils/service_container.dart';
import 'package:linum/core/authentication/domain/services/authentication_service.dart';
import 'package:linum/core/balance/services/balance_data_service.dart';
import 'package:linum/core/categories/settings/data/category_settings.dart';
@@ -9,6 +11,7 @@ import 'package:linum/core/categories/settings/presentation/category_settings_se
import 'package:linum/core/events/event_service.dart';
import 'package:linum/core/localization/settings/data/language_settings.dart';
import 'package:linum/core/localization/settings/data/language_settings_mapper.dart';
+import 'package:linum/core/localization/settings/data/language_settings_pref_adapter.dart';
import 'package:linum/core/localization/settings/domain/language_settings_service_impl.dart';
import 'package:linum/core/localization/settings/presentation/language_settings_service.dart';
import 'package:linum/core/settings/data/settings_repository_impl.dart';
@@ -26,40 +29,71 @@ import 'package:linum/features/currencies/settings/presentation/currency_setting
import 'package:linum/generated/objectbox/objectbox.g.dart';
import 'package:linum/screens/lock_screen/services/pin_code_service.dart';
import 'package:provider/provider.dart';
+import 'package:shared_preferences/shared_preferences.dart';
/// All services that depend on a signed in user are defined here.
-class UserDependentServices extends StatelessWidget {
+class UserDependentServices extends StatefulWidget {
final Store store;
+ final User? user;
+ final SharedPreferences preferences;
final Widget child;
- const UserDependentServices({super.key, required this.store, required this.child});
+ const UserDependentServices({
+ super.key,
+ required this.store,
+ required this.child,
+ required this.preferences,
+ required this.user,
+ });
@override
- Widget build(BuildContext context) {
- final authService = context.read();
- final eventService = context.read();
+ State createState() => _UserDependentServicesState();
+}
+
+class _UserDependentServicesState extends State {
+ SettingsStorageImpl? settingsStorage;
+ final ServiceContainer _container = ServiceContainer();
+ late Widget _serviceWidget;
+
+ @override
+ void initState() {
+ // print("State init");
+ settingsStorage = SettingsStorageImpl(FirebaseFirestore.instance, widget.user?.uid);
+ _buildServices(context.read());
+ super.initState();
+ }
+
+ @override
+ void didUpdateWidget(UserDependentServices oldWidget) {
+ // print("didUpdateWidget");
+ if (oldWidget.user != widget.user) {
+ settingsStorage?.dispose();
+ settingsStorage = SettingsStorageImpl(FirebaseFirestore.instance, widget.user?.uid);
+ _buildServices(context.read());
+ super.didUpdateWidget(oldWidget);
+ }
+ }
- final settingsStorage = SettingsStorageImpl(FirebaseFirestore.instance);
+ void _buildServices(EventService eventService) {
+ _container.clear();
final languageSettingsRepository = SettingsRepositoryImpl(
- userId: authService.currentUser?.uid,
- adapter: settingsStorage,
+ adapter: settingsStorage!,
mapper: LanguageSettingsMapper(),
+ prefAdapter: LanguageSettingsPrefAdapter(preferences: widget.preferences),
);
final categorySettingsRepository = SettingsRepositoryImpl(
- userId: authService.currentUser?.uid,
- adapter: settingsStorage,
+ adapter: settingsStorage!,
mapper: CategorySettingsMapper(),
);
final currencySettingsRepository = SettingsRepositoryImpl(
- userId: authService.currentUser?.uid,
- adapter: settingsStorage,
+ adapter: settingsStorage!,
mapper: CurrencySettingsMapper(),
);
- final exchangeRateStorage = ExchangeRateStorageImpl(store);
- final exchangeRateSynchronizer = ExchangeRateSynchronizer(store);
+ final exchangeRateStorage = ExchangeRateStorageImpl(widget.store);
+ final exchangeRateSynchronizer = ExchangeRateSynchronizer(widget.store);
final exchangeRateRepository = ExchangeRateRepositoryImpl(
exchangeRateStorage, exchangeRateSynchronizer,
@@ -69,41 +103,52 @@ class UserDependentServices extends StatelessWidget {
exchangeRateRepository,
);
+ final currencySettingsService = CurrencySettingsServiceImpl(
+ currencySettingsRepository,
+ );
+
+ final categorySettingsService = CategorySettingsServiceImpl(
+ categorySettingsRepository,
+ );
+
+ final languageSettingsService = LanguageSettingsServiceImpl(
+ languageSettingsRepository,
+ eventService,
+ );
- return MultiProvider(
- providers: [
- ChangeNotifierProvider(
- create: (context) => CurrencySettingsServiceImpl(
- currencySettingsRepository,
- ),
- ),
- ChangeNotifierProvider(
- create: (context) => CategorySettingsServiceImpl(
- categorySettingsRepository,
- ),
- ),
- ChangeNotifierProvider(
- create: (context) => LanguageSettingsServiceImpl(
- languageSettingsRepository,
- eventService,
- ),
- ),
- ChangeNotifierProvider(
- create: (context) => BalanceDataService(
- authService.currentUser?.uid ?? "",
- ),
- ),
- ChangeNotifierProvider(
- create: (context) => ExchangeRateServiceImpl(
- context.read(),
- exchangeRateFetcher,
- ),
- ),
- ChangeNotifierProvider(
- create: (context) => PinCodeService(authService),
- ),
- ],
- child: child,
+ final balanceDataService = BalanceDataService(
+ widget.user?.uid ?? "",
);
+
+ final exchangeRateService = ExchangeRateServiceImpl(
+ currencySettingsService,
+ exchangeRateFetcher,
+ );
+
+
+ final pinCodeService = PinCodeService(context.read());
+
+ _container.registerProvidableService(currencySettingsService);
+ _container.registerProvidableService(categorySettingsService);
+ _container.registerProvidableService(languageSettingsService);
+ _container.registerProvidableService(balanceDataService);
+ _container.registerProvidableService(exchangeRateService);
+ _container.registerProvidableService(pinCodeService);
+
+ _serviceWidget = _container.build(context, widget.child);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ // print("Rebuild: ");
+ // print(widget.user?.uid);
+ return _serviceWidget;
+ }
+
+ @override
+ void dispose() {
+ settingsStorage?.dispose();
+ _container.dispose();
+ super.dispose();
}
}
diff --git a/lib/user_independent_services.dart b/lib/user_independent_services.dart
index de1c038d..1952f14b 100644
--- a/lib/user_independent_services.dart
+++ b/lib/user_independent_services.dart
@@ -9,8 +9,8 @@ import 'package:provider/provider.dart';
/// All services that do not depend on a signed in user are defined here.
class UserIndependentServices extends StatelessWidget {
- final Widget child;
- const UserIndependentServices({super.key, required this.child});
+ final Widget Function(BuildContext context, User? user, Widget? child) builder;
+ const UserIndependentServices({super.key, required this.builder});
@override
Widget build(BuildContext context) {
@@ -34,9 +34,13 @@ class UserIndependentServices extends StatelessWidget {
ChangeNotifierProvider(
create: (_) => ActionLipViewModel(),
),
-
],
- child: child,
+ child: Selector(
+ builder: (BuildContext context, User? user, Widget? child) {
+ return builder(context, user, child);
+ },
+ selector: (context, authService) => authService.currentUser,
+ ),
);
}
}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..3f37f8b3
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,30 @@
+{
+ "name": "linum",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "dotenv": "^16.4.5"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ }
+ },
+ "dependencies": {
+ "dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..3d6216c4
--- /dev/null
+++ b/package.json
@@ -0,0 +1,8 @@
+{
+ "scripts": {
+ "build": "node build.js"
+ },
+ "dependencies": {
+ "dotenv": "^16.4.5"
+ }
+}
\ No newline at end of file
diff --git a/pubspec.yaml b/pubspec.yaml
index c53fd9be..4e981677 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -15,61 +15,63 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 1.2.3+20
+version: 1.3.0+21
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
- badges: ^2.0.2
- cloud_firestore: ^4.8.0
+ badges: ^3.1.2
+ cloud_firestore: ^5.1.0
code_builder: ^4.1.0
collection: ^1.17.1
crypto: ^3.0.1
cupertino_icons: ^1.0.2
dart_style: ^2.2.3
easy_localization: ^3.0.2
- easy_localization_loader: ^1.0.1+1
+ easy_localization_loader: ^2.0.2
enum_to_string: ^2.0.1
- firebase_auth: ^4.6.2
- firebase_core: ^2.14.0
- flat_buffers: ^2.0.5
- flip_card: ^0.5.0 # Don't update to 0.6.0
+ firebase_auth: ^5.1.2
+ firebase_core: ^3.2.0
+ flat_buffers: ^23.5.26
+ flip_card: ^0.7.0 # Don't update to 0.6.0
flutter:
sdk: flutter
+ flutter_dotenv: ^5.1.0
flutter_spinkit: ^5.1.0
- flutter_svg: ^1.0.0
+ flutter_svg: ^2.0.10+1
fluttertoast: ^8.0.8
- google_fonts: ^4.0.4
- google_sign_in: ^5.2.4
- gradient_widgets: ^0.6.0
- http: ^0.13.6
- intl: ^0.18.0
+ google_fonts: ^6.2.1
+ google_sign_in: ^6.2.1
+ http: ^1.2.1
+ intl: ^0.19.0
json_annotation: ^4.7.0
- logger: ^1.1.0
+ logger: ^2.3.0
mockito: ^5.3.2
- objectbox: 2.1.0
- objectbox_flutter_libs: 2.1.0
- objectbox_generator: 2.1.0
- package_info_plus: ^4.0.2
+ objectbox: ^4.0.1
+ objectbox_flutter_libs: ^4.0.1
+ objectbox_generator: ^4.0.1
+ package_info_plus: ^8.0.0
path: ^1.8.2
provider: ^6.0.2
- rxdart: ^0.27.7
+ rxdart: ^0.28.0
+ sentry_flutter: ^8.3.0
shared_preferences: ^2.0.13
- sign_in_with_apple: ^4.2.0
+ sign_in_with_apple: ^6.1.1
tuple: ^2.0.0
url_launcher: ^6.1.2
url_launcher_ios: ^6.0.15
- uuid: ^3.0.5
+ uuid: ^4.4.0
+ wiredash: ^2.2.0
dev_dependencies:
build_runner: ^2.2.1
- flutter_launcher_icons: ^0.11.0
+ flutter_launcher_icons: ^0.13.1
flutter_native_splash: ^2.2.14
flutter_test:
sdk: flutter
@@ -116,6 +118,7 @@ flutter:
- assets/images/
- assets/svg/
- assets/lang/
+ - .env
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
diff --git a/test/widgets/lock_screen/pin_field_test.dart b/test/widgets/lock_screen/pin_field_test.dart
index 59f0eff2..8f4f70eb 100644
--- a/test/widgets/lock_screen/pin_field_test.dart
+++ b/test/widgets/lock_screen/pin_field_test.dart
@@ -6,8 +6,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:linum/core/design/theme/constants/main_theme_data.dart';
-import 'package:linum/core/design/theme/constants/ring_colors.dart';
+import 'package:linum/core/design/theme/ring_colors.dart';
+import 'package:linum/core/design/theme/theme_data.dart';
import 'package:linum/screens/lock_screen/widgets/pin_field.dart';
void main() {
@@ -16,14 +16,14 @@ void main() {
testWidgets('Test the initial state of the widget', (tester) async {
await tester.pumpWidget(
MaterialApp(
- theme: MainThemeData.lightTheme,
+ theme: LinumTheme.light(),
home: const Scaffold(
body: PinField(1, 0, RingColors.green, key: key),
),
),
);
final Finder ring = find.byKey(key);
- final colorScheme = MainThemeData.lightTheme.colorScheme;
+ final colorScheme = LinumTheme.light().colorScheme;
final bool Function(Widget) hasWidgetBoxDecoration = (Widget widget) =>
widget is Container && widget.decoration is BoxDecoration;
@@ -33,7 +33,7 @@ void main() {
(Widget widget) =>
hasWidgetBoxDecoration(widget) &&
((widget as Container).decoration! as BoxDecoration).color ==
- colorScheme.background,
+ colorScheme.surface,
),
findsOneWidget,
);
@@ -54,14 +54,14 @@ void main() {
testWidgets('Test when Pin is already written', (tester) async {
await tester.pumpWidget(
MaterialApp(
- theme: MainThemeData.lightTheme,
+ theme: LinumTheme.light(),
home: const Scaffold(
body: PinField(1, 2, RingColors.green, key: key),
),
),
);
final Finder ring = find.byKey(key);
- final colorScheme = MainThemeData.lightTheme.colorScheme;
+ final colorScheme = LinumTheme.light().colorScheme;
final bool Function(Widget) hasWidgetBoxDecoration = (Widget widget) =>
widget is Container && widget.decoration is BoxDecoration;